Primitivas com a API REST
Os passos neste tópico descrevem como executar e configurar cargas de trabalho com primitivas usando a API REST, e demonstram como invocá-las em qualquer programa de sua escolha.
Esta documentação utiliza o módulo Python requests para demonstrar a API REST do Qiskit Runtime. No entanto, esse fluxo de trabalho pode ser executado usando qualquer linguagem ou framework que suporte trabalhar com APIs REST. Consulte a documentação de referência da API para mais detalhes.
Primitiva Estimator com a API REST
1. Inicializar a conta
Como o Estimator do Qiskit Runtime é um serviço gerenciado, você primeiro precisa inicializar sua conta. Em seguida, pode selecionar o dispositivo que deseja usar para calcular o valor esperado.
Encontre detalhes sobre como inicializar sua conta, visualizar backends disponíveis e invalidar tokens neste tópico.
2. Criar um circuito QASM
Você precisa de pelo menos um circuito como entrada para a primitiva Estimator.
Defina um circuito quântico QASM. Por exemplo:
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];
'''
Os trechos de código a seguir presumem que o qasm_string foi transpilado para uma nova string resulting_qasm.
3. Executar o circuito quântico usando a API Estimator V2
Os jobs a seguir usam primitivas V2 do Qiskit Runtime. Tanto SamplerV2 quanto EstimatorV2 recebem um ou mais blocos unificados de primitivas (PUBs) como entrada. Cada PUB é uma tupla que contém um circuito e os dados transmitidos para esse circuito, que podem ser múltiplos observáveis e parâmetros. Cada PUB retorna um resultado.
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. Verificar o status do job e obter resultados
Em seguida, passe o job_id para a API:
response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')
Saída
>>> Job ID: 58223448-5100-4dec-a47a-942fb30edcad
>>> Job Status: JobStatus.RUNNING
Obter resultados do job:
response_result= requests.get(url+'/'+job_id+'/results', headers=headers)
res_dict=response_result.json()
estimator_result=res_dict['results']
print(estimator_result)
Saída
[{'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. Trabalhar com opções do Runtime
As técnicas de mitigação de erros permitem que os usuários mitiguem erros de circuito modelando o ruído do dispositivo no momento da execução. Isso normalmente resulta em sobrecarga de pré-processamento quântico relacionada ao treinamento do modelo, e sobrecarga de pós-processamento clássico para mitigar erros nos resultados brutos usando o modelo gerado.
As técnicas de mitigação de erros incorporadas às primitivas são opções avançadas de resiliência. Para especificar essas opções, use a opção resilience_level ao submeter seu job.
Os exemplos a seguir demonstram as opções padrão para desacoplamento dinâmico, twirling e TREX + ZNE. Encontre mais opções e detalhes adicionais no tópico Técnicas de mitigação e supressão de erros.
- 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}")
Primitiva Sampler com a API REST
1. Inicializar a conta
Como o Sampler do Qiskit Runtime é um serviço gerenciado, você primeiro precisa inicializar sua conta. Em seguida, pode selecionar o dispositivo que deseja usar para executar seus cálculos.
Encontre detalhes sobre como inicializar sua conta, visualizar backends disponíveis e invalidar tokens neste tópico.
2. Criar um circuito QASM
Você precisa de pelo menos um circuito como entrada para a primitiva Sampler.
Defina um circuito quântico 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];
'''
Os trechos de código fornecidos abaixo presumem que o qasm_string foi transpilado para uma nova string resulting_qasm.
3. Executar o circuito quântico usando a API Sampler V2
Os jobs abaixo usam primitivas V2 do Qiskit Runtime. Tanto SamplerV2 quanto EstimatorV2 recebem um ou mais blocos unificados de primitivas (PUBs) como entrada. Cada PUB é uma tupla que contém um circuito e os dados transmitidos para esse circuito, que podem ser múltiplos observáveis e parâmetros. Cada PUB retorna um resultado.
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. Verificar o status do job e obter resultados
Em seguida, passe o job_id para a API:
response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')
Saída
>>> Job ID: 58223448-5100-4dec-a47a-942fb30edced
>>> Job Status: JobStatus.RUNNING
Obter resultados do 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])
Saída
['0x3', '0x0', '0x2', '0x1', '0x0', '0x3', '0x0', '0x3', '0x1', '0x2', '0x2', '0x0', '0x2', '0x0', '0x3', '0x3', '0x2', '0x0', '0x1', '0x0']
5. Trabalhar com opções do Runtime
As técnicas de mitigação de erros permitem que os usuários mitiguem erros de circuito modelando o ruído do dispositivo no momento da execução. Isso normalmente resulta em sobrecarga de pré-processamento quântico relacionada ao treinamento do modelo, e sobrecarga de pós-processamento clássico para mitigar erros nos resultados brutos usando o modelo gerado.
As técnicas de mitigação de erros incorporadas às primitivas são opções avançadas de resiliência. Para especificar essas opções, use a opção resilience_level ao submeter seu job.
O Sampler V2 não suporta a especificação de níveis de resiliência. No entanto, você pode ativar ou desativar métodos individuais de mitigação / supressão de erros.
Os exemplos a seguir demonstram as opções padrão para desacoplamento dinâmico e twirling. Encontre mais opções e detalhes adicionais no tópico Técnicas de mitigação e supressão de erros.
- 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}")
Primitiva Sampler com a API REST e circuitos parametrizados
1. Inicializar a conta
Como o Qiskit Runtime é um serviço gerenciado, você primeiro precisa inicializar sua conta. Em seguida, pode selecionar o dispositivo que deseja usar para executar seus cálculos.
Encontre detalhes sobre como inicializar sua conta, visualizar backends disponíveis e invalidar tokens neste tópico.
2. Definir parâmetros
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. Criar um circuito quântico e adicionar gates parametrizados
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. Gerar código QASM 3
qasm_str = dumps(isa_circuit)
print("Generated QASM 3 code:")
print(qasm_str)
5. Executar o circuito quântico usando a API Sampler V2
Os jobs a seguir usam primitivas V2 do Qiskit Runtime. Tanto SamplerV2 quanto EstimatorV2 recebem um ou mais blocos unificados de primitivas (PUBs) como entrada. Cada PUB é uma tupla que contém um circuito e os dados transmitidos para esse circuito, que podem ser múltiplos observáveis e parâmetros. Cada PUB retorna um resultado.
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. Verificar o status do job e obter resultados
Em seguida, passe o job_id para a API:
response_status_singlejob = requests.get(f"{url}/{job_id}", headers=headers)
response_status_singlejob.json().get('state')
Saída
{'status': 'Completed'}
Obter resultados do 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])
Saída
['0x1', '0x2', '0x1', '0x2', '0x1', '0x2', '0x0', '0x2', '0x1', '0x1', '0x2', '0x2', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1']
Próximos passos
- Há várias formas de executar cargas de trabalho, dependendo das suas necessidades: modo de job, modo de sessão e modo de lote. Aprenda como trabalhar com o modo de sessão e o modo de lote no tópico sobre modos de execução. Observe que usuários do Plano Gratuito não podem submeter jobs em sessão.
- Aprenda como inicializar sua conta com a API REST.
- Leia Migrar para primitivas V2.
- Pratique com primitivas trabalhando na lição sobre função de custo no IBM Quantum Learning.
- Aprenda como transpilar localmente na seção Transpile.
- Migre para as primitivas V2 do Qiskit Runtime.