Saltar al contenido principal

Primeros pasos con las primitivas

Nuevo modelo de ejecución, ahora en versión beta

La versión beta de un nuevo modelo de ejecución ya está disponible. El modelo de ejecución dirigida ofrece mayor flexibilidad al personalizar tu flujo de trabajo de mitigación de errores. Consulta la guía del Modelo de ejecución dirigida para más información.

nota

Si bien esta documentación utiliza las primitivas de Qiskit Runtime, que te permiten usar backends de IBM®, las primitivas también pueden ejecutarse con cualquier proveedor usando las primitivas de backend. Además, puedes usar las primitivas de referencia para ejecutar en un simulador de vector de estado local. Consulta Simulación exacta con las primitivas de Qiskit para más detalles.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime

Los pasos de este tema describen cómo configurar las primitivas, explorar las opciones disponibles para configurarlas e invocarlas en un programa.

Uso de Fractional Gates

Para usar los fractional gates recién incorporados, establece use_fractional_gates=True al solicitar un backend a una instancia de QiskitRuntimeService. Por ejemplo:

service = QiskitRuntimeService()
fractional_gate_backend = service.least_busy(use_fractional_gates=True)

Ten en cuenta que esta es una función experimental y podría cambiar en el futuro.

Versiones de paquetes

El código de esta página fue desarrollado con los siguientes requisitos. Recomendamos usar estas versiones o versiones más recientes.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Primeros pasos con Estimator

1. Inicializa la cuenta

Dado que Qiskit Runtime Estimator es un servicio administrado, primero debes inicializar tu cuenta. Luego puedes seleccionar la QPU que quieres usar para calcular el valor esperado.

Sigue los pasos del tema de instalación y configuración si aún no tienes una cuenta.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)

print(backend.name)
ibm_torino

2. Crea un Circuit y un observable

Necesitas al menos un Circuit y un observable como entradas para la primitiva Estimator.

from qiskit.circuit.library import qaoa_ansatz
from qiskit.quantum_info import SparsePauliOp

entanglement = [tuple(edge) for edge in backend.coupling_map.get_edges()]
observable = SparsePauliOp.from_sparse_list(
[("ZZ", [i, j], 0.5) for i, j in entanglement],
num_qubits=backend.num_qubits,
)
circuit = qaoa_ansatz(observable, reps=2)
# the circuit is parametrized, so we will define the parameter values for execution
param_values = [0.1, 0.2, 0.3, 0.4]

print(f">>> Observable: {observable.paulis}")
>>> Observable: ['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...',
'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...', ...]

El Circuit y el observable deben transformarse para usar únicamente instrucciones compatibles con la QPU (denominados circuitos de arquitectura de conjunto de instrucciones (ISA)). Usaremos el Transpiler para esto.

from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print(f">>> Circuit ops (ISA): {isa_circuit.count_ops()}")
>>> Circuit ops (ISA): OrderedDict([('rz', 3826), ('sx', 1601), ('cz', 968)])

3. Inicializa Qiskit Runtime Estimator

Al inicializar el Estimator, usa el parámetro mode para especificar el modo en que quieres ejecutarlo. Los valores posibles son objetos batch, session o backend para los modos de ejecución por lote, sesión y trabajo, respectivamente. Para más información, consulta Introducción a los modos de ejecución de Qiskit Runtime.

from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)

4. Invoca el Estimator y obtén los resultados

A continuación, invoca el método run() para calcular los valores esperados de los circuitos y observables de entrada. El Circuit, el observable y los conjuntos opcionales de valores de parámetros se ingresan como tuplas primitive unified bloc (PUB).

job = estimator.run([(isa_circuit, isa_observable, param_values)])
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: d5k96c4jt3vs73ds5smg
>>> Job Status: QUEUED
result = job.result()
print(f">>> {result}")
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")
>>> PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})
> Expectation value: 25.8930784649363
> Metadata: {'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32}

Primeros pasos con Sampler

1. Inicializa la cuenta

Dado que Qiskit Runtime Sampler es un servicio administrado, primero debes inicializar tu cuenta. Luego puedes seleccionar la QPU que quieres usar para calcular el valor esperado.

Sigue los pasos del tema de instalación y configuración si aún no tienes una cuenta configurada.

from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)

2. Crea un Circuit

Necesitas al menos un Circuit como entrada para la primitiva Sampler.

import numpy as np
from qiskit.circuit.library import efficient_su2

circuit = efficient_su2(127, entanglement="linear")
circuit.measure_all()
# The circuit is parametrized, so we will define the parameter values for execution
param_values = np.random.rand(circuit.num_parameters)

Usa el Transpiler para obtener un Circuit ISA.

from qiskit.transpiler import generate_preset_pass_manager

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
print(f">>> Circuit ops (ISA): {isa_circuit.count_ops()}")
>>> Circuit ops (ISA): OrderedDict([('sx', 3089), ('rz', 3036), ('cz', 1092), ('measure', 127), ('barrier', 1)])

3. Inicializa el Qiskit Runtime Sampler

Al inicializar el Sampler, usa el parámetro mode para especificar el modo en que quieres ejecutarlo. Los valores posibles son objetos batch, session o backend para los modos de ejecución por lote, sesión y trabajo, respectivamente. Para más información, consulta Introducción a los modos de ejecución de Qiskit Runtime. Ten en cuenta que los usuarios del Plan Abierto no pueden enviar trabajos de sesión.

from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

4. Invoca el Sampler y obtén los resultados

A continuación, invoca el método run() para generar la salida. El Circuit y los conjuntos opcionales de valores de parámetros se ingresan como tuplas primitive unified bloc (PUB).

job = sampler.run([(isa_circuit, param_values)])
print(f">>> Job ID: {job.job_id()}")
print(f">>> Job Status: {job.status()}")
>>> Job ID: d5k96rsjt3vs73ds5tig
>>> Job Status: QUEUED
result = job.result()

# Get results for the first (and only) PUB
pub_result = result[0]
print(
f"First ten results for the 'meas' output register: {pub_result.data.meas.get_bitstrings()[:10]}"
)
First ten results for the 'meas' output register: ['0101001101010000011001110001011000010010001100001000100110011111011110000010110001101000110011101010000100011011000110101111000', '0100111000000100110001100100000101111000111001101000110111101110110010010100001101001111001010011101010000010011000110000010001', '0101111101111111010011010101000000110100000010000010011101100011100011001100000100100001000101000000100001010101010011001101100', '1100110101111111001110010000010100101010101010001000001100100110011111010000000010001000110111010000010101100000100000110111001', '0010000001111001111010100100010111101000101000100000101100001000011100000100011010110110100011100110001001110110111101010011000', '0101110000001000100100010010100100111000010100000000010010000000010110010010000110000001110110010100000111001110100100111101100', '0100011111101001000111110011011101101101110101110001010111011101111110011101001000000001110000011110000101010000001010000100000', '0001010101011000110100000100111111100001011000111110000011000111001101010000010001001100000110000000100000110101010010101110010', '0100011010001110011110000110100101100100101001001111010100100101010100010000000010100000101010110010000000001000010101011111110', '0000011000000111000001000101111111110110101100110000001100010010011101011100001010000100011010001010001101000000000000010001001']

Primeros pasos con las primitivas de backend

A diferencia de las primitivas específicas de cada proveedor, las primitivas de backend son implementaciones genéricas que pueden usarse con cualquier objeto backend, siempre que implemente la interfaz Backend.

Algunos proveedores implementan primitivas de forma nativa. Consulta la página del Ecosistema Qiskit para más detalles.

Ejemplo: BackendEstimator

from qiskit.primitives import BackendEstimatorV2
from <some_qiskit_provider> import QiskitProvider

provider = QiskitProvider()
backend = provider.get_backend('backend_name')
estimator = BackendEstimatorV2(backend)

Ejemplo: BackendSampler

from qiskit.primitives import BackendSamplerV2
from <some_qiskit_provider> import QiskitProvider

provider = QiskitProvider()
backend = provider.get_backend('backend_name')
sampler = BackendSamplerV2(backend)

Similitudes y diferencias entre las primitivas de backend y de Runtime

Próximos pasos

Recomendaciones