Saltar al contenido principal

Primitivos con la API REST

Los pasos de este tema describen cómo ejecutar y configurar cargas de trabajo con primitivos usando la API REST, y muestran cómo invocarlos desde cualquier programa de tu elección.

nota

Esta documentación utiliza el módulo requests de Python para demostrar la API REST de Qiskit Runtime. Sin embargo, este flujo de trabajo puede ejecutarse con cualquier lenguaje o framework que soporte el trabajo con APIs REST. Consulta la documentación de referencia de la API para más detalles.

Primitivo Estimator con la API REST

1. Inicializar la cuenta

Como Qiskit Runtime Estimator es un servicio gestionado, primero necesitas inicializar tu cuenta. Luego puedes seleccionar el dispositivo que quieres usar para calcular el valor esperado.

Encuentra los detalles sobre cómo inicializar tu cuenta, ver los backends disponibles e invalidar tokens en este tema.

2. Crear un circuito QASM

Necesitas al menos un circuito como entrada al primitivo Estimator.

Define un circuito cuántico QASM. Por ejemplo:

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];
'''

Los siguientes fragmentos de código asumen que qasm_string ha sido transpilado a una nueva cadena resulting_qasm.

3. Ejecutar el circuito cuántico con la API Estimator V2

nota

Los siguientes jobs usan primitivos Qiskit Runtime V2. Tanto SamplerV2 como EstimatorV2 toman uno o más bloques unificados de primitivos (PUBs) como entrada. Cada PUB es una tupla que contiene un circuito y los datos transmitidos a ese circuito, que pueden ser múltiples observables y parámetros. Cada PUB devuelve un 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 el estado del job y obtener resultados

A continuación, pasa el job_id a la API:

response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')

Salida

>>> Job ID: 58223448-5100-4dec-a47a-942fb30edcad
>>> Job Status: JobStatus.RUNNING

Obtener los resultados del job:

response_result= requests.get(url+'/'+job_id+'/results', headers=headers)

res_dict=response_result.json()

estimator_result=res_dict['results']
print(estimator_result)

Salida

[{'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. Trabajar con opciones de Runtime

Las técnicas de mitigación de errores permiten a los usuarios mitigar errores del circuito modelando el ruido del dispositivo en el momento de la ejecución. Esto generalmente conlleva una sobrecarga de preprocesamiento cuántico relacionada con el entrenamiento del modelo, y una sobrecarga de posprocesamiento clásico para mitigar los errores en los resultados brutos usando el modelo generado.

Las técnicas de mitigación de errores integradas en los primitivos son opciones avanzadas de resiliencia. Para especificar estas opciones, usa la opción resilience_level al enviar tu job.

Los siguientes ejemplos demuestran las opciones predeterminadas para desacoplamiento dinámico, twirling y TREX + ZNE. Encuentra más opciones y detalles adicionales en el tema Técnicas de mitigación y supresión de errores.

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}")

Primitivo Sampler con la API REST

1. Inicializar la cuenta

Como Qiskit Runtime Sampler es un servicio gestionado, primero necesitas inicializar tu cuenta. Luego puedes seleccionar el dispositivo que quieres usar para ejecutar tus cálculos.

Encuentra los detalles sobre cómo inicializar tu cuenta, ver los backends disponibles e invalidar tokens en este tema.

2. Crear un circuito QASM

Necesitas al menos un circuito como entrada al primitivo Sampler.

Define un circuito cuá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];
'''

Los fragmentos de código que se muestran a continuación asumen que qasm_string ha sido transpilado a una nueva cadena resulting_qasm.

3. Ejecutar el circuito cuántico con la API Sampler V2

nota

Los jobs a continuación usan primitivos Qiskit Runtime V2. Tanto SamplerV2 como EstimatorV2 toman uno o más bloques unificados de primitivos (PUBs) como entrada. Cada PUB es una tupla que contiene un circuito y los datos transmitidos a ese circuito, que pueden ser múltiples observables y parámetros. Cada PUB devuelve un 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 el estado del job y obtener resultados

A continuación, pasa el job_id a la API:

response_status_singlejob= requests.get(url+'/'+job_id, headers=headers)
response_status_singlejob.json().get('state')

Salida

>>> Job ID: 58223448-5100-4dec-a47a-942fb30edced
>>> Job Status: JobStatus.RUNNING

Obtener los resultados del 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])

Salida

['0x3', '0x0', '0x2', '0x1', '0x0', '0x3', '0x0', '0x3', '0x1', '0x2', '0x2', '0x0', '0x2', '0x0', '0x3', '0x3', '0x2', '0x0', '0x1', '0x0']

5. Trabajar con opciones de Runtime

Las técnicas de mitigación de errores permiten a los usuarios mitigar errores de circuito modelando el ruido del dispositivo en el momento de la ejecución. Esto suele conllevar una sobrecarga de preprocesamiento cuántico relacionada con el entrenamiento del modelo y una sobrecarga de posprocesamiento clásico para mitigar errores en los resultados brutos mediante el modelo generado.

Las técnicas de mitigación de errores integradas en las primitivas son opciones avanzadas de resiliencia. Para especificar estas opciones, usa la opción resilience_level al enviar tu trabajo. Sampler V2 no admite especificar niveles de resiliencia. Sin embargo, puedes activar o desactivar individualmente los métodos de mitigación y supresión de errores.

Los siguientes ejemplos muestran las opciones predeterminadas para el desacoplamiento dinámico y el twirling. Encuentra más opciones y más detalles en el tema Técnicas de mitigación y supresión de errores.

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}")

Primitiva Sampler con REST API y circuitos parametrizados

1. Inicializar la cuenta

Como Qiskit Runtime es un servicio gestionado, primero debes inicializar tu cuenta. Luego puedes seleccionar el dispositivo en el que deseas ejecutar tus cálculos.

Encuentra detalles sobre cómo inicializar tu cuenta, ver los backends disponibles e invalidar tokens en este tema.

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. Crear un circuito cuántico y agregar puertas parametrizadas

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. Generar código QASM 3

qasm_str = dumps(isa_circuit)
print("Generated QASM 3 code:")
print(qasm_str)

5. Ejecutar el circuito cuántico con la API de Sampler V2

nota

Los siguientes trabajos usan las primitivas de Qiskit Runtime V2. Tanto SamplerV2 como EstimatorV2 toman uno o más bloques unificados de primitiva (PUBs) como entrada. Cada PUB es una tupla que contiene un circuito y los datos transmitidos a ese circuito, que pueden ser múltiples observables y parámetros. Cada PUB devuelve un 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 el estado del trabajo y obtener resultados

A continuación, pasa el job_id a la API:

response_status_singlejob = requests.get(f"{url}/{job_id}", headers=headers)
response_status_singlejob.json().get('state')

Salida

{'status': 'Completed'}

Obtener los resultados del trabajo:

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])

Salida

['0x1', '0x2', '0x1', '0x2', '0x1', '0x2', '0x0', '0x2', '0x1', '0x1', '0x2', '0x2', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1', '0x1']

Próximos pasos

Recomendaciones