Executor-Übertragung
Die an die Executor-Primitive übermittelten Daten können in verschiedenen Formen angeordnet werden, um durch Broadcasting Flexibilität bei der Arbeitslast zu gewährleisten. In dieser Anleitung wird erläutert, wie der Executor Array-Ein- und -Ausgaben mithilfe der Broadcasting-Semantik verarbeitet. Wenn Sie diese Konzepte verstehen, können Sie Parameterwerte effizient durchlaufen, mehrere Konfigurationen kombinieren und die Struktur der zurückgegebenen Daten interpretieren.
Die Beispiele in diesem Thema können nicht eigenständig ausgeführt werden. Es wird davon ausgegangen, dass Sie bereits geeignete Schaltkreise definiert, mit dem Samplomatic-Pass-Manager Boxen und Anmerkungen hinzugefügt und bei Bedarf die Samplomatic-Methode build verwendet haben, um für jeden Codeblock eine Schaltkreisvorlage und ein Samplex zu erhalten.
Schnellstart-Beispiel
Dieses Beispiel veranschaulicht den Kerngedanken. Es erstellt eine parametrische Schaltung und fünf verschiedene Parameterkonfigurationen. Der Ausführende führt alle fünf Konfigurationen aus und gibt die Daten nach Konfigurationen geordnet zurück, wobei jedes Quantenprogrammelement ein Ergebnis pro klassischem Register enthält.
Im weiteren Verlauf dieses Leitfadens wird auf dieses Beispiel zurückgegriffen, um zu erläutern, wie dies funktioniert und wie man komplexere Sweeps erstellt, einschließlich Samplomatic-basierter Randomisierung und Eingaben.
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"]Intrinsische und extrinsische Achsen
Die Übertragung gilt nur für die äußeren Achsen. Die Eigenachsen bleiben stets wie angegeben erhalten.
-
Intrinsische Achsen (ganz rechts): Werden durch den Datentyp bestimmt.
(3,)Wenn Ihr Schaltkreis beispielsweise drei Parameter hat, dann benötigen die Parameterwerte drei Zahlen, was eine intrinsische Form von ergibt. -
Externe Achsen (ganz links): Ihre Sweep-Abmessungen. Hiermit legen Sie fest, wie viele Konfigurationen Sie ausführen möchten.
Eingabetyp | Intrinsische Form | Beispiel für die vollständige Form |
|---|---|---|
| Parameterwerte (n Parameter) | (n,) | (5, 3) für fünf Konfigurationen und drei Parameter |
| Skalare Eingaben (z. B. Rauschskala) | () | (4,) für vier Konfigurationen |
| Beobachtbare Größen (falls zutreffend) | unterschiedlich | Hängt vom Typ der Beobachtungsgröße ab |
Beispiel
Betrachten Sie eine Schaltung mit zwei Parametern, die Sie über ein Raster von Konfigurationen mit der Option „ 4x3 “ durchlaufen lassen möchten, wobei sich die Parameterwerte und ein Rauschskalierungsfaktor ändern:
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,
},
)Die Formen sind wie folgt:
Eingabe | Vollständige Form | Äußere Form | Intrinsische Form |
|---|---|---|---|
parameter_values | (4, 1, 2) | (4, 1) | (2,) |
noise_scale | (3,) | (3,) | () |
| Übertragen | Keine | (4, 3) | Keine |
Formen von Ausgabematrizen
Ausgabe-Arrays folgen dem gleichen Muster für extrinsische und intrinsische Variablen:
- Externe Form: Entspricht der Broadcast-Form aller Eingaben
- Intrinsische Form: Wird durch den Ausgabetyp bestimmt
Die häufigste Ausgabe sind Bitstring-Daten aus Messungen, die als Array von Booleschen Werten formatiert sind:
Ausgabetyp | Intrinsische Form | Beschreibung |
|---|---|---|
| Daten aus dem klassischen Register | (Anzahl_Schüsse, Cre-Größe) | Bitstring-Daten aus Messungen |
Beispiel
(4, 3)``(3,)Wenn Sie Eingaben mit den extrinsischen Formen (4, 1) und bereitstellen, lautet die übertragene extrinsische
Form. Der folgende Code verwendet eine Schaltung mit 1024 Schüssen und einem 4-Bit-Register (wie im Quickstart -Beispiel definiert):
# 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)Bei jeder Konfiguration wird die im Quantenprogramm angegebene Gesamtzahl an Schüssen ausgeführt. Die Aufnahmen sind nicht nach Konfigurationen unterteilt. Wenn Sie beispielsweise 1024 Schüsse anfordern und über 10 Konfigurationen verfügen, führt jede Konfiguration 1024 Schüsse aus (insgesamt 10.240 ausgeführte Schüsse).
Randomisierung und der shape Parameter
Bei Verwendung eines Samplexes entspricht jedes Element der äußeren Struktur einer eigenständigen Schaltkreisauflösung. Der Samplex fügt in der Regel Zufälligkeiten (zum Beispiel „Gate Twirling“) in jede Ausführung ein, sodass jedes Element – auch ohne explizite Anforderung mehrerer Zufallsgenerierungen – eine zufällige Realisierung erhält.
Mit dem shape Parameter können Sie die extrinsische Form des Objekts erweitern, indem Sie
Achsen hinzufügen, die speziell der mehrmaligen Zufallsgenerierung derselben Konfiguration entsprechen. Es muss
aus der in Ihrer samplex_arguments. implizierten Form Achsen, bei denen shape den Wert
der impliziten Form überschreitet, führen zu zusätzlichen unabhängigen Zufallswertgenerierungen.
Keine expliziten Randomisierungsachsen
Wenn Sie dies weglassen shape (oder so einstellen, dass es Ihren Eingabeformen entspricht), erfolgt eine Ausführung pro
Eingabekonfiguration. Jede Ausführung wird zwar weiterhin durch den Samplex randomisiert, doch bei nur einer
einzigen Zufallsrealisierung profitieren Sie nicht von der Mittelwertbildung über mehrere Randomisierungen hinweg.
twirling=TrueWenn Sie es gewohnt sind, das Twirling mit einem einfachen Flag wie zu aktivieren, beachten Sie bitte, dass
der Executor verlangt, dass Sie mehrere Randomisierungen explizit mit dem shape Argument anfordern,
damit Ihre Nachbearbeitungsroutinen von den Vorteilen der Mittelwertbildung über mehrere
Randomisierungen profitieren können. Bei einer einfachen Randomisierung (der Standard, wenn shape weggelassen wird) werden
zufällige Gatter verwendet, was jedoch in der Regel keinen Vorteil gegenüber der Ausführung der Basisschaltung ohne
Randomisierung bietet.
Das folgende Beispiel veranschaulicht das Standardverhalten:
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)Einzelne Randomisierungsachse
Um mehrere Randomisierungen pro Konfiguration durchzuführen, erweitern Sie die Form um zusätzliche Achsen. Der folgende Code führt beispielsweise für jede der 10 Parameterkonfigurationen 20 Randomisierungen durch:
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)Mehrere Randomisierungsachsen
Sie können Randomisierungen in einem mehrdimensionalen Raster anordnen. Dies ist nützlich für die strukturierte Analyse, beispielsweise um Randomisierungen nach Typ zu trennen oder sie für die statistische Auswertung zu gruppieren.
(2, 14, 10)Hier wird die extrinsische Eingabeform (10,) auf die gewünschte Form übertragen,
wobei die Achsen 0 und 1 durch unabhängige Zufallswerte gefüllt werden.
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)Wie shape und Eingabeformen zusammenwirken
Der shape Parameter muss von Ihren externen Eingabeformen weitergeleitet werden können. Das bedeutet:
shapeEingabeformen mit den Abmessungen „ size-1 “ können sich entsprechend anpassen.shapeDie Eingabeformen müssen rechtsbündig ausgerichtet sein.- Achsen,
shapedie die Eingabemaße überschreiten, führen zu Zufallswertgenerierungen.
Beachten Sie, dass shape size-1 -Dimensionen enthalten kann,
die sich an die Eingabedimensionen anpassen, wie in der letzten Zeile der folgenden Tabelle dargestellt.
Beispiele:
Externe Eingabe | Form | Ergebnis |
|---|---|---|
| (10,) | (10,) | 10 Konfigurationen, jeweils 1 Randomisierung |
| (10,) | (5, 10) | 10 Konfigurationen mit jeweils 5 Randomisierungen |
| (10,) | (2, 3, 10) | 10 Konfigurationen, jeweils 2×3=6 Randomisierungen |
| (4, 1) | (4, 5) | 4 Konfigurationen mit jeweils 5 Randomisierungen |
| (4, 3) | (2, 4, 3) | 4 × 3 = 12 Konfigurationen, jeweils 2 Randomisierungen |
| (4, 3) | (2, 1, 3) | 4 × 3 = 12 Konfigurationen, jeweils 2 Randomisierungen (die 1 ergibt 4) |
Ergebnisse anzeigen
Mit Randomisierungsachsen können Sie auf bestimmte Kombinationen aus Randomisierung und Parametern zugreifen:
# 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))Häufige Muster
Einen einzelnen Parameter durchlaufen
Verwenden Sie einen Code wie den folgenden, um einen Parameter zu variieren, während die anderen konstant bleiben:
# 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)Erstellen eines „ 2D “-Rasterdurchlaufs
So erstellen Sie ein Raster anhand von drei Parametern:
# 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,)Mehrere Eingaben kombinieren
Wenn Sie Eingaben mit unterschiedlichen intrinsischen Formen kombinieren, richten Sie die extrinsischen Abmessungen anhand der Achsen „ size-1 “ aus:
# 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)Nächste Schritte
- Sehen Sie sich die Übersicht über die Sendungen an.
- Die Ein- und Ausgänge des Executors verstehen.