Primitives avec l'API REST
Les étapes décrites dans cette page expliquent comment exécuter et configurer des charges de travail primitives avec l'API REST, et montrent comment les invoquer dans n'importe quel programme de ton choix.
Cette documentation utilise le module Python requests pour illustrer l'API REST Qiskit Runtime. Ce workflow peut toutefois être exécuté avec n'importe quel langage ou framework prenant en charge les API REST. Consulte la documentation de référence de l'API pour plus de détails.
Primitive Estimator avec l'API REST
1. Initialiser le compte
Comme Qiskit Runtime Estimator est un service géré, tu dois d'abord initialiser ton compte. Tu pourras ensuite sélectionner le dispositif à utiliser pour calculer la valeur d'espérance.
Retrouve les détails sur l'initialisation de ton compte, la consultation des backends disponibles et l'invalidation des tokens dans cette page.
2. Créer un circuit QASM
Il te faut au moins un circuit en entrée de la primitive Estimator.
Définis un circuit quantique QASM. Par exemple :
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];
'''
Les extraits de code suivants supposent que qasm_string a été transpilée en une nouvelle chaîne resulting_qasm.
3. Exécuter le circuit quantique avec l'API Estimator V2
Les jobs suivants utilisent les primitives Qiskit Runtime V2. SamplerV2 et EstimatorV2 acceptent un ou plusieurs blocs unifiés primitifs (PUBs) en entrée. Chaque PUB est un tuple contenant un circuit et les données diffusées à ce circuit, ce qui peut inclure plusieurs observables et paramètres. Chaque PUB retourne un résultat.
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. Vérifier le statut du job et récupérer les résultats
Ensuite, passe le job_id à l'API :
response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')
Sortie
>>> Job ID: 58223448-5100-4dec-a47a-942fb30edcad
>>> Job Status: JobStatus.RUNNING
Récupérer les résultats du job :
response_result= requests.get(url+'/'+job_id+'/results', headers=headers)
res_dict=response_result.json()
estimator_result=res_dict['results']
print(estimator_result)
Sortie
[{'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. Travailler avec les options Runtime
Les techniques d'atténuation des erreurs permettent aux utilisateurs de réduire les erreurs de circuit en modélisant le bruit du dispositif au moment de l'exécution. Cela entraîne généralement un surcoût de pré-traitement quantique lié à l'entraînement du modèle, ainsi qu'un surcoût de post-traitement classique pour atténuer les erreurs dans les résultats bruts à l'aide du modèle généré.
Les techniques d'atténuation des erreurs intégrées aux primitives sont des options de résilience avancées. Pour les spécifier, utilise l'option resilience_level lors de la soumission de ton job.
Les exemples suivants illustrent les options par défaut pour le découplage dynamique, le twirling et TREX + ZNE. Retrouve plus d'options et de détails dans la page Techniques d'atténuation et de suppression des erreurs.
- 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}")
Primitive Sampler avec l'API REST
1. Initialiser le compte
Comme Qiskit Runtime Sampler est un service géré, tu dois d'abord initialiser ton compte. Tu pourras ensuite sélectionner le dispositif sur lequel tu veux effectuer tes calculs.
Retrouve les détails sur l'initialisation de ton compte, la consultation des backends disponibles et l'invalidation des tokens dans cette page.
2. Créer un circuit QASM
Il te faut au moins un circuit en entrée de la primitive Sampler.
Définis un circuit quantique QASM :
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];
'''
Les extraits de code ci-dessous supposent que qasm_string a été transpilée en une nouvelle chaîne resulting_qasm.
3. Exécuter le circuit quantique avec l'API Sampler V2
Les jobs ci-dessous utilisent les primitives Qiskit Runtime V2. SamplerV2 et EstimatorV2 acceptent un ou plusieurs blocs unifiés primitifs (PUBs) en entrée. Chaque PUB est un tuple contenant un circuit et les données diffusées à ce circuit, ce qui peut inclure plusieurs observables et paramètres. Chaque PUB retourne un résultat.
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. Vérifier le statut du job et récupérer les résultats
Ensuite, passe le job_id à l'API :
response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')
Sortie
>>> Job ID: 58223448-5100-4dec-a47a-942fb30edced
>>> Job Status: JobStatus.RUNNING
Récupérer les résultats du job :
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])
Sortie
['0x3', '0x0', '0x2', '0x1', '0x0', '0x3', '0x0', '0x3', '0x1', '0x2', '0x2', '0x0', '0x2', '0x0', '0x3', '0x3', '0x2', '0x0', '0x1', '0x0']
5. Travailler avec les options Runtime
Les techniques d'atténuation des erreurs permettent aux utilisateurs de réduire les erreurs de circuit en modélisant le bruit du dispositif au moment de l'exécution. Cela entraîne généralement un surcoût de pré-traitement quantique lié à l'entraînement du modèle, ainsi qu'un surcoût de post-traitement classique pour atténuer les erreurs dans les résultats bruts à l'aide du modèle généré.
Les techniques d'atténuation des erreurs intégrées aux primitives sont des options de résilience avancées. Pour les spécifier, utilise l'option resilience_level lors de la soumission de ton job.
Sampler V2 ne prend pas en charge la spécification de niveaux de résilience. Tu peux cependant activer ou désactiver individuellement les méthodes d'atténuation et de suppression des erreurs.
Les exemples suivants illustrent les options par défaut pour le découplage dynamique et le twirling. Retrouve plus d'options et de détails dans la page Techniques d'atténuation et de suppression des erreurs.
- 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}")
Primitive Sampler avec l'API REST et circuits paramétrés
1. Initialiser le compte
Comme Qiskit Runtime est un service géré, tu dois d'abord initialiser ton compte. Tu pourras ensuite sélectionner le dispositif sur lequel tu veux effectuer tes calculs.
Retrouve les détails sur l'initialisation de ton compte, la consultation des backends disponibles et l'invalidation des tokens dans cette page.
2. Définir les paramètres
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. Créer un circuit quantique et ajouter des portes paramétrées
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. Générer le code QASM 3
qasm_str = dumps(isa_circuit)
print("Generated QASM 3 code:")
print(qasm_str)
5. Exécuter le circuit quantique avec l'API Sampler V2
Les jobs suivants utilisent les primitives Qiskit Runtime V2. SamplerV2 et EstimatorV2 acceptent un ou plusieurs blocs unifiés primitifs (PUBs) en entrée. Chaque PUB est un tuple contenant un circuit et les données diffusées à ce circuit, ce qui peut inclure plusieurs observables et paramètres. Chaque PUB retourne un résultat.
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. Vérifier le statut du job et récupérer les résultats
Ensuite, passe le job_id à l'API :
response_status_singlejob = requests.get(f"{url}/{job_id}", headers=headers)
response_status_singlejob.json().get('state')
Sortie
{'status': 'Completed'}
Récupérer les résultats du job :
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])
Sortie
['0x1', '0x2', '0x1', '0x2', '0x1', '0x2', '0x0', '0x2', '0x1', '0x1', '0x2', '0x2', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1']
Prochaines étapes
- Il existe plusieurs façons d'exécuter des charges de travail selon tes besoins : le mode job, le mode session et le mode batch. Apprends à utiliser le mode session et le mode batch dans la page sur les modes d'exécution. Note que les utilisateurs du plan Open ne peuvent pas soumettre de jobs en mode session.
- Apprends à initialiser ton compte avec l'API REST.
- Lis Migrer vers les primitives V2.
- Mets en pratique les primitives en suivant le cours sur les fonctions de coût dans IBM Quantum Learning.
- Apprends à transpilar localement dans la section Transpiler.
- Migre vers les primitives Qiskit Runtime V2.