Dynamische Circuits ausführen
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
Dynamische Circuits sind leistungsstarke Werkzeuge, mit denen du Qubits mitten in der Ausführung eines Quantum Circuits messen und dann klassische Logikoperationen innerhalb des Circuits durchführen kannst, basierend auf dem Ergebnis dieser Zwischenmessungen. Dieser Prozess wird auch als klassisches Feedforward bezeichnet. Obwohl es noch frühe Tage sind, wenn es darum geht, das Beste aus dynamischen Circuits herauszuholen, hat die Quantenforschungsgemeinschaft bereits eine Reihe von Anwendungsfällen identifiziert, zum Beispiel:
- Effiziente Quantenzustandsvorbereitung, wie GHZ-Zustand, W-Zustand (weitere Informationen zum W-Zustand findest du auch unter "State preparation by shallow circuits using feed forward") und eine breite Klasse von Matrixproduktzuständen
- Effiziente Langstrecken-Verschränkung zwischen Qubits auf demselben Chip durch flache Circuits
- Effizientes Sampling von IQP-ähnlichen Circuits
Die Verbesserungen durch dynamische Circuits gehen jedoch mit Kompromissen einher. Zwischenmessungen und klassische Operationen haben in der Regel längere Ausführungszeiten als Zwei-Qubit-Gates, und dieser Zeitanstieg könnte die Vorteile der reduzierten Circuit-Tiefe zunichte machen. Daher ist die Verkürzung der Dauer von Zwischenmessungen ein Verbesserungsschwerpunkt, während IBM Quantum® die neue Version der dynamischen Circuits veröffentlicht. Weitere Einschränkungen bei der Verwendung dynamischer Circuits findest du in der Kompatibilitätstabelle für Estimator- oder Sampler-Features.
Die OpenQASM 3-Spezifikation definiert eine Reihe von Kontrollflussstrukturen, aber Qiskit Runtime unterstützt derzeit nur die bedingte if-Anweisung. In Qiskit SDK entspricht dies der Methode if_test auf QuantumCircuit. Diese Methode gibt einen Context-Manager zurück und wird typischerweise in einer with-Anweisung verwendet. Diese Anleitung beschreibt, wie du diese bedingte Anweisung verwendest.
Die Codebeispiele in dieser Anleitung verwenden die standardmäßige Messanweisung für Zwischenmessungen. Es wird jedoch empfohlen, stattdessen die Anweisung MidCircuitMeasure zu verwenden, wenn das Backend sie unterstützt. Weitere Details findest du im Abschnitt Zwischenmessungen.
Backends finden, die dynamische Circuits unterstützen
Um alle Backends zu finden, auf die dein Konto Zugriff hat und die dynamische Circuits unterstützen, führe Code wie den folgenden aus. Dieses Beispiel setzt voraus, dass du deine Anmeldedaten gespeichert hast. Du kannst auch Anmeldedaten explizit angeben, wenn du dein Qiskit Runtime Service-Konto initialisierst. Damit kannst du beispielsweise Backends anzeigen, die für eine bestimmte Instanz oder einen bestimmten Plan-Typ verfügbar sind.
- Die Backends, die dem Konto zur Verfügung stehen, hängen von der in den Anmeldedaten angegebenen Instanz ab.
- Die neue Version der dynamischen Circuits ist jetzt für alle Nutzer auf allen Backends verfügbar. Weitere Details findest du in der Ankündigung.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# This cell is hidden from users. It hides all those "...instance was not set..." warnings.
import warnings
warnings.filterwarnings("ignore", message=".*Instance was not set*")
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_kingston')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_boston')>]
Zwischenmessungen
Vor qiskit-ibm-runtime v0.43.0 war measure die einzige Messanweisung in Qiskit. Zwischenmessungen haben jedoch andere Abstimmungsanforderungen als terminale Messungen (Messungen, die am Ende eines Circuits stattfinden). Zum Beispiel musst du die Anweisungsdauer berücksichtigen, wenn du eine Zwischenmessung abstimmst, da längere Anweisungen lautere Circuits erzeugen. Für terminale Messungen musst du die Anweisungsdauer nicht berücksichtigen, da nach terminalen Messungen keine weiteren Anweisungen folgen.
Die Anweisung MidCircuitMeasure wird auf die Anweisung measure_2 abgebildet, die in den supported_instructions des Backends gemeldet wird. Allerdings wird measure_2 nicht auf allen Backends unterstützt. Verwende service.backends(filters=lambda b: "measure_2" in b.supported_instructions), um Backends zu finden, die es unterstützen. In Zukunft könnten neue Messungen hinzugefügt werden, aber das ist nicht garantiert.
MidCircuitMeasure-Methode
In qiskit-ibm-runtime v0.43.0 wurde die Anweisung MidCircuitMeasure eingeführt. Wie der Name schon sagt, handelt es sich um eine neue Messanweisung, die für Zwischenmessungen auf IBM® QPUs optimiert ist. Du kannst zwar QuantumCircuit.measure für eine Zwischenmessung verwenden, aber aufgrund ihrer Auslegung ist MidCircuitMeasure in der Regel die bessere Wahl. Sie fügt deinem Circuit zum Beispiel weniger Overhead hinzu als QuantumCircuit.measure.
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)
circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
- Es muss mindestens ein klassisches Register vorhanden sein, um Messungen zu verwenden.
- Das Sampler-Primitiv erfordert Circuit-Messungen. Du kannst Circuit-Messungen mit dem Estimator-Primitiv hinzufügen, sie werden jedoch ignoriert.
Store
Mit qiskit-ibm-runtime Version 0.47.0 oder höher kannst du die Anweisung store verwenden, um das Ergebnis eines klassischen Ausdrucks zu speichern, wenn dieser Ausdruck wiederholt verwendet wird. Operationen werden automatisch parallelisiert, was deinen Code zur Laufzeit deutlich effizienter macht.
Weitere Informationen findest du in der Anleitung Klassisches Feedforward und Kontrollfluss.
Wenn du store verwendest, um einen Wert in einem klassischen Register auf einem echten Backend zu speichern, wird dieser Wert nur während der Ausführung im Speicher gehalten und nicht kopiert oder im Job-Ergebnis zurückgegeben.
Im folgenden Code hat temp während der Ausführung denselben Wert wie creg, und der if_test funktioniert wie erwartet. Nach Abschluss des Jobs enthält das im Job-Ergebnis zurückgegebene temp BitArray jedoch nicht den Wert von creg. Das heißt, job.result()[0].data.temp ist 0.
creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...
Vollständiges Beispiel
Der folgende Code erstellt einen dynamischen Circuit und führt ihn auf IBM®-Hardware aus.
from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)
# Create a dynamic circuit
qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits
qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
qc.x(q0)
qc.measure(q0, c0)
# Convert to an ISA circuit for the given backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
# Generate samplers for backend targets
sampler = SamplerV2(backend)
# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()
print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
>>> Job ID: d88cakp789is7391vq0g (DONE)
Einschränkungen von Qiskit Runtime
Beachte die folgenden Einschränkungen beim Ausführen dynamischer Circuits in Qiskit Runtime.
-
Aufgrund des begrenzten physischen Speichers der Steuerelektronik gibt es auch ein Limit für die Anzahl der
if-Anweisungen und die Größe ihrer Operanden. Dieses Limit ist eine Funktion der Anzahl der Broadcasts und der Anzahl der gesendeten Bits in einem Job (nicht in einem Circuit).Beim Verarbeiten einer
if-Bedingung müssen Messdaten an die Steuerlogik übertragen werden, um die Auswertung vorzunehmen. Ein Broadcast ist eine Übertragung eindeutiger klassischer Daten, und gesendete Bits sind die Anzahl der übertragenen klassischen Bits. Betrachte das Folgende:c0 = ClassicalRegister(3)c1 = ClassicalRegister(5)...with circuit.if_test((c0, 1)) ...with circuit.if_test((c0, 3)) ...with circuit.if_test((c1[2], 1)) ...Im vorherigen Codebeispiel werden die ersten beiden
if_test-Objekte aufc0als ein Broadcast betrachtet, da sich der Inhalt vonc0nicht geändert hat und daher nicht erneut gesendet werden muss. Dasif_testaufc1ist ein zweiter Broadcast. Der erste sendet alle drei Bits inc0und der zweite sendet nur ein Bit, was insgesamt vier gesendete Bits ergibt.Wenn du derzeit 60 Bits auf einmal sendest, kann der Job etwa 300 Broadcasts haben. Wenn du jedoch nur ein Bit auf einmal sendest, kann der Job 2400 Broadcasts haben.
-
Der Operand in einer
if_test-Anweisung muss 32 oder weniger Bits umfassen. Wenn du also ein gesamtesClassicalRegistervergleichst, muss die Größe diesesClassicalRegister32 oder weniger Bits betragen. Wenn du jedoch nur ein einzelnes Bit aus einemClassicalRegistervergleichst, kann diesesClassicalRegisterbeliebig groß sein (da der Operand nur ein Bit ist).Zum Beispiel funktioniert der Codeblock „Nicht gültig" nicht, weil
crmehr als 32 Bits hat. Du kannst jedoch ein klassisches Register mit mehr als 32 Bits verwenden, wenn du nur ein Bit testest, wie im Codeblock „Gültig" gezeigt.- Nicht gültig
- Gültig
cr = ClassicalRegister(50)qr = QuantumRegister(50)circuit = QuantumCircuit(qr, cr)...circ.measure(qr, cr)with circ.if_test((cr, 15)):...cr = ClassicalRegister(50)qr = QuantumRegister(50)circuit = QuantumCircuit(qr, cr)...circ.measure(qr, cr)with circ.if_test((cr[5], 1)):... -
Verschachtelte Bedingungen sind nicht erlaubt. Zum Beispiel funktioniert der folgende Codeblock nicht, weil er ein
if_testinnerhalb eines anderenif_testenthält:- Nicht gültig
- Gültig
c1 = ClassicalRegister(1, "c1")c2 = ClassicalRegister(2, "c2")...with circ.if_test((c1, 1)):with circ.if_test(c2, 1)):...cr = ClassicalRegister(2)...with circuit.if_test((cr, 0b11)):... -
resetoder Messungen innerhalb von Bedingungen werden nicht unterstützt. -
Arithmetische Operationen werden nicht unterstützt.
-
Sieh dir die OpenQASM 3-Featuretabelle an, um zu ermitteln, welche OpenQASM 3-Features in Qiskit und Qiskit Runtime unterstützt werden.
-
Wenn OpenQASM 3 (anstelle von
QuantumCircuit) als Eingabeformat für die Übergabe von Circuits an Qiskit Runtime-Primitive verwendet wird, werden nur Anweisungen unterstützt, die in Qiskit geladen werden können. Klassische Operationen werden beispielsweise nicht unterstützt, da sie nicht in Qiskit geladen werden können. Weitere Informationen findest du unter Ein OpenQASM 3-Programm in Qiskit importieren. -
Die Anweisungen
for,whileundswitchwerden nicht unterstützt.
Dynamische Circuits mit Estimator verwenden
Da Estimator dynamische Circuits nicht unterstützt, kannst du Sampler verwenden und eigene Messprogramme erstellen.
Um das Verhalten des Estimators nachzuahmen, befolge diesen Prozess:
- Gruppiere die Terme aller Observablen in eine Partition. Dies kann beispielsweise mithilfe der
PauliList-API erfolgen.hinweisDu kannst das Primitiv-Attribut
BitArrayverwenden, um die Erwartungswerte der bereitgestellten Observablen zu berechnen. - Führe pro Partition einen Basiswechsel-Circuit aus (welchen Basiswechsel du für jede Partition durchführen musst). Weitere Informationen findest du im Messbasis-Addon-Hilfsprogramm
measurement_bases-Modul. Weitere Informationen findest du in der Dokumentation des Qiskit-Addon-Hilfsprogrammpakets. - Addiere die Ergebnisse für jede Partition wieder zusammen.
Einschränkungen
Überprüfe alle Feature-Kompatibilitätstabellen, um die Einschränkungen bei der Verwendung dynamischer Circuits zu verstehen. Beachte, dass die Feature-Kompatibilität nicht vom Primitiv abhängt.
Nächste Schritte
- Lerne, wie du genaues Dynamic Decoupling mithilfe von Stretch implementierst.
- Lies die Anleitung zu klassischem Feedforward und Kontrollfluss.
- Verwende die Circuit-Schedule-Visualisierung, um deine dynamischen Circuits zu debuggen und zu optimieren.
- Nicht alle Funktionen sind mit dynamischen Circuits kompatibel. Überprüfe den Abschnitt zur Feature-Kompatibilität für Sampler oder Executor für Details.