Executor-Eingaben und -Ausgaben
Paketversionen
Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
samplomatic~=0.18.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic
Das Executor-Primitive ist Teil des directed execution model (gerichteten Ausführungsmodells), das mehr Flexibilität bei der Anpassung eines Fehlerminderungsworkflows bietet.
Die Eingaben und Ausgaben des Executor-Primitives unterscheiden sich stark von denen der Sampler- und Estimator-Primitives. Anstatt zum Beispiel eine Liste von PUBs als Eingabe zu nehmen, nimmt Executor ein QuantumProgram, das eine Liste von QuantumProgramItem-Objekten enthält. Diese Container-Klassen bieten mehr Flexibilität als ein PUB, das eine einfache Tupel-Datenstruktur ist.
Die Ausgabe des Executors ist ein QuantumProgramResult, das iterierbar ist und ein Element für jedes eingegebene QuantumProgramItem enthält.
Eingaben: Quantenprogramme
Wie bereits erwähnt, ist die Eingabe für ein Executor-Primitive ein QuantumProgram, das ein Iterable von QuantumProgramItem-Objekten ist. Diese Objekte können zwei Typen haben:
CircuitItem, das typischerweise einen Circuit und seine Parameterwerte (falls vorhanden) speichert.SamplexItem, das typischerweise Folgendes speichert:- Einen Template-Circuit
- Ein Samplex-Objekt, das zur Laufzeit randomisierte Parametersätze erzeugt (z. B. zur Durchführung von Twirling oder zum Einschleusen von Rauschen)
- Argumente für den Samplex, die möglicherweise Parameterwerte für den ursprünglichen Circuit enthalten
Jedes dieser Elemente stellt eine andere Aufgabe dar, die Executor ausführen soll.
Bevor du anfängst
Einige der Codebeispiele auf dieser Seite verwenden samplex, das Teil des Samplomatic-Pakets ist. Bevor du diese Codeblöcke ausführst, musst du daher Samplomatic installieren, wie im folgenden Codeblock gezeigt. Weitere Informationen findest du in der Samplomatic-Dokumentation.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]
Beispiel: Ein QuantumProgram mit zwei verschiedenen Aufgaben erstellen
Initialisiere zunächst dein Quantenprogramm und füge dann Programmelemente mit append_circuit_item oder append_samplex_item (falls ein Samplex vorhanden ist) hinzu, wie in den folgenden Beispielen gezeigt.
Die folgende Zelle initialisiert ein QuantumProgram und legt fest, dass für jede Konfiguration jedes Elements im Programm 1024 Shots ausgeführt werden sollen.
Anders als beim Sampler akzeptiert ein QuantumProgram nur einen einzigen Shot-Wert. Wenn du einen anderen Shot-Wert möchtest, benötigst du ein separates QuantumProgram, was einem separaten Job entspricht.
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit_ibm_runtime import Executor, QiskitRuntimeService
from qiskit.circuit import Parameter, QuantumCircuit
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Initialize an empty program
program = QuantumProgram(shots=1024)
# Initialize and transpile a 3-qubit quantum circuit with 2 parameters.
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
# `measure_all` adds a 3-bit classical register named "meas"
circuit.measure_all()
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Generate a preset pass manager
# This will be used to convert the abstract circuit to an
# equivalent Instruction Set Architecture (ISA) circuit.
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)
# Transpile the circuit
isa_circuit = preset_pass_manager.run(circuit)
Ein CircuitItem anhängen
Füge anschließend den Ziel-Circuit, der gemäß der Instruction Set Architecture (ISA) des Backends transpiliert wurde, zum QuantumProgram hinzu. Da dieser Circuit zwei Parameter hat, müssen wir auch die Parameterwerte angeben (in diesem Beispiel 10 Sätze). Das Ausführen dieses CircuitItem ist die erste Aufgabe, die das Programm durchführen wird.
# Append the transpiled circuit and an array
# containing 10 sets of parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(
10, 2
), # 10 sets of parameter values and 2 parameters
)
Ein SamplexItem anhängen
Circuit-Elemente werden ohne jegliche Randomisierung ausgeführt. Samplex-Elemente hingegen erlauben es dir, die Randomisierung ihres Inhalts festzulegen. Die nächste Zelle verwendet die Funktion generate_boxing_pass_manager(), um die Gates und Messungen des Circuits in Boxen zu gruppieren und jeder Box eine Twirling-Annotation hinzuzufügen. Anschließend werden mithilfe der Funktion build() ein Template-Circuit und ein Samplex-Paar generiert.
Das Ausführen dieses SamplexItem ist die zweite Aufgabe, die das Programm durchführen wird.
Vollständige Details zu samplex und seinen Argumenten findest du in der Samplomatic-API-Dokumentation. Informationen zur Verwendung der Funktion generate_boxing_pass_manager() findest du im Samplomatic-Transpiler-Leitfaden.
# Transpile the circuit, additionally grouping gates and measurements into annotated boxes
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=0
)
# Use the boxing pass manager to group gates
# and measurements into boxes and add
# a`Twirl` annotation.
preset_pass_manager.post_scheduling = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = preset_pass_manager.run(circuit)
# Build the template circuit and the samplex. The template circuit has parametric gates
# without fixed values and the samplex randomly generates the parameter
# values on the server side at runtime to perform twirling.
template_circuit, samplex = build(boxed_circuit)
# Determine what arguments are required by the samplex.
# Input the arguments in samplex_arguments.
print(samplex.inputs())
TensorInterface(<
- 'parameter_values' <float64[2]>: Input parameter values to use during sampling.
>)
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
# the arguments required by the samplex.sample method
"parameter_values": np.random.rand(10, 2),
},
shape=(28, 10), # 28 randomizations and 10 sets of parameter values
)
# Initialize an Executor with the default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()
Ausgaben
Die Ausgabe des Executors ist ein QuantumProgramResult, das iterierbar ist. Es enthält einen Eintrag pro eingegebenem QuantumProgramItem in derselben Reihenfolge wie die Eingabeelemente. Jedes dieser Ausgabeelemente ist ein Dictionary, dessen Schlüssel Zeichenketten sind, die den Namen der klassischen Register in den Eingabe-Circuits entsprechen (unter anderem), sodass du dir diese Namen nicht mehr merken musst wie bei der Sampler-Ausgabe. Die Dictionary-Werte haben den Typ np.ndarray.
Das Ergebnis für das vorige Beispiel enthält diese Elemente:
CircuitItem-Ergebnis
Das erste Element enthält die Ergebnisse der Ausführung der ersten Aufgabe (eines CircuitItem) im Programm. Es enthält einen einzigen Schlüssel, meas, der dem Namen des klassischen Registers im Eingabe-Circuit entspricht. Der Wert dieses Schlüssels ist ein np.ndarray der Form (parameter sets, shots, register bits), also (10, 1024, 3) für das obige Beispiel.
Der folgende Code zeigt, wie du auf diese Informationen zugreifen kannst:
# Access the results of the classical register of task #0, a CircuitItem
result_0 = result[0]["meas"]
print(f"Result shape: {result_0.shape}")
Result shape: (10, 1024, 3)
SamplexItem-Ergebnis
Das zweite Element enthält die Ergebnisse der Ausführung der zweiten Aufgabe (eines SamplexItem) im Programm. Dieses Element enthält mehrere Schlüssel. Der Schlüssel meas, der dem Namen des klassischen Registers des Eingabe-Circuits entspricht, verweist auf das Ergebnis-Array dieses Registers. Dieses Array hat die Form (randomizations, parameter sets, shots, classical bits), also (28, 10, 1024, 3) in diesem Beispiel. Zusätzlich enthält die Ausgabe einen Schlüssel measurement_flips.meas, der die Bit-Flip-Korrekturen enthält, um das Measurement-Twirling für das meas-Register rückgängig zu machen. Diese Ausgabeform ist (28, 10, 1, 3) in unserem Beispiel, da für den Bit-Flip nur ein Shot benötigt wird.
# Access the results of the classical register of task #1
result_1 = result[1]["meas"]
print(f"Result shape: {result_1.shape}")
# Access the bit-flip corrections
flips_1 = result[1]["measurement_flips.meas"]
print(f"Bit-flip corrections shape: {flips_1.shape}")
# Undo the bit flips via classical XOR
unflipped_result_1 = result_1 ^ flips_1
Result shape: (28, 10, 1024, 3)
Bit-flip corrections shape: (28, 10, 1, 3)
Nächste Schritte
- Erkunde Beispiele, die Executor verwenden.
- Lerne mehr über das directed execution model.
- Verstehe das Executor Broadcasting.