Ejemplos de ejecutores
El código de esta página se ha desarrollado teniendo en cuenta los siguientes requisitos. Recomendamos utilizar estas versiones o posteriores.
qiskit[all]~=2.4.0 qiskit-ibm-runtime~=0.46.1 samplomatic~=0.18.0
Los ejemplos de esta sección ilustran algunas formas habituales de utilizar la primitiva Executor. Antes de ejecutar estos ejemplos, sigue las instrucciones de la guía de inicio rápido «Instalación de Qiskit y Executor».
Antes de empezar
Algunos de los ejemplos de código de esta página utilizan samplex, que forma parte del paquete Samplomatic. Por lo tanto, antes de ejecutar esos bloques de código, debes instalar Samplomatic, tal y como se muestra en el siguiente bloque de código. Para obtener más información, consulta la documentación de Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]Ejemplo: Circuito parametrizado
Este ejemplo muestra cómo añadir elementos de circuito con parámetros, así como cómo añadir elementos Samplex. Consiste en los siguientes pasos:
- Configurar el circuito: Generar y transpilar el circuito de destino.
- Prepara un samplex: agrupa las puertas y las mediciones en recuadros anotados y genera la plantilla del circuito y el par de samplex.
- Ejecutar: Añadir un elemento de circuito y un elemento Samplex a un
QuantumProgramy ejecutarlos ambos en un único trabajo.
Monta el circuito
Prepara un estado GHZ de tres qubits, gira los qubits alrededor del eje de Pauli-Z y mide los qubits en la base computacional.
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Generate the circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
circuit.rz(Parameter("lam"), 2)
circuit.measure_all()Especifica el backend y compila el circuito para que utilice únicamente instrucciones compatibles con la QPU (lo que se conoce como un circuito de arquitectura de conjunto de instrucciones, o ISA).
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Transpile the circuit to ISA
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = preset_pass_manager.run(circuit)Prepara el samplex
Utiliza la generate_boxing_pass_manager función de conveniencia y sus parámetros de giro para agrupar puertas de dos qubits y mediciones en recuadros y aplicar anotaciones de giro.
boxing_pm = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = boxing_pm.run(isa_circuit)Utiliza el build método para generar el circuito de referencia y el samplex.
# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)Realiza los circuitos
El ejecutor ejecuta QuantumProgram objetos. Cada uno QuantumProgram puede contener varios elementos. En este ejemplo se añaden un elemento de circuito y un elemento Samplex para su ejecución. Para obtener más información, consulta la sección «Entradas y salidas del ejecutor ».
El primer paso consiste en inicializar un programa vacío, solicitando 1024 tomas para cada configuración de cada elemento.
# Generate a quantum program
program = QuantumProgram(shots=1024)Añade el elemento del circuito al QuantumProgram. Este elemento del circuito consta de dos partes: el circuito ISA y 10 conjuntos de valores de sus parámetros.
# Append the circuit and the parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, 3), # 10 sets of parameter values
)QuantumProgram Añade el elemento «samplex» a con estos argumentos:
- El circuito de plantilla y el samplex generado por la
buildfunción - Diez conjuntos de valores de parámetros para el circuito original
- El número de aleatorizaciones que hay que realizar
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(
10, 3
), # 10 sets of parameter values
},
shape=(2, 14, 10),
)Ejecuta la tarea de Executor
# initialize an Executor with default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()Obtén el resultado de cada tarea.
# Access the results of the classical register of task #0, the CircuitItem
result_0 = result[0]["meas"]
# Access the results of the classical register of task #1, the SamplexItem
result_1 = result[1]["meas"]Ejemplo: Realizar PEC
Este ejemplo muestra cómo utilizar un elemento «samplex» para llevar a cabo la cancelación probabilística de errores ( PEC ) con el fin de mitigar los errores.
Consideremos una versión simétrica de un circuito con diez qubits y dos capas distintas de puertas CX. Estas son las tareas principales:
- Realiza el circuito con giros.
- Ejecuta el circuito con mitigación PEC, tal y como se describe en el artículo «Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors ».
El proceso consta de los siguientes pasos:
- Configuración: Genera el circuito de destino y agrupa sus operaciones en recuadros.
- Aprender: Identificar el ruido de las instrucciones que queremos mitigar con PEC.
- Ejecutar: Ejecuta el circuito en un servidor.
- Analizar: Procesar y analizar los resultados.
A modo de comparación, ejecutaremos este circuito simétrico dos veces. Una vez aplicando únicamente el efecto de Pauli, y otra vez aplicando la corrección PEC.
La duración de este ejemplo es de unos 10 minutos en un procesador Heron r2.
Monta el circuito
Elige un backend y prepara un circuito de 10 qubits.
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.transpiler import generate_preset_pass_manager
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic import build
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Prepare a circuit
num_qubits = 10
num_layers = 10
qubits = list(range(num_qubits))
circuit = QuantumCircuit(num_qubits)
for layer_idx in range(num_layers):
circuit.rx(Parameter(f"theta_{layer_idx}"), qubits)
for i in range(num_qubits // 2):
circuit.cz(qubits[2 * i], qubits[2 * i + 1])
circuit.rx(Parameter(f"phi_{layer_idx}"), qubits)
for i in range(num_qubits // 2 - 1):
circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)
circuit.draw("mpl", scale=0.35, fold=100)Output:
Combina el circuito con su inverso para crear un circuito especular.
mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()
mirror_circuit.draw("mpl", scale=0.35, fold=100)Output:
Establece algunos valores de los parámetros:
import numpy as np
parameter_values = np.random.rand(mirror_circuit.num_parameters)Utiliza el gestor de pases para transpilar el circuito y convertirlo en un circuito ISA.
preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)
isa_circuit = preset_pass_manager.run(mirror_circuit)A continuación, agrupa las puertas y las mediciones en recuadros con anotaciones. Puedes hacerlo manualmente o, si lo prefieres, utilizar la generate_boxing_pass_manager función de Samplomatic. Al primer circuito solo se le aplicará el efecto de giro, por lo que solo necesita la Twirl anotación. El segundo circuito se ejecutará con mitigación PEC completa y requiere las Twirl anotaciones y InjectNoise .
# Pass manager used to create twirled-annotated boxes.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
mirror_circuit_twirl = boxing_pm.run(isa_circuit)
# Pass manager used to create a new boxed circuit with
# both Twirl and InjectNoise annotations.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
mirror_circuit_pec = boxing_pm.run(isa_circuit)Aprende a reconocer el ruido
Para reducir al mínimo el número de experimentos de aprendizaje con ruido, identifica las instrucciones únicas del segundo circuito (el que tiene casillas marcadas con InjectNoise). A la hora de definir la unicidad, dos instrucciones de cuadro se consideran iguales si se cumplen las dos condiciones siguientes:
- Su contenido es idéntico, salvo por las puertas de un solo qubit.
- Su
Twirlanotación es igual (se ignoran todas las demás anotaciones).
Esto da lugar a tres instrucciones específicas: las casillas de puertas pares e impares y la casilla de medición final.
from samplomatic.utils import find_unique_box_instructions
unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3NoiseLearnerV3Inicialice un proceso, seleccione los parámetros de aprendizaje configurando sus opciones y ejecute una tarea de aprendizaje de ruido.
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]
learner_job = learner.run(unique_box_instructions)
learner_job.job_id()
learner_result = learner_job.result()Convierte result el objeto al formato requerido por el samplex utilizando el result.to_dict método.
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)Realiza los circuitos
Executor ejecuta QuantumProgram objetos. Cada uno QuantumProgram puede contener varios elementos, que se añaden al programa. Cada elemento es una tarea que debe realizar el programa.
Inicia un programa vacío que solicite 1000 imágenes para cada configuración de cada elemento.
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)A continuación, crea el circuito de plantilla y el samplex para mirror_circuit_twirl y añádelos al programa. Solicita 900 también aleatorizaciones del conjunto de muestras. Esto significa que el samplex generará 900 conjuntos de parámetros, y cada conjunto se ejecutará 1000 un número de veces (el número de iteraciones) en la QPU.
Esta es la primera tarea del programa (resultado 0).
template_twirl, samplex_twirl = build(mirror_circuit_twirl)
program.append_samplex_item(
template_twirl,
samplex=samplex_twirl,
samplex_arguments={"parameter_values": parameter_values},
shape=(900,),
)Del mismo modo, añade el circuito de plantilla y el samplex creados para mirror_circuit_pec, solicitando 900 aleatorizaciones. Esta es la segunda tarea del programa (resultado 1).
template_pec, samplex_pec = build(mirror_circuit_pec)
program.append_samplex_item(
template_pec,
samplex=samplex_pec,
samplex_arguments={
"parameter_values": parameter_values,
"pauli_lindblad_maps": noise_maps,
"noise_scales": {
ref: -1.0 for ref in noise_maps
}, # Set the scales to -1 for PEC
},
shape=(900,),
)Importar Executor y enviar un trabajo.
from qiskit_ibm_runtime.executor import Executor
executor = Executor(backend)
executor_job = executor.run(program)
executor_job.job_id()
executor_results = executor_job.result()
executor_results
twirl_result = executor_results[0]
print(f"Twirl result keys:\n {list(twirl_result.keys())}\n")
print(f"Shape of results: {twirl_result['meas'].shape}")
pec_result = executor_results[1]
print(f"PEC result keys:\n {list(pec_result.keys())}\n")
print(f"Shape of results: {pec_result['meas'].shape}")Output:
Twirl result keys:
['meas', 'measurement_flips.meas']
Shape of results: (900, 1000, 10)
PEC result keys:
['meas', 'measurement_flips.meas', 'pauli_signs']
Shape of results: (900, 1000, 10)
Analizar los resultados
Por último, procesa los resultados para estimar los valores esperados de los operadores Pauli-Z de un solo qubit que actúan sobre cada uno de los diez qubits activos (valor esperado: 1.0).
# Undo measurement twirling
twirl_result_unflipped = (
twirl_result["meas"] ^ twirl_result["measurement_flips.meas"]
)
# Calculate the expectation values of single-qubit Z operators
exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")Output:
Qubit 0 -> 0.77
Qubit 1 -> 0.76
Qubit 2 -> 0.66
Qubit 3 -> 0.71
Qubit 4 -> 0.69
Qubit 5 -> 0.67
Qubit 6 -> 0.62
Qubit 7 -> 0.59
Qubit 8 -> 0.62
Qubit 9 -> 0.68
# Undo measurement twirling
pec_result_unflipped = (
pec_result["meas"] ^ pec_result["measurement_flips.meas"]
)
# Calculate the signs for PEC mitigation
signs = np.prod((-1) ** pec_result["pauli_signs"], axis=-1)
signs = signs.reshape((signs.shape[0], 1))
# Calculate the expectation values of single-qubit Z operators as required by
# PEC mitigation
exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")Output:
Qubit 0 -> 0.98
Qubit 1 -> 0.99
Qubit 2 -> 0.96
Qubit 3 -> 0.98
Qubit 4 -> 0.98
Qubit 5 -> 0.98
Qubit 6 -> 0.98
Qubit 7 -> 0.95
Qubit 8 -> 0.95
Qubit 9 -> 0.94
Próximos pasos
- Consulte la descripción general de la emisión.
- Aprende a utilizar las opciones de Executor.
- Comprender el modelo de ejecución dirigida.
- Consulta la documentación de Samplomatic.
- Descubre cómo combinar diferentes técnicas de mitigación de errores al utilizar el modelo de ejecución dirigida en el tutorial «Cancelación probabilística de errores con conos de luz sombreados ».