Skip to main content
IBM Quantum Platform

Transmisión del ejecutor

Los datos proporcionados a la primitiva «Executor» pueden organizarse en diversas estructuras para ofrecer flexibilidad en una carga de trabajo mediante la difusión. En esta guía se explica cómo gestiona el Executor las entradas y salidas de matrices mediante la semántica de difusión. Comprender estos conceptos te ayudará a recorrer de forma eficaz los valores de los parámetros, combinar varias configuraciones e interpretar la estructura de los datos devueltos.

Note

Los ejemplos de este tema no se pueden ejecutar por sí solos. Se da por hecho que has definido los circuitos adecuados, que has utilizado el gestor de pasos de Samplomatic para añadir cuadros y anotaciones, y que has utilizado el método de Samplomatic build para obtener un circuito de plantilla y un samplex para cada bloque de código, según sea necesario.


Ejemplo de inicio rápido

Este ejemplo ilustra la idea fundamental. Crea un circuito paramétrico y cinco configuraciones de parámetros diferentes. El ejecutor ejecuta las cinco configuraciones y devuelve los datos organizados por configuración, con un resultado por cada registro clásico en cada elemento del programa cuántico.

El resto de esta guía se basa en este ejemplo para explicar cómo funciona y cómo crear barridos más complejos, incluyendo la aleatorización basada en Samplomatic y las entradas.

import numpy as np
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

# A circuit with 2 parameters
# This circuit is used throughout the rest of this guide.
circuit = QuantumCircuit(4)
circuit.rx(Parameter("a"), 0)
circuit.rx(Parameter("b"), 1)
circuit.h(2)
circuit.cx(2, 3)
circuit.measure_all()

# 5 different parameter configurations (shape: 5 configurations × 2 parameters)
parameter_values = np.linspace(0, np.pi, 10).reshape(5, 2)

# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

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

# This program is used throughout the rest of this guide.
program = QuantumProgram(shots=1024)
program.append_circuit_item(isa_circuit, circuit_arguments=parameter_values)

# initialize an Executor with default options
executor = Executor(mode=backend)

# Run and get results
result = executor.run(program).result()

# result is a list with one entry per program item
# result[0] is a dict mapping classical register names to data arrays
# Output bool arrays have shape (5, 1024, 4)
#   5 = number of parameter configurations
#   1024 = number of shots
#   4 = bits in the classical register
result[0]["meas"]

Ejes intrínsecos y extrínsecos

La difusión solo se aplica a los ejes extrínsecos. Los ejes intrínsecos siempre se conservan tal y como se ha especificado.

  • Ejes intrínsecos (los de la derecha): vienen determinados por el tipo de datos. Por ejemplo, si tu circuito tiene tres parámetros, los valores de los parámetros requieren tres números, lo que da lugar a una forma intrínseca de (3,).

  • Ejes extrínsecos (los más a la izquierda): tus dimensiones de barrido. Esto determina cuántas configuraciones quieres ejecutar.

Tipo de entrada
Forma intrínseca
Ejemplo de forma completa
Valores de los parámetros (n parámetros)(n,)(5, 3) para cinco configuraciones y tres parámetros
Entradas escalares (por ejemplo, escala de ruido)()(4,) para cuatro configuraciones
Observables (si procede)varíaDepende del tipo de observable

Ejemplo

Imaginemos un circuito con dos parámetros que queremos barrer a lo largo de una red de configuraciones « 4x3 », variando los valores de los parámetros y un factor de escala de ruido:

import numpy as np

# Parameter values: 4 configurations along axis 0, intrinsic shape (2,)
# Full shape: (4, 1, 2) - the "1" allows broadcasting with noise_scale
parameter_values = np.array([
    [[0.1, 0.2]],
    [[0.3, 0.4]],
    [[0.5, 0.6]],
    [[0.7, 0.8]],
])  # shape (4, 1, 2)

# Noise scale: 3 configurations, intrinsic shape () (scalar)
# Full shape: (3,)
noise_scale = np.array([0.8, 1.0, 1.2])  # shape (3,)

# Extrinsic shapes: (4, 1) and (3,) → broadcast to (4, 3)
# Result: 12 total configurations in a 4×3 grid
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": parameter_values,
        "noise_scales.mod_ref1": noise_scale,
    },
)

Las formas son las siguientes:

Entrada
Forma completa
Forma extrínseca
Forma intrínseca
parameter_values(4, 1, 2)(4, 1)(2,)
noise_scale(3,)(3,)()
DifusiónNinguna(4, 3)Ninguna

Formatos de las matrices de salida

Las matrices de salida siguen el mismo patrón extrínseco/intrínseco:

  • Forma extrínseca: se ajusta a la forma de difusión de todas las entradas
  • Forma intrínseca: viene determinada por el tipo de salida

El resultado más habitual son los datos en forma de cadena de bits procedentes de las mediciones, que se presentan como una matriz de valores booleanos:

Tipo de salida
Forma intrínseca
Descripción
Datos del registro clásico(núm. de disparos, tamaño del creg)Datos de cadenas de bits procedentes de mediciones

Ejemplo

Si se proporcionan entradas con formas (4, 1) extrínsecas y (3,), la forma extrínseca de difusión es (4, 3). El siguiente código utiliza un circuito con 1024 disparos y un registro clásico de 4 bits (tal y como se define en el ejemplo de la guía de inicio rápido ):

# Input extrinsic shapes: (4, 1) and (3,) → (4, 3)
# Output for classical register "meas":
#   extrinsic: (4, 3)
#   intrinsic: (1024, 4)  - shots × bits
#   full shape: (4, 3, 1024, 4)

result = executor.run(program).result()
meas_data = result[0]["meas"]  # result[0] for first program item
print(meas_data.shape)  # (4, 3, 1024, 4)

# Access a specific configuration
config_2_1 = meas_data[2, 1, :, :]  # shape (1024, 4)
Note

Cada configuración ejecuta el recuento total de disparos especificado en el programa cuántico. Los tiros no se reparten entre las distintas configuraciones. Por ejemplo, si solicitas 1024 disparos y tienes 10 configuraciones, cada configuración ejecuta 1024 disparos (un total de 10 240 disparos ejecutados).


La aleatorización y el shape parámetro

Cuando se utiliza un samplex, cada elemento de la forma extrínseca se corresponde con una ejecución de circuito independiente. El samplex suele introducir aleatoriedad (por ejemplo, mediante la rotación de puertas) en cada ejecución, por lo que, incluso sin solicitar explícitamente múltiples aleatorizaciones, cada elemento recibe una realización aleatoria.

Puedes utilizar el shape parámetro para ampliar la forma extrínseca del elemento, lo que equivale, en la práctica, a añadir ejes que se corresponden específicamente con la repetición aleatoria de la misma configuración en numerosas ocasiones. Debe poder emitirse a partir de la forma implícita en tu samplex_arguments. Los ejes en los que shape supera la forma implícita enumeran aleatorizaciones independientes adicionales.

No hay ejes de aleatorización explícitos

Si lo omites shape (o lo configuras para que se adapte a tus formas de entrada), obtendrás una ejecución por cada configuración de entrada. Cada ejecución sigue siendo aleatoria gracias al samplex, pero con una única realización aleatoria no se obtiene la ventaja de promediar los resultados de varias aleatorizaciones.

Note

Si estás acostumbrado a habilitar el «twirling» con un simple indicador como twirling=True, ten en cuenta que el Executor requiere que solicites explícitamente varias aleatorizaciones mediante el shape argumento para que tus rutinas de posprocesamiento puedan beneficiarse del promedio obtenido a partir de varias aleatorizaciones. Se aplica una aleatorización única (la opción predeterminada cuando shape se omite ) que aplica puertas aleatorias, pero que normalmente no ofrece ninguna ventaja con respecto a la ejecución del circuito básico sin aleatorización.

El siguiente ejemplo muestra el comportamiento predeterminado:

program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # extrinsic (10,)
    },
    # shape defaults to (10,) - one randomized execution per config
)
# Output shape for "meas": (10, num_shots, creg_size)

Eje de aleatorización único

Para realizar varias aleatorizaciones por configuración, amplíe la figura añadiendo ejes adicionales. Por ejemplo, el siguiente código ejecuta 20 simulaciones aleatorias para cada una de las 10 configuraciones de parámetros:

program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # extrinsic (10,)
    },
    shape=(20, 10),  # 20 randomizations × 10 configurations
)
# Output shape for "meas": (20, 10, num_shots, creg_size)

Múltiples ejes de aleatorización

Puedes organizar las aleatorizaciones en una cuadrícula multidimensional. Esto resulta útil para el análisis estructurado, por ejemplo, para clasificar los ensayos aleatorios por tipo o agruparlos con vistas a su procesamiento estadístico.

(2, 14, 10)En este caso, la forma (10,) extrínseca de entrada se transmite a la forma solicitada, con los ejes 0 y 1 rellenados mediante aleatorizaciones independientes.

program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "parameter_values": np.random.rand(10, 2),  # extrinsic (10,)
    },
    # 2×14=28 randomizations per configuration, 10 configurations
    # Or you could set shape=(28, 10) for the same effect
    shape=(2, 14, 10),  
)
# Output shape for "meas": (2, 14, 10, num_shots, creg_size)

Cómo shape interactúan las formas de entrada

El shape parámetro debe poder transmitirse desde tus formas extrínsecas de entrada. Esto significa:

  • Las formas de entrada con dimensiones de « size-1 » pueden ampliarse para ajustarse a shape.
  • Las formas de entrada deben alinearse a la derecha con shape.
  • Los shape ejes que superan las dimensiones de entrada enumeran las aleatorizaciones.

Tenga en cuenta que shape puede contener dimensiones de tipo « size-1 » que se amplían para ajustarse a las dimensiones de entrada, tal y como se muestra en la última fila de la tabla siguiente.

Ejemplos:

Entrada extrínseca
Forma
Resultado
(10,)(10,)10 configuraciones, con una aleatorización cada una
(10,)(5, 10)10 configuraciones, con 5 aleatorizaciones cada una
(10,)(2, 3, 10)10 configuraciones, 2 × 3 = 6 aleatorizaciones cada una
(4, 1)(4, 5)4 configuraciones, con 5 aleatorizaciones cada una
(4, 3)(2, 4, 3)4 × 3 = 12 configuraciones, con 2 aleatorizaciones cada una
(4, 3)(2, 1, 3)4 × 3 = 12 combinaciones, con 2 aleatorizaciones cada una (el 1 se expande a 4)

Ir a los resultados

Con los ejes de aleatorización, puedes acceder a combinaciones específicas de aleatorización y parámetros:

# Using shape=(2, 14, 10) with input extrinsic shape (10,), and 
# 1024 shots and 4 classical registers.
result = executor.run(program).result()
meas_data = result[0]["meas"]  # shape (2, 14, 10, 1024, 4)

# Get all shots for randomization (0, 7) and parameter config 3
specific = meas_data[0, 7, 3, :, :]  # shape (1024, 4)

# Average over all randomizations for parameter config 5 on bit 2
averaged = meas_data[:, :, 5, :, 2].mean(axis=(0, 1))

Patrones habituales

Recorrer un único parámetro

Utiliza un código como el siguiente para variar un parámetro mientras mantienes los demás fijos:

# Circuit has 2 parameters, sweep first one over 20 values
sweep_values = np.linspace(0, 2*np.pi, 20)

parameter_values = np.column_stack([
    sweep_values,
    np.full(20, 0.5),
])  # shape (20, 2)

Creación de un barrido de cuadrícula « 2D »

Para crear una cuadrícula basada en tres parámetros:

# Sweep param 0 over 10 values, param 1 over 8 values, param 2 fixed
p0 = np.linspace(0, np.pi, 10)[:, np.newaxis, np.newaxis]  # (10, 1, 1)
p1 = np.linspace(0, np.pi, 8)[np.newaxis, :, np.newaxis]   # (1, 8, 1)
p2 = np.array([[[0.5]]])                                   # (1, 1, 1)

parameter_values = np.broadcast_arrays(p0, p1, p2)
parameter_values = np.stack(parameter_values, axis=-1).squeeze()  # (10, 8, 3)

# Extrinsic shape: (10, 8), intrinsic shape: (3,)

Combinación de varias entradas

Al combinar elementos con formas intrínsecas diferentes, alinea las dimensiones extrínsecas utilizando los ejes de « size-1 »:

# 4 parameter configurations, 3 noise scales → 4×3 = 12 total configurations
parameter_values = np.random.rand(4, 1, 2)  # extrinsic (4, 1), intrinsic (2,)
noise_scale = np.array([0.8, 1.0, 1.2])     # extrinsic (3,), intrinsic ()

# Broadcasted extrinsic shape: (4, 3)

Próximos pasos

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