Esempi di esecutori testamentari
Il codice presente in questa pagina è stato sviluppato sulla base dei seguenti requisiti. Si consiglia di utilizzare queste versioni o quelle più recenti.
qiskit[all]~=2.4.0 qiskit-ibm-runtime~=0.46.1 samplomatic~=0.18.0
Gli esempi riportati in questa sezione illustrano alcuni modi comuni di utilizzare la primitiva Executor. Prima di eseguire questi esempi, segui le istruzioni riportate nella guida rapida "Installazione di Qiskit ed Executor".
Prima di iniziare
Alcuni degli esempi di codice presenti in questa pagina utilizzano samplex, che fa parte del pacchetto Samplomatic. Pertanto, prima di eseguire quei blocchi di codice, è necessario installare Samplomatic, come illustrato nel blocco di codice seguente. Per ulteriori informazioni, consultare la documentazione di Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]Esempio: circuito parametrizzato
Questo esempio illustra come aggiungere elementi di circuito con parametri, nonché come aggiungere elementi Samplex. Si articola nelle seguenti fasi:
- Configurare il circuito: generare e transpilare il circuito di destinazione.
- Preparare un samplex: raggruppare i gate e le misurazioni in riquadri annotati e generare il modello del circuito e la coppia samplex.
- Esegui: aggiungi un elemento di circuito e un elemento Samplex a un
QuantumProgramed esegui entrambi in un unico processo.
Configurare il circuito
Preparare uno stato GHZ a tre qubit, ruotare i qubit attorno all'asse di Pauli-Z e misurare i qubit nella base computazionale.
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()Specificare il backend e trasporre il circuito in modo che utilizzi solo istruzioni supportate dalla QPU (circuito denominato "architecture del set di istruzioni" 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)Preparare il campione
Utilizza la generate_boxing_pass_manager funzione di comodità e i relativi parametri di rotazione per raggruppare porte a due qubit e misurazioni in riquadri e applicare annotazioni di rotazione.
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)Utilizza il build metodo per generare il circuito modello e il samplex.
# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)Esegui i circuiti
Executor esegue QuantumProgram gli oggetti. Ciascuno QuantumProgram può contenere diversi elementi. Questo esempio aggiunge un elemento di circuito e un elemento Samplex da eseguire. Per ulteriori dettagli, consultare la sezione "Input e output di Executor".
Il primo passo consiste nell'inizializzare un programma vuoto, richiedendo 1024 delle immagini per ogni configurazione di ciascun elemento.
# Generate a quantum program
program = QuantumProgram(shots=1024)QuantumProgramAggiungi l'elemento del circuito al. Questo elemento del circuito è composto da due parti: il circuito ISA e 10 set di valori dei suoi parametri.
# 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 Aggiungi l'elemento samplex a con questi argomenti:
- Il circuito modello e il samplex generato dalla
buildfunzione - Dieci serie di valori dei parametri per il circuito originale
- Il numero di randomizzazioni da eseguire
# 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),
)Esegui il processo Executor
# initialize an Executor with default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()Recupera il risultato per ogni attività.
# 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"]Esempio: Eseguire il PEC
Questo esempio illustra come utilizzare un elemento Samplex per eseguire la cancellazione probabilistica degli errori ( PEC ) al fine di ridurre gli errori.
Si consideri una versione speculare di un circuito composto da dieci qubit e due livelli distinti di porte CX. Questi sono i compiti principali:
- Esegui il circuito con dei giri.
- Eseguire il circuito con mitigazione PEC, come descritto nell'articolo "Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors".
Il processo si articola nelle seguenti fasi:
- Configurazione: generare il circuito di destinazione e raggrupparne le operazioni in riquadri.
- Identificare: identificare il rumore delle istruzioni che si desidera attenuare con il PEC.
- Esegui: esegui il circuito su un backend.
- Analisi: elaborare e analizzare i risultati.
A titolo di confronto, eseguiremo questo circuito speculare due volte. Una volta con l'applicazione del solo effetto Pauli, e una volta con l'applicazione della correzione PEC.
L'esecuzione di questo esempio richiede circa 10 minuti su un processore Heron r2.
Configurare il circuito
Scegli un backend e prepara un circuito a 10 qubit.
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 il circuito con il suo inverso per creare un circuito speculare.
mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()
mirror_circuit.draw("mpl", scale=0.35, fold=100)Output:
Imposta alcuni valori dei parametri:
import numpy as np
parameter_values = np.random.rand(mirror_circuit.num_parameters)Utilizza il gestore di passaggi per trasporre il circuito in un circuito ISA.
preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)
isa_circuit = preset_pass_manager.run(mirror_circuit)Successivamente, raggruppa i gate e le misurazioni in riquadri annotati. Puoi farlo manualmente oppure, per comodità, utilizzare la generate_boxing_pass_manager funzione di Samplomatic. Twirl Al primo circuito verrà applicata solo la rotazione e pertanto richiede solo l'annotazione. Il secondo circuito verrà eseguito con la mitigazione PEC completa e richiede sia Twirl le annotazioni che 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)Impara a riconoscere il rumore
Per ridurre al minimo il numero di esperimenti di apprendimento del rumore, individuare le istruzioni uniche presenti nel secondo circuito (quello con i riquadri contrassegnati con InjectNoise). Ai fini della definizione di unicità, due istruzioni "box" sono uguali se si verificano entrambe le seguenti condizioni:
- Il loro contenuto è identico, fino ai gate a singolo qubit.
- La loro
Twirlannotazione è identica (tutte le altre annotazioni vengono ignorate).
Ciò porta a tre istruzioni distinte, ovvero le caselle dei gate pari e dispari e la casella della misurazione finale.
from samplomatic.utils import find_unique_box_instructions
unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3Inizializzare un NoiseLearnerV3, scegliere i parametri di apprendimento impostando le relative opzioni ed eseguire un processo di apprendimento del rumore.
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()Converti result l'oggetto nel formato richiesto dal samplex utilizzando il result.to_dict metodo.
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)Esegui i circuiti
Executor esegue QuantumProgram gli oggetti. Ciascuno QuantumProgram può contenere diversi elementi, che vengono aggiunti al programma. Ogni voce rappresenta un'operazione che il programma deve eseguire.
Avvia un programma vuoto, richiedendo 1000 immagini per ogni configurazione di ciascun elemento.
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)Successivamente, crea il circuito modello e il codice di esempio per mirror_circuit_twirl e aggiungili al programma. Richiedi 900 anche randomizzazioni dal samplex. Ciò significa che il samplex genererà 900 insiemi di parametri e che ciascun insieme verrà eseguito 1000 un numero di volte pari al numero di shot nella QPU.
Questo è il primo compito del programma (risultato 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,),
)Allo stesso modo, aggiungi il circuito modello e il samplex creati per mirror_circuit_pec, richiedendo 900 delle randomizzazioni. Questo è il secondo compito del programma (risultato 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,),
)Importa Executor e invia un lavoro.
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)
Analizzare i risultati
Infine, si procede alla post-elaborazione dei risultati per stimare i valori attesi degli operatori di Pauli-Z su un singolo qubit che agiscono su ciascuno dei dieci qubit attivi (valore atteso: 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
Passi successivi
- Consulta la panoramica sulle trasmissioni.
- Scopri come utilizzare le opzioni di Executor.
- Comprendere il modello di esecuzione diretta.
- Consulta la documentazione di Samplomatic.
- Scopri come combinare diverse tecniche di mitigazione degli errori quando si utilizza il modello di esecuzione diretta nel tutorial " Probabilistic error cancellation with shaded lightcones ".