Primitives dengan REST API
Langkah-langkah dalam topik ini menjelaskan cara menjalankan dan mengonfigurasi beban kerja primitives dengan REST API, serta mendemonstrasikan cara memanggilnya di program apa pun yang kamu pilih.
Dokumentasi ini menggunakan modul Python requests untuk mendemonstrasikan Qiskit Runtime REST API. Namun, alur kerja ini bisa dijalankan menggunakan bahasa atau framework apa pun yang mendukung REST API. Lihat dokumentasi referensi API untuk detailnya.
Estimator primitive dengan REST APIβ
1. Inisialisasi akunβ
Karena Qiskit Runtime Estimator adalah layanan terkelola, kamu perlu menginisialisasi akun terlebih dahulu. Setelah itu, kamu bisa memilih perangkat yang ingin digunakan untuk menghitung nilai ekspektasi.
Temukan detail cara menginisialisasi akun, melihat Backend yang tersedia, dan membatalkan token di topik ini.
2. Buat Circuit QASMβ
Kamu membutuhkan setidaknya satu Circuit sebagai input untuk primitive Estimator.
Definisikan Circuit kuantum QASM. Contohnya:
qasm_string='''
OPENQASM 3;
include "stdgates.inc";
qreg q[2];
creg c[2];
x q[0];
cx q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
'''
Cuplikan kode berikut mengasumsikan bahwa qasm_string telah ditranspilasi ke string baru resulting_qasm.
3. Jalankan Circuit kuantum menggunakan Estimator V2 APIβ
Job-job berikut menggunakan Qiskit Runtime V2 primitives. Baik SamplerV2 maupun EstimatorV2 menerima satu atau lebih primitive unified blocs (PUBs) sebagai input. Setiap PUB adalah tuple yang berisi satu Circuit dan data yang disiarkan ke Circuit tersebut, yang bisa berupa beberapa observabel dan parameter. Setiap PUB mengembalikan satu hasil.
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "<BACKEND_NAME>"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'estimator',
"backend": backend,
"params": {
"pubs": [ #primitive unified blocs (PUBs) containing one circuit each.
[resulting_qasm, # QASM circuit
{"IIZII": 1, "XIZZZ": 2.3}, # Observable
None # parameter values
]]
}}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
4. Periksa status job dan dapatkan hasilnyaβ
Selanjutnya, masukkan job_id ke API:
response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')
Output
>>> Job ID: 58223448-5100-4dec-a47a-942fb30edcad
>>> Job Status: JobStatus.RUNNING
Dapatkan hasil job:
response_result= requests.get(url+'/'+job_id+'/results', headers=headers)
res_dict=response_result.json()
estimator_result=res_dict['results']
print(estimator_result)
Output
[{'data': {'evs': 0.7428980350102542, 'stds': 0.029884014518789213, 'ensemble_standard_error': 0.03261147170624149}, 'metadata': {'shots': 10016, 'target_precision': 0.01, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32}}]
5. Bekerja dengan opsi Runtimeβ
Teknik mitigasi error memungkinkan pengguna mengurangi error pada Circuit dengan memodelkan noise perangkat saat eksekusi berlangsung. Hal ini biasanya menghasilkan overhead pra-pemrosesan kuantum terkait pelatihan model, serta overhead pasca-pemrosesan klasikal untuk mengurangi error pada hasil mentah menggunakan model yang dihasilkan.
Teknik mitigasi error yang sudah terintegrasi ke dalam primitives adalah opsi resilience tingkat lanjut. Untuk menentukan opsi-opsi ini, gunakan opsi resilience_level saat mengirimkan job kamu.
Contoh-contoh berikut mendemonstrasikan opsi default untuk dynamical decoupling, twirling, dan TREX + ZNE. Temukan lebih banyak opsi dan detail lebih lanjut di topik Teknik mitigasi dan supresi error.
- TREX + ZNE
- Dynamical Decoupling
- Twirling
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "BACKEND_NAME"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'estimator',
"backend": backend,
"params": {
"pubs": [ #primitive unified blocs (PUBs) containing one circuit each
[resulting_qasm, # QASM circuit
{"IIZII": 1, "XIZZZ": 2.3}, # Observable
None # parameter values
]]
"options": {
"resilience": {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": {
"extrapolator":["exponential", "linear"],
"noise_factors":[1, 3, 5],
},
},
},
}
}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "BACKEND_NAME"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'estimator',
"backend": backend,
"params": {
"pubs": [ #primitive unified blocs (PUBs) containing one circuit each
[resulting_qasm, # QASM circuit
{"IIZII": 1, "XIZZZ": 2.3}, # Observable
None # parameter values
]]
"options": {
"dynamical_decoupling": {
"enable": True,
"sequence_type": 'XpXm',
"extra_slack_distribution": 'middle',
"scheduling_method": 'alap',
},
},
}
}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "BACKEND_NAME"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'estimator',
"backend": backend,
"params": {
"pubs": [ #primitive unified blocs (PUBs) containing one circuit each
[resulting_qasm, # QASM circuit
{"IIZII": 1, "XIZZZ": 2.3}, # Observable
None # parameter values
]]
"options": {
"twirling": {
"enable_gates": True,
"enable_measure": True,
"num_randomizations": "auto",
"shots_per_randomization": "auto",
"strategy": "active-accum",
},
},
}
}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
Sampler primitive with REST APIβ
1. Initialize the accountβ
Because Qiskit Runtime Sampler is a managed service, you first need to initialize your account. You can then select the device you want to use to run your calculations on.
Find details on how to initialize your account, view available backends, and invalidate tokens in this topic.
2. Create a QASM circuitβ
You need at least one circuit as the input to the Sampler primitive.
Define a QASM quantum circuit:
qasm_string='''
OPENQASM 3;
include "stdgates.inc";
qreg q[2];
creg c[2];
x q[0];
cx q[0], q[1];
c[0] = measure q[0];
c[1] = measure q[1];
'''
The code snippets given below assume that the qasm_string has been transpiled to a new string resulting_qasm.
3. Run the quantum circuit using Sampler V2 APIβ
The jobs below use Qiskit Runtime V2 primitives. Both SamplerV2 and EstimatorV2 take one or more primitive unified blocs (PUBs) as the input. Each PUB is a tuple that contains one circuit and the data broadcasted to that circuit, which can be multiple observables and parameters. Each PUB returns a result.
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "<BACKEND_NAME>"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'sampler',
"backend": backend,
"params": {
"pubs": [[resulting_qasm],[resulting_qasm,None,500]] # primitive unified blocs (PUBs) containing one circuit each.
}}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
4. Check job status and get resultsβ
Next, pass the job_id to the API:
response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')
Output
>>> Job ID: 58223448-5100-4dec-a47a-942fb30edced
>>> Job Status: JobStatus.RUNNING
Get job results:
response_result= requests.get(url+'/'+job_id+'/results', headers=headers)
res_dict=response_result.json()
# Get results for the first PUB
counts=res_dict['results'][0]['data']['c']['samples']
print(counts[:20])
Output
['0x3', '0x0', '0x2', '0x1', '0x0', '0x3', '0x0', '0x3', '0x1', '0x2', '0x2', '0x0', '0x2', '0x0', '0x3', '0x3', '0x2', '0x0', '0x1', '0x0']
5. Work with Runtime optionsβ
Error mitigation techniques allow users to mitigate circuit errors by modeling the device noise at the time of execution. This typically results in quantum pre-processing overhead related to model training, and classical post-processing overhead to mitigate errors in the raw results by using the generated model.
The error mitigation techniques built in to primitives are advanced resilience options. To specify these options, use the resilience_level option when submitting your job.
Sampler V2 does not support specifying resilience levels. However, you can turn on or off individual error mitigation / suppression methods.
The following examples demonstrate the default options for dynamical decoupling and twirling. Find more options and further details in the Error mitigation and suppression techniques topic.
- Dynamical Decoupling
- Twirling
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "<BACKEND_NAME>"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'sampler',
"backend": backend,
"params": {
"pubs": [[resulting_qasm]], # primitive unified blocs (PUBs) containing one circuit each.
"options": {
"dynamical_decoupling": {
"enable": True,
"sequence_type": 'XpXm',
"extra_slack_distribution": 'middle',
"scheduling_method": 'alap',
},
},
}
}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "<BACKEND_NAME>"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'sampler',
"backend": backend,
"params": {
"pubs": [[resulting_qasm]], # primitive unified blocs (PUBs) containing one circuit each.
"options": {
"twirling": {
"enable_gates": True,
"enable_measure": True,
"num_randomizations": "auto",
"shots_per_randomization": "auto",
"strategy": "active-accum",
},
},
}
}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print("Job created:",response.text)
else:
print(f"Error: {response.status_code}")
Sampler primitive with REST API and parameterized circuitsβ
1. Initialize the accountβ
Because Qiskit Runtime is a managed service, you first need to initialize your account. You can then select the device you want to use to run your calculations on.
Find details on how to initialize your account, view available backends, and invalidate tokens in this topic.
2. Define parametersβ
import requests
import qiskit_ibm_runtime
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.qasm3 import dumps
from qiskit import QuantumCircuit
from qiskit.circuit import Parameter
from qiskit import transpile
service = QiskitRuntimeService(channel='ibm_quantum')
backend = service.backend("<SPECIFY BACKEND>")
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
theta = Parameter('theta')
phi = Parameter('phi')
parameter_values = {'theta': 1.57, 'phi': 3.14} # In case we want to pass a dictionary
3. Create a quantum circuit and add parameterized gatesβ
qc = QuantumCircuit(2)
# Add parameterized gates
qc.rx(theta, 0)
qc.ry(phi, 1)
qc.cx(0, 1)
qc.measure_all()
# Draw the original circuit
qc.draw('mpl')
# Get an ISA circuit
isa_circuit = pm.run(qc)
4. Generate QASM 3 codeβ
qasm_str = dumps(isa_circuit)
print("Generated QASM 3 code:")
print(qasm_str)
5. Run the quantum circuit using Sampler V2 APIβ
The following jobs use Qiskit Runtime V2 primitives. Both SamplerV2 and EstimatorV2 take one or more primitive unified blocs (PUBs) as the input. Each PUB is a tuple that contains one circuit and the data broadcasted to that circuit, which can be multiple observables and parameters. Each PUB returns a result.
import requests
url = 'https://quantum.cloud.ibm.com/api/v1/jobs'
auth_id = "Bearer <YOUR_BEARER_TOKEN>"
crn = "<SERVICE-CRN>"
backend = "<BACKEND_NAME>"
headers = {
'Content-Type': 'application/json',
'Authorization':auth_id,
'Service-CRN': crn
}
job_input = {
'program_id': 'sampler',
"backend": backend,
"params": {
# Choose one option: direct parameter transfer or through a dictionary
#"pubs": [[qasm_str,[1,2],500]], # primitive unified blocs (PUBs) containing one circuit each.
"pubs": [[qasm_str,parameter_values,500]], # primitive unified blocs (PUBs) containing one circuit each.
}}
response = requests.post(url, headers=headers, json=job_input)
if response.status_code == 200:
job_id = response.json().get('id')
print(f"Job created: {response.text}")
else:
print(f"Error: {response.status_code}")
print(response.text)
6. Check job status and get resultsβ
Next, pass the job_id to the API:
response_status_singlejob = requests.get(f"{url}/{job_id}", headers=headers)
response_status_singlejob.json().get('state')
Output
{'status': 'Completed'}
Get job results:
response_result = requests.get(f"{url}/{job_id}/results", headers=headers)
res_dict=response_result.json()
# Get results for the first PUB
counts=res_dict['results'][0]['data']['c']['samples']
print(counts[:20])
Output
['0x1', '0x2', '0x1', '0x2', '0x1', '0x2', '0x0', '0x2', '0x1', '0x1', '0x2', '0x2', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1']
Langkah selanjutnyaβ
- Ada beberapa cara untuk menjalankan workload, tergantung kebutuhanmu: job mode, session mode, dan batch mode. Pelajari cara bekerja dengan session mode dan batch mode di topik execution modes. Perlu diketahui bahwa pengguna Open Plan tidak dapat mengirimkan session job.
- Pelajari cara menginisialisasi akun dengan REST API.
- Baca Migrasi ke V2 primitives.
- Berlatih dengan primitives melalui pelajaran Cost function di IBM Quantum Learning.
- Pelajari cara melakukan transpilasi secara lokal di bagian Transpile.
- Migrasi ke Qiskit Runtime V2 primitives.