Von Sampler zu Executor migrieren
In diesem Leitfaden wird beschrieben, wie Sie Quantum-Sampling-Workloads von der „ IBM Quantum® “-Sampler- Primitive auf die „Executor“-Primitive verlagern können.
Die Primitive „Executor“ ist Teil des gerichteten Ausführungsmodells. Alle Komponenten des Modells zur gerichteten Ausführung befinden sich derzeit in der Beta-Phase und sind möglicherweise noch nicht stabil. Sie sind herzlich eingeladen, diese zu testen und Feedback zu geben, indem Sie ein Issue in den Repositories „Samplomatic“ oder „qiskit-ibm-runtime“ GitHub eröffnen.
Sollten Sie migrieren?
Nicht jeder sollte von Sampler auf Executor umsteigen. Es gibt viele Unterschiede zwischen den Primitiven, doch die folgenden Hinweise können Ihnen bei der Entscheidung helfen, ob eine Migration sinnvoll ist:
Wechseln Sie zu Executor, wenn Sie als Quanteninformationswissenschaftler Experimente im industriellen Maßstab durchführen und eine detaillierte, reproduzierbare Kontrolle über Techniken wie Pauli-Twirling, das Erlernen und Einbringen von Rauschmodellen sowie Basiswechsel benötigen – oder wenn Sie eine der zusätzlichen Funktionen von Executor benötigen.
Verwenden Sie Sampler weiterhin, wenn Sie eine einfache, übergreifende Schnittstelle wünschen und möchten, dass die Primitive die Fehlerunterdrückung und -minderung für Sie übernimmt.
Einschränkungen und Vorbehalte
Da sich Executor und das Modell der gesteuerten Ausführung noch in der Beta-Phase befinden, sollten Sie Folgendes beachten, bevor Sie sich für eine Migration entscheiden:
- Noch keine Simulatorunterstützung : Im Gegensatz zu Sampler, das über eine
AerSamplerImplementierung fürqiskit-aerdie lokale Simulation verfügt, gibt es derzeit kein Simulator-Backend für Executor. Simulator Die Unterstützung dafür wird voraussichtlich bald verfügbar sein. In der Zwischenzeit können Sie die Vorlagenschaltung weiterhin lokal prüfen und testen, um Ihren Arbeitsablauf zu validieren, bevor Sie ihn an die Hardware übermitteln. - Diese Anleitung bezieht sich ausschließlich auf den Sampler, nicht auf den Estimator. Die Migration von „Estimator“ zu „Executor“ ist wesentlich aufwendiger als die Migration von „Sampler“, da „Estimator“ Erwartungswerte berechnet, anstatt Rohstichproben zurückzugeben. Um das Verhalten von „Estimator“ mit „Executor“ nachzubilden, ist eine zusätzliche Nachbearbeitung erforderlich. Hilfsfunktionen für die Migration von „Estimator“ zu „Executor“ befinden sich noch in der Entwicklung, daher beschreibt diese Anleitung bewusst nur den „Sampler“-Workflow.
Wesentliche Unterschiede zwischen Executor und Sampler
Sowohl „Sampler“ als auch „Executor“ lesen die Ausgaberegister von Quantenschaltungen aus, richten sich jedoch an unterschiedliche Nutzer:
- „Sampler“ ist eine Abstraktion auf hoher Ebene. Es weist folgende Merkmale auf:
- Es verfügt über eine integrierte Fehlerunterdrückung (dynamische Entkopplung und Twirling).
- Es trifft implizit Entscheidungen für dich.
- Es ist so konzipiert, dass sich Algorithmusentwickler auf Innovationen statt auf Daten konzentrieren können. Umwandlung.
- „Executor“ ist Teil des Modells der gerichteten Ausführung. Es unterscheidet sich in vielerlei Hinsicht von Sampler und weist die folgenden Merkmale auf:
-
Es verfügt über keine integrierte Fehlerunterdrückung oder -minderung. Stattdessen halten Sie Ihre Entwurfsabsicht auf der Client-Seite fest (mithilfe von Schaltungsanmerkungen und einem Samplex ), und die rechenintensive Generierung von Schaltungsvarianten wird auf die Serverseite verlagert.
-
Es trifft keine impliziten Entscheidungen. Es hält sich genau an Ihre Anweisungen und bietet Ihnen volle Kontrolle und Transparenz.
-
Executor und Samplomatic bieten gemeinsam zusätzliche Funktionen, die Sampler nicht bietet, darunter (unter anderem) die folgenden:
- Weitere Twirling-Gruppen: Mit „Samplomatic“ können Sie für jedes Feld selbst auswählen, welche Twirling-Gruppe angewendet werden soll,
anstatt auf die einzige Strategie beschränkt zu sein, die „Sampler“ automatisch für Sie anwendet. Es unterstützt auch andere Twirling-Gruppen als die Pauli-Gruppe, wie beispielsweise die
"local_c1"Twirling- Gruppe. - Kernel- und klassifizierte Messungen zusammen: Die Einstellung
QuantumProgram.meas_level = "both"(hinzugefügt unter v0.48.0qiskit-ibm-runtime) sieht vor, dass sowohl klassifizierte als auch Kernel-Messungen in den Ergebnissen enthalten sind, anstatt pro Auftrag einen einzigen Messungstyp auszuwählen. - Twirling für Schaltungen mit fraktionalen Gatter: Executor kann Twirling auf Schaltungen anwenden, die fraktionale Gatter enthalten.
- Fein abgestimmte, kombinierbare Fehlerminderung: Beispielsweise die Auswahl der Schaltungsebenen, auf denen eine Fehlerminderung erfolgen soll, sowie die Anpassung der in die Schaltung eingespeisten Rauschraten.
Hinweise- Zukünftige neue Funktionen werden voraussichtlich zunächst für Executor veröffentlicht und möglicherweise nicht auf Sampler portiert. Wenn Sie Wert auf den Zugriff auf die neuesten Funktionen legen, ist Executor die zukunftssicherere Wahl.
- Das Qiskit-Basispaket bietet noch keine
Basisklasse für die Executor-Primitive (für andere Primitive ist dies jedoch der Fall
SamplerV2).
- Weitere Twirling-Gruppen: Mit „Samplomatic“ können Sie für jedes Feld selbst auswählen, welche Twirling-Gruppe angewendet werden soll,
anstatt auf die einzige Strategie beschränkt zu sein, die „Sampler“ automatisch für Sie anwendet. Es unterstützt auch andere Twirling-Gruppen als die Pauli-Gruppe, wie beispielsweise die
-
Konzeptionelle Kartierung
Die folgende Tabelle veranschaulicht, wie die Sampler-Konzepte dem Executor zugeordnet werden.
Konzept | Stichprobenfunktion | Steuerprogramm |
|---|---|---|
| Importieren | from qiskit_ibm_runtime import SamplerV2 | from qiskit_ibm_runtime import Executor |
| Eingabe | Liste der PUBs (Tupel) | Eine Anzahl von QuantumProgram Objekten QuantumProgramItem |
| Schaltung und Parameter | (circuit, params, shots) Tupel | program.append_circuit_item(circuit, circuit_arguments=...) |
| Wirbeln | TwirlingOptions | Deutlich erkennbar durch beschriftete Kästchen und ein Samplex (append_samplex_item) |
| Anruf ausführen | sampler.run([pub, ...]) | executor.run(program) |
| Ergebnistyp | PrimitiveResultvonSamplerPubResult | QuantumProgramResult (iterierbar) |
| Datenzugriff | result[0].data.<register> (BitArray) | result[0]["<register>"] (np.ndarray) |
| Lärm bewältigen | Integrierte Optionen | Muss manuell zusammengestellt werden (Anmerkungen, Samplex, NoiseLearnerV3) |
Übersicht über die Migrationsschritte
- Installieren Sie Samplomatic.
- Ändere die Importe.
- PUB -Tupel ersetzen.
- Ändere die Art und Weise, wie Schüsse dargestellt werden.
- Passen Sie gegebenenfalls weitere Optionen an.
- Aktualisieren Sie den Befehl
run. - Aktualisierung der Ergebnisauswertung.
- Das Drehen rückgängig machen.
Schritt 1. Installieren Sie die erforderlichen Pakete
Für „Executor“ und das Modell der gerichteten Ausführung ist das folgende Paket samplomatic erforderlich:
pip install qiskit qiskit-ibm-runtime samplomatic
# For visualization support:
# pip install samplomatic[vis]qiskit-ibm-runtimev0.48.0 wird empfohlen, da dadurch die Optionmeas_level = "both"und dielocal_c1Twirling-Gruppe hinzugefügt werden.qiskit >= 2.3.0ist erforderlich.samplomatic >= 0.18.0ist erforderlich.
Schritt 2. Ändere die Importe
Auszug:
from qiskit_ibm_runtime import SamplerV2 as SamplerTestamentsvollstrecker:
from qiskit_ibm_runtime import Executor, QuantumProgramSchritt 3. Ersetze die Tupel „ PUB “ durch ein QuantumProgram
Anstatt eine Liste von Tupeln (PUBs) zu übergeben, erstellt man bei der Verwendung von Executor ein und QuantumProgram fügt Elemente daran an.
A QuantumProgram Akzeptiert Schaltungselemente und Samplex- Elemente:
-
append_circuit_item: Fügt ein „CircuitItem,“ hinzu, das für eine Schaltung steht, sowie (optional) deren Parameterwerte. Es wird unverändert und ohne jegliche Zufallsauswahl ausgeführt.Verwenden Sie diese Option, wenn Sie lediglich eine Schaltung abtasten möchten – genau so, wie es „Sampler“ bei einem „ PUB “ ohne Twirling tun würde; beispielsweise beim Einreichen eines einfachen Abtastauftrags oder wenn Sie bereits manuell alle gewünschten Varianten hinzugefügt haben.
-
append_samplex_item: Fügt ein „samplexItem,“ hinzu, bei dem es sich um eine Vorlagenschaltung plus ein „Samplex“ handelt, das serverseitig zufällige Parametersätze generiert.Verwenden Sie diese Option, wenn Sie möchten, dass der Inhalt der Schaltung zufällig angeordnet wird. Der Hauptfall betrifft das Twirling (Gate oder Messung) oder die Rauscheinjektion. Diese Funktion ersetzt die in Sampler integrierte Twirling-Funktion.
Ein einzelnes QuantumProgram kann beide Elementtypen akzeptieren; jedes angehängte Element wird als
eigenständige Aufgabe ausgeführt und erzeugt einen eigenen Eintrag in den Ergebnissen. Verwenden Sie im Allgemeinen, wenn append_circuit_item Ihre Schaltung nicht randomisiert werden muss. Andernfalls verwenden Sie append_samplex_item.
In den folgenden Abschnitten werden nacheinander folgende Themen behandelt: parametrisierte Schaltungen, die … verwenden
append_circuit_item, sowie die Migration von Twirling unter Verwendung von append_samplex_item…
In den folgenden Code-Beispielen bezieht isa_circuit sich auf die Schaltung, die so transpiliert wurde, dass sie der Befehlssatzarchitektur (ISA) des Ziel-Backends entspricht. Dies enthält isa_circuit zwei Parameter.
Schritt 3a. Parametrisierte Schaltungen migrieren
Bei „Sampler“ sind die Parameterwerte das zweite Element des Tupels „ PUB “. Bei Executor
übergeben Sie diese als circuit_arguments an append_circuit_item.
Auszug:
params = np.random.rand(10, circuit.num_parameters) # 10 parameter sets
pubs = (isa_circuit, params)Steuerprogramm
program = QuantumProgram(shots=1024)
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, circuit.num_parameters), # 10 sets
)
# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]Schritt 3b. Integrierte Twirling-Funktionen in explizite Annotationen migrieren
Das ist die wichtigste Änderung. Sampler wendet mithilfe verschiedener Optionen den „Twirling“-Effekt für Sie an. Mit Executor bringst du diese Absicht explizit zum Ausdruck, indem du annotierte Felder und ein Samplex (von Samplomatic ) verwendest.
Beispiel (mit den Optionen drehen):
sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = TrueAusführender (Wirbel mit Kisten und einem Samplex):
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
enable_gates=True, # gate twirling
enable_measures=True, # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)
# 2. Build the (template circuit, samplex) pair.
# The template circuit's single-qubit gates are replaced by parameterized gates;
# the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)
# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(10, 2), # original circuit params
},
shape=(28, 10), # 28 randomizations x 10 parameter sets
)Da die Vorlagenschaltung und der Samplex auf der Client-Seite erstellt werden, können Sie diese lokal überprüfen und abtasten, um die Ausgabe zu verifizieren, bevor Sie Daten an die Hardware senden.
Überprüfung: Testen Sie die Vorlagenschaltung lokal
Sie können Zufallswerte aus dem Samplex ziehen und diese an die Schaltungsvorlage binden,
um zu überprüfen, ob der Samplex die von Ihnen erwarteten Parameterwerte liefert.
Die von zurückgegebenen Parameterwerte sind samplex.sample direkt kompatibel mit den
Parametern der Vorlagenschaltung.
# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())
# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
parameter_values=np.random.rand(2), # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)
# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)Um noch einen Schritt weiter zu gehen, kann man überprüfen, ob jede Randomisierung logisch äquivalent zur
ursprünglichen Schaltung ist, indem man beispielsweise beide in Operator Objekte umwandelt und ihre unitären Implementierungen vergleicht (nach
Berücksichtigung der Korrekturen outputs["measurement_flips.<register>"] , die das
„Measurement Twirling“ rückgängig machen) oder indem man die Erwartungswerte aus einem lokalen
StatevectorSampler bzw. Lauf StatevectorEstimator vergleicht. Eine vollständige Anleitung finden Sie im Handbuch zu
den Ein- und Ausgängen des
Samplomatic Samplex.
Schritt 4. Ändern Sie die Art und Weise, wie Aufnahmen angefordert werden
Bilder von der „ PUB “ nach QuantumProgram(shots=...)… verschieben. In Executor gilt shots dies für den gesamten Auftrag. Reichen Sie mehrere Aufträge ein, wenn Sie unterschiedliche Bildanzahlen benötigen.
Auszug:
# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])Testamentsvollstrecker:
# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)Schritt 5. Aktualisieren Sie die Optionen nach Bedarf
Executor bietet weniger Optionen als Sampler, da die Einstellungen zur Fehlerminderung nun in Ihren Annotationen und in „samplex“ statt in den Optionen zu finden sind.
Außerdem gibt es einen strukturellen Unterschied hinsichtlich der Speicherorte der Einstellungen.
-
Bei Sampler wird alles – einschließlich der Einstellungen, die sich auf die Nachbearbeitung der Ergebnisse auswirken – über die Optionen der Primitive oder im „ PUB “ konfiguriert.
-
Bei Executor werden die Einstellungen, die Einfluss darauf haben, wie die Auftragsergebnisse gestaltet und nachbearbeitet werden, auf der Seite festgelegt
QuantumProgram, nicht aufExecutorOptions.
Beispiele:
Stichprobenfunktion | Steuerprogramm |
|---|---|
shots | QuantumProgram(shots=...) |
meas_type | QuantumProgram(meas_level=...) |
ExecutorOptions enthält lediglich Ausführungs- und Umgebungseinstellungen auf niedrigerer Ebene,
die die Struktur der zurückgegebenen Daten nicht verändern. Es umfasst drei Gruppen auf oberster Ebene:
environment(EnvironmentOptions)execution(ExecutionOptions): Bietet weniger Optionen als der Sampler. Beispielsweise gibt es keine Option „Executormeas_type“.experimental
Insbesondere gibt es die Optionen dynamical_decoupling und twirling im Sampler, nicht jedoch im Executor. Stattdessen werden diese Optionswerte über das gerichtete Ausführungsmodell ausgedrückt.
Beispiel:
from qiskit_ibm_runtime import Executor, ExecutorOptions
options = ExecutorOptions(
environment={"log_level": "INFO"},
execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True
executor = Executor(mode=backend, options=options)Schritt 6. Den Befehl run aktualisieren
Als Eingabe für einen Executor-Job dient das Programm anstelle von PUBs.
Auszug:
# Submit a job
sampler.run([(isa_circuit, parameter_values)])Testamentsvollstrecker:
# Submit a job
executor.run(program)Schritt 7. Ändern Sie die Art und Weise, wie Sie auf Ergebnisse zugreifen
In Executor sind die Ergebnisse „ NumPy “-Arrays und keine BitArray Objekte. Verwende die Zeichenkette als Index (result[0]["meas"]) und erhalte ein zurück np.ndarray . Man muss sich den Pfad des Attributs .data.<register> nicht merken.
Um von „Sampler“ auf „Executor“ umzustellen, ändern Sie result[i].data.<reg> (BitArray) in result[i]["<reg>"] (np.ndarray)und schreiben Sie die get_counts-basierte Nachbearbeitung als „ NumPy “-Operationen um.
Aufgabe | Stichprobenfunktion | Steuerprogramm |
|---|---|---|
| Registerdaten abrufen | result[0].data.meas | result[0]["meas"] |
| Datentyp | BitArray | np.ndarray |
| Zählwörterbuch | result[0].data.meas.get_counts() | Das Array manuell nachbearbeiten |
| Mehrere Register | result[0].data.<name> pro Register | result[0]["<name>"] pro Register |
| CircuitItem Array-Form | - | (parameter_sets, shots, register_bits) |
| SamplexItem Array-Form | - | (randomizations, parameter_sets, shots, register_bits) |
| Messung rückgängig machen | Automatisch | result[i]["measurement_flips.<name>"] + XOR |
Sampler's BitArray bietet Hilfsmittel (get_counts, slice_bits, slice_shots, expectation_values, sowie Masken zur Nachauswahl) an. Der Executor gibt „ NumPy “-Arrays im Rohformat zurück, sodass Sie diese Nachbearbeitung mit Standardoperationen von „ NumPy “ durchführen können.
Schritt 8. Behandlung von durch Drehung verursachten Ergebnissen (Korrekturen bei Bit-Flips)
Wenn Sie eine Messung mittels „Twirling“ durch ein anwenden SamplexItem, gibt „Executor“ die Rohwerte
(die durch „Twirling“ bearbeiteten Messwerte) sowie die Bit-Flip-Korrekturen zurück, die erforderlich sind, um das „Twirling“ rückgängig zu machen.
Sie müssen sie manuell anwenden; es erfolgt keine automatische Korrektur.
Wenn Sie Executor verwenden, heben Sie das Twirling explizit mithilfe der Korrekturen measurement_flips.<reg> und einer XOR-Operation auf, wie im folgenden Beispiel gezeigt:
# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"] # example: (28, 10, 1024, 2)
# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"] # example: (28, 10, 1, 2)
# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1In Sampler gibt es keinen entsprechenden Schritt, da dort das Ausblenden automatisch rückgängig gemacht wird.
Vollständiges Beispiel: Migration eines einfachen Abtastauftrags
Stichprobenfunktion
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()
# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()
# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()Steuerprogramm
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()
# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)
# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()
# 6. Access results: a plain np.ndarray keyed by register name
# shape = (shots, register_bits)
meas = result[0]["meas"]