Skip to main content
IBM Quantum Platform

Trasmissione dell'esecutore

I dati forniti alla primitiva Executor possono essere organizzati in diverse configurazioni per garantire flessibilità nel carico di lavoro tramite la trasmissione diffusa. Questa guida spiega come Executor gestisce gli input e gli output degli array utilizzando la semantica del broadcasting. Comprendere questi concetti ti aiuterà a scorrere in modo efficiente i valori dei parametri, a combinare più configurazioni e a interpretare la struttura dei dati restituiti.

Note

Gli esempi riportati in questa sezione non possono essere eseguiti autonomamente. Si presume che abbiate definito i circuiti appropriati, che abbiate utilizzato il gestore dei passaggi di Samplomatic per aggiungere riquadri e annotazioni e che abbiate utilizzato il metodo Samplomatic build per ottenere un circuito modello e un samplex per ciascun blocco di codice, se necessario.


Esempio di avvio rapido

Questo esempio illustra il concetto fondamentale. Crea un circuito parametrico e cinque diverse configurazioni dei parametri. L'esecutore esegue tutte e cinque le configurazioni e restituisce i dati organizzati per configurazione, con un risultato per ogni registro classico in ogni elemento del programma quantistico.

Il resto di questa guida fa riferimento a questo esempio per spiegare come funziona e come creare sweep più complessi, compresa la randomizzazione basata su Samplomatic e gli input.

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

Assi intrinseci ed estrinseci

La trasmissione si applica solo agli assi estrinseci. Gli assi intrinseci vengono sempre mantenuti come specificato.

  • Assi intrinseci (all'estrema destra): determinati dal tipo di dati. Ad esempio, se il circuito ha tre parametri, i valori dei parametri richiedono tre numeri, il che determina una forma intrinseca di (3,).

  • Assi estrinseci (all'estrema sinistra): le dimensioni di scansione. Queste opzioni definiscono il numero di configurazioni che si desidera eseguire.

Tipo di input
Forma intrinseca
Esempio di forma completa
Valori dei parametri (n parametri)(n,)(5, 3) per cinque configurazioni e tre parametri
Ingressi scalari (ad esempio, scala del rumore)()(4) per quattro configurazioni
Osservabili (se applicabile)varianzeDipende dal tipo dell'osservabile

Esempio

Si consideri un circuito con due parametri che si desidera variare su una griglia di configurazioni 4x3, modificando i valori dei parametri e un fattore di scala del rumore:

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

Le forme sono le seguenti:

Immissione
Forma completa
Forma estrinseca
Forma intrinseca
parameter_values(4, 1, 2)(4, 1)(2,)
noise_scale(3,)(3,)()
TrasmissioneNessuna(4, 3)Nessuna

Formati degli array di output

Gli array di output seguono lo stesso schema estrinseco/intrinseco:

  • Forma estrinseca: corrisponde alla forma di trasmissione di tutti gli input
  • Forma intrinseca: determinata dal tipo di output

Il risultato più comune è costituito dai dati delle misurazioni sotto forma di stringa di bit, formattati come un array di valori booleani:

Tipo di output
Forma intrinseca
Descrizione
Dati relativi al registro classico(numero_scatti, dimensione_creg)Dati in formato stringa di bit ricavati dalle misurazioni

Esempio

Se si specificano input con forme (4, 1) estrinseche e (3,), la forma estrinseca di trasmissione è (4, 3). Il codice seguente utilizza un circuito con 1024 impulsi e un registro classico a 4 bit (come definito nell'esempio della Guida rapida ):

# 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

Ogni configurazione esegue il numero totale di misurazioni specificato nel programma quantistico. I colpi non sono ripartiti tra le diverse configurazioni. Ad esempio, se si richiedono 1024 scatti e si dispone di 10 configurazioni, ciascuna configurazione esegue 1024 scatti (per un totale di 10.240 scatti eseguiti).


Randomizzazione e il shape parametro

Quando si utilizza un samplex, ogni elemento della forma estrinseca corrisponde all'esecuzione di un circuito indipendente. Il samplex introduce solitamente un elemento di casualità (ad esempio, la rotazione dei gate) in ogni esecuzione, quindi, anche senza richiedere esplicitamente più randomizzazioni, ogni elemento ottiene una realizzazione casuale.

È possibile utilizzare il shape parametro per ampliare la forma estrinseca dell'elemento, aggiungendo di fatto assi che corrispondono specificatamente alla ripetizione casuale della stessa configurazione più volte. Deve essere trasmissibile a partire dalla forma implicita nel tuo samplex_arguments. Gli assi in cui shape supera la forma implicita enumerano ulteriori randomizzazioni indipendenti.

Nessun asse di randomizzazione esplicito

Se lo si omette shape (o lo si imposta in modo che corrisponda alle forme di input), si ottiene un'esecuzione per ogni configurazione di input. Ogni esecuzione è comunque randomizzata dal samplex, ma con una sola realizzazione casuale non si beneficia della media ottenuta da più randomizzazioni.

Note

Se sei abituato ad abilitare il twirling con un semplice flag come twirling=True, tieni presente che Executor richiede di richiedere esplicitamente più randomizzazioni tramite l'argomento shape per consentire alle tue routine di post-elaborazione di trarre vantaggio dalla media calcolata su più randomizzazioni. Si applica una singola randomizzazione (l'impostazione predefinita quando shape viene omesso) che utilizza porte casuali, ma in genere non offre alcun vantaggio rispetto all'esecuzione del circuito di base senza randomizzazione.

L'esempio seguente illustra il comportamento predefinito:

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)

Asse di randomizzazione singolo

Per eseguire più randomizzazioni per ogni configurazione, estendere la figura aggiungendo ulteriori assi. Ad esempio, il codice seguente esegue 20 simulazioni per ciascuna delle 10 configurazioni dei parametri:

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)

Assi di randomizzazione multipli

È possibile organizzare le randomizzazioni in una griglia multidimensionale. Ciò risulta utile per l'analisi strutturata, ad esempio per separare le randomizzazioni in base al tipo o raggrupparle ai fini dell'elaborazione statistica.

(2, 14, 10)In questo caso, la forma (10,) estrinseca di input viene trasmessa alla forma richiesta, con gli assi 0 e 1 popolati da variabili casuali indipendenti.

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)

Come shape interagiscono le forme di input

Il shape parametro deve essere trasmissibile dalle forme estrinseche di input. Ciò significa che:

  • shapeLe forme inserite con dimensioni " size-1 " possono espandersi per adattarsi.
  • Le forme inserite devono essere allineate a destra con shape.
  • Gli assi che superano le dimensioni di shape input generano una serie di randomizzazioni.

Si noti che shape può contenere dimensioni di tipo " size-1 " che si espandono per adattarsi alle dimensioni dell'input, come illustrato nell'ultima riga della tabella seguente.

Esempi:

Input estrinseco
Forma
Risultato
(10,)(10,)10 configurazioni, ciascuna con una randomizzazione
(10,)(5, 10)10 configurazioni, ciascuna con 5 randomizzazioni
(10,)(2, 3, 10)10 configurazioni, 2×3=6 randomizzazioni ciascuna
(4, 1)(4, 5)4 configurazioni, ciascuna con 5 randomizzazioni
(4, 3)(2, 4, 3)4×3=12 combinazioni, con 2 randomizzazioni per ciascuna
(4, 3)(2, 1, 3)4×3=12 combinazioni, con 2 randomizzazioni per ciascuna (il 1 si espande a 4)

Indice dei risultati

Grazie agli assi di randomizzazione, è possibile accedere a specifiche combinazioni di randomizzazione e parametri:

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

Modelli ricorrenti

Analizzare un singolo parametro

Utilizza un codice simile al seguente per variare un parametro mantenendo fissi gli altri:

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

Creazione di una scansione a griglia " 2D "

Per creare una griglia basata su tre parametri:

# 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,)

Combinazione di più input

Quando si combinano elementi con forme intrinseche diverse, allineare le dimensioni estrinseche utilizzando gli assi dell' 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)

Passi successivi

Suggerimenti
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.