Skip to main content
IBM Quantum Platform

Migrar de Sampler a Executor

Esta guía describe cómo trasladar las cargas de trabajo de muestreo cuántico de la primitiva « IBM Quantum® » a la primitiva «Executor».

Release Beta

La primitiva «Executor» forma parte del modelo de ejecución dirigida. Todos los componentes del modelo de ejecución dirigida se encuentran actualmente en fase beta y es posible que no sean estables. Te invitamos a que las pruebes y nos des tu opinión abriendo una incidencia en los repositorios de Samplomatic o qiskit-ibm-runtime GitHub.


¿Deberías migrar?

No todo el mundo debería pasar de Sampler a Executor. Existen muchas diferencias entre los tipos primitivos, pero las siguientes indicaciones pueden ayudarte a decidir si conviene realizar la migración:

Cambia a Executor si eres un investigador en información cuántica que lleva a cabo experimentos a escala industrial y necesitas un control preciso y reproducible sobre técnicas como el «Pauli twirling», el aprendizaje y la inyección de modelos de ruido, y los cambios de base, o si necesitas alguna de las capacidades adicionales que ofrece Executor.

Sigue utilizando Sampler si prefieres una interfaz sencilla y de alto nivel, y quieres que la primitiva se encargue de suprimir y mitigar los errores por ti.

Limitaciones y advertencias

Dado que Executor y el modelo de ejecución dirigida se encuentran en fase beta, ten en cuenta lo siguiente antes de decidir realizar la migración:

  • Aún no hay compatibilidad con simuladores : a diferencia de Sampler, que cuenta con una AerSamplerimplementación para qiskit-aer la simulación local, actualmente no existe ningún backend de simulador para Executor. Se espera que la compatibilidad con el simulador esté disponible en breve. Mientras tanto, puedes seguir revisando y probando el circuito de la plantilla de forma local para validar tu flujo de trabajo antes de enviarlo al departamento de hardware.
  • Esta guía se refiere únicamente a Sampler, no a Estimator. La migración de «Estimator» a «Executor» es considerablemente más compleja que la migración desde «Sampler», ya que «Estimator» calcula valores esperados en lugar de devolver muestras sin procesar. Para reproducir el comportamiento de Estimator con Executor es necesario realizar un procesamiento posterior adicional. Las funciones de utilidad para facilitar la migración de Estimator a Executor aún se encuentran en fase de desarrollo, por lo que esta guía describe, de forma intencionada, únicamente el flujo de trabajo de Sampler.

Diferencias clave entre Executor y Sampler

Tanto «Sampler» como «Executor» muestrean los registros de salida de los circuitos cuánticos, pero se dirigen a usuarios diferentes:

  • Sampler es una abstracción de alto nivel. Presenta las siguientes características:
    • Cuenta con supresión de errores integrada (desacoplamiento dinámico y giro).
    • Toma decisiones implícitas por ti.
    • Está diseñado para que los desarrolladores de algoritmos puedan centrarse en la innovación en lugar de en los datos. conversión.
  • El ejecutor forma parte del modelo de ejecución dirigida. Se diferencia de Sampler en muchos aspectos y presenta las siguientes características:
    • No cuenta con ningún mecanismo integrado de supresión o mitigación de errores. En su lugar, se plasma la intención del diseño en el lado del cliente (mediante anotaciones de circuitos y un «samplex» ), y la costosa generación de variantes de circuitos se traslada al lado del servidor.

    • No toma ninguna decisión implícita. Sigue tus instrucciones al pie de la letra, lo que te ofrece un control total y total transparencia.

    • Executor y Samplomatic, en conjunto, ofrecen funciones adicionales que Sampler no ofrece, entre las que se incluyen (sin limitarse a ellas) las siguientes:

      • Más grupos de rotación: Samplomatic te permite elegir qué grupo de rotación aplicar a cada casilla, en lugar de limitarte a la única estrategia que Sampler aplica automáticamente. También admite grupos de giro distintos de los de Pauli, como el grupo de giro "local_c1" .
      • Mediciones con kernel y clasificadas conjuntamente: La configuración QuantumProgram.meas_level = "both" (añadida en v0.48.0qiskit-ibm-runtime ) solicita que en los resultados figuren tanto las mediciones clasificadas como las con kernel, en lugar de seleccionar un único tipo de medición por trabajo.
      • Aplicación de «twirling» a circuitos con puertas fraccionarias: Executor puede aplicar el «twirling» a circuitos que contengan puertas fraccionarias.
      • Mitigación de errores de alta precisión y combinable: por ejemplo, elegir qué capas del circuito se van a mitigar y ajustar los índices de ruido inyectados en el circuito.
      Notas
      • Se prevé que las futuras novedades se lancen primero en Executor y es posible que no se incorporen a Sampler. Si necesitas tener acceso a las últimas funciones, Executor es la opción más preparada para el futuro.
      • El paquete básico de Qiskit aún no ofrece una clase base para la primitiva Executor (sí la ofrece paraSamplerV2).

Mapas conceptuales

La siguiente tabla muestra cómo se corresponden los conceptos de Sampler con los de Executor.

Concepto
Muestreador
Ejecutor
Importarfrom qiskit_ibm_runtime import SamplerV2from qiskit_ibm_runtime import Executor
EntradaLista de PUB (tuplas)Un conjunto de QuantumProgram objetos QuantumProgramItem
Circuito y parámetros(circuit, params, shots) tuplaprogram.append_circuit_item(circuit, circuit_arguments=...)
GirandoTwirlingOptionsDe forma explícita mediante recuadros con anotaciones y un «samplex» (append_samplex_item)
Ejecutar llamadasampler.run([pub, ...])executor.run(program)
Tipo de resultadoPrimitiveResult de SamplerPubResultQuantumProgramResult (iterable)
Acceder a datosresult[0].data.<register> (BitArray)result[0]["<register>"] (np.ndarray)
Gestionar el ruidoOpciones integradasDebe crearse manualmente (anotaciones, samplex, etc. NoiseLearnerV3)

Resumen de los pasos de la migración

  1. Instala Samplomatic.
  2. Modifica las importaciones.
  3. Sustituir las tuplas « PUB ».
  4. Modificar la forma en que se expresan los tiros.
  5. Actualiza las demás opciones según sea necesario.
  6. Actualiza el comando run .
  7. Actualizar el análisis de resultados.
  8. Deshacer el giro.

Paso 1. Instala los paquetes necesarios

El ejecutor y el modelo de ejecución dirigida requieren el paquete samplomatic :

pip install qiskit qiskit-ibm-runtime samplomatic

# For visualization support:
# pip install samplomatic[vis]
Notas de la versión
  • qiskit-ibm-runtime v0.48.0 Se recomienda porque añade la opción meas_level = "both" y el grupo local_c1 giratorio.
  • qiskit >= 2.3.0 es necesario.
  • samplomatic >= 0.18.0 es necesario.

Paso 2. Modifica las importaciones

Muestra:

from qiskit_ibm_runtime import SamplerV2 as Sampler

Albacea:

from qiskit_ibm_runtime import Executor, QuantumProgram

Paso 3. Sustituye las tuplas « PUB » por un QuantumProgram

En lugar de pasar una lista de tuplas (PUBs), al utilizar Executor, se crea un QuantumProgram y se le añaden elementos.

A admite QuantumProgram elementos de circuito y elementos Samplex :

  • append_circuit_item: Añade un CircuitItem, que es un circuito y (opcionalmente) sus valores de parámetros. Se ejecuta tal cual, sin ningún tipo de aleatorización.

    Utiliza esta opción cuando solo quieras tomar una muestra de un circuito, exactamente igual que lo haría Sampler con un « PUB » sin giros; por ejemplo, al enviar un trabajo de muestreo sencillo, o cuando ya hayas incluido manualmente las variantes que desees.

  • append_samplex_item: Añade un samplexItem, que es un circuito de plantilla más un samplex que genera conjuntos de parámetros aleatorios en el lado del servidor.

    Utiliza esto cuando quieras que el contenido del circuito se muestre de forma aleatoria. El caso principal se da con el giro (de compuerta o de medida) o la inyección de ruido. Esta función sustituye al efecto de giro integrado en Sampler.

Un único QuantumProgram puede admitir ambos tipos de elementos; cada elemento añadido se ejecuta como una tarea independiente y genera su propia entrada en los resultados. En general, utilízalo append_circuit_item cuando tu circuito no necesite ser aleatorio. De lo contrario, utiliza append_samplex_item.

En los siguientes apartados se muestran, uno tras otro: circuitos parametrizados que utilizan append_circuit_item, y el «twirling» migratorio mediante el uso de append_samplex_item.

En los siguientes ejemplos de código, se refiere isa_circuit al circuito que se ha transpuesto para ajustarse a la arquitectura de conjunto de instrucciones (ISA) del backend de destino. Esto contiene isa_circuit dos parámetros.

Paso 3a. Migrar circuitos parametrizados

En Sampler, los valores de los parámetros son el segundo elemento de la tupla « PUB ». Con Executor, pásalas como circuit_arguments a append_circuit_item.

Muestra:

params = np.random.rand(10, circuit.num_parameters)  # 10 parameter sets
pubs = (isa_circuit, params)

Ejecutor

program = QuantumProgram(shots=1024)
program.append_circuit_item(
    isa_circuit,
    circuit_arguments=np.random.rand(10, circuit.num_parameters),  # 10 sets
)

# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]

Paso 3b. Migrar el «twirling» integrado a anotaciones explícitas

Este es el cambio más importante. Sampler aplica el efecto giratorio por ti mediante las opciones disponibles. Con Executor, se declara esa intención de forma explícita utilizando recuadros anotados y un «samplex» (de Samplomatic ).

Muestra (girando mediante las opciones):

sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = True

Ejecutor (giros con cajas y un samplex):

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
    enable_gates=True,     # gate twirling
    enable_measures=True,  # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)

# 2. Build the (template circuit, samplex) pair.
#    The template circuit's single-qubit gates are replaced by parameterized gates;
#    the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)

# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # original circuit params
    },
    shape=(28, 10),  # 28 randomizations x 10 parameter sets
)

Dado que el circuito de plantilla y el samplex se crean en el lado del cliente, puedes inspeccionarlos y tomar muestras de ellos localmente para verificar el resultado antes de enviar nada al hardware.

Verificación: Probar el circuito de la plantilla de forma local

Puedes extraer muestras aleatorias del «samplex» y vincularlas al circuito de plantilla para confirmar que el «samplex» está generando los valores de los parámetros que esperas. Los valores de los parámetros devueltos por samplex.sample son directamente compatibles con los parámetros del circuito de plantilla.

# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())

# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
    parameter_values=np.random.rand(2),  # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)

# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)

Para profundizar más, se puede verificar que cada aleatorización es lógicamente equivalente al circuito original, por ejemplo, convirtiendo ambos en objetos Operator y comparando sus implementaciones unitarias (tras tener en cuenta las outputs["measurement_flips.<register>"] correcciones que revierten el «twirling» de la medición), o comparando los valores esperados de una ejecución local StatevectorSampler o de una StatevectorEstimator ejecución. Consulta la guía de entradas y salidas del Samplomatic Samplex para obtener una explicación detallada.

Paso 4. Modificar la forma de solicitar las tomas

Mueve las imágenes de « PUB » a QuantumProgram(shots=...). En Executor, se shots aplica a todo el trabajo. Envía varios trabajos si necesitas un número diferente de tomas.

Muestra:

# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])

Albacea:

# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

Paso 5. Actualiza las opciones según sea necesario

Executor dispone de menos opciones que Sampler, ya que las opciones de mitigación de errores ahora se encuentran en las anotaciones y en «samplex», en lugar de en las opciones.

También existe una diferencia estructural en cuanto a la ubicación de los ajustes.

  • Con Sampler, todo, incluidas las opciones que afectan al posprocesamiento de los resultados, se configura en las opciones de la primitiva o en el archivo « PUB ».

  • Con Executor, las opciones que influyen en cómo se configuran y se procesan posteriormente los resultados del trabajo se establecen en el QuantumProgram, y no en el ExecutorOptions.

Ejemplos:

Muestreador
Ejecutor
shotsQuantumProgram(shots=...)
meas_typeQuantumProgram(meas_level=...)

ExecutorOptions solo contiene ajustes de ejecución y de entorno de nivel inferior que no modifican la estructura de los datos devueltos. Cuenta con tres grupos de primer nivel:

Cabe destacar que las opciones dynamical_decoupling y twirling están disponibles en Sampler, pero no en Executor. En cambio, esos valores de opción se expresan a través del modelo de ejecución dirigida.

Ejemplo:

from qiskit_ibm_runtime import Executor, ExecutorOptions

options = ExecutorOptions(
    environment={"log_level": "INFO"},
    execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True

executor = Executor(mode=backend, options=options)

Paso 6. Actualizar el comando run

La entrada de un trabajo de Executor es el programa, en lugar de los PUB.

Muestra:

# Submit a job
sampler.run([(isa_circuit, parameter_values)])

Albacea:

# Submit a job
executor.run(program)

Paso 7. Cambia la forma de acceder a los resultados

En Executor, los resultados son matrices de tipo « NumPy », no BitArray objetos. Utiliza la cadena de nombre como índice (result[0]["meas"]) y obtén un como resultado np.ndarray . No es necesario recordar la ruta del atributo .data.<register> .

Para pasar de Sampler a Executor, cambia result[i].data.<reg> (BitArray) por result[i]["<reg>"] (np.ndarray), y a continuación reescribe el posprocesamiento get_countsbasado en - como operaciones de NumPy.

Tarea
Muestreador
Ejecutor
Obtener datos de registroresult[0].data.measresult[0]["meas"]
Tipo de datosBitArraynp.ndarray
Diccionario de contadoresresult[0].data.meas.get_counts()Realizar el posprocesamiento de la matriz manualmente
Varios registrosresult[0].data.<name> por registroresult[0]["<name>"] por registro
CircuitItem forma de la matriz-(parameter_sets, shots, register_bits)
SamplexItem forma de la matriz-(randomizations, parameter_sets, shots, register_bits)
Deshacer el giro de la mediciónAutomáticoresult[i]["measurement_flips.<name>"] + XOR
Note

Sampler's BitArray ofrece herramientas de ayuda (get_counts, slice_bits, slice_shots, expectation_values, y máscaras de poselección). El ejecutor devuelve matrices « NumPy » sin procesar, por lo que puedes realizar este posprocesamiento con operaciones estándar de « NumPy ».

Paso 8. Gestionar resultados alterados (correcciones de inversión de bits)

Cuando se aplica la rotación de medidas a través de un SamplexItem, Executor devuelve las medidas sin procesar (rotadas) junto con las correcciones de inversión de bits necesarias para revertir la rotación. Hay que aplicarlas manualmente; no se corrige nada de forma automática.

Al utilizar Executor, desactiva el «twirling» de forma explícita mediante las correcciones measurement_flips.<reg> y una operación XOR, tal y como se muestra en el siguiente ejemplo:

# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"]                       # example: (28, 10, 1024, 2)

# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"]      # example: (28, 10, 1, 2)

# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1

En Sampler no existe un paso equivalente, ya que la aplicación desactiva automáticamente el efecto de giro por ti.


Ejemplo completo: Migrar un trabajo de muestreo básico

Muestreador

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()

# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()

Ejecutor

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()

# 6. Access results: a plain np.ndarray keyed by register name
#    shape = (shots, register_bits)
meas = result[0]["meas"]

Próximos pasos

¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.