Skip to main content
IBM Quantum Platform

Transpiler

Die Funktion generate_boxing_pass_manager() ist ein flexibles und praktisches Werkzeug zum Erstellen eines, mit dem qiskit.transpiler.PassManager sich die Schaltkreisanweisungen in kommentierte Kästchen gruppieren lassen. Dies bietet eine einfache Alternative zum manuellen Gruppieren und Beschriften von Schaltkreisen und eignet sich zur Automatisierung eines Arbeitsablaufs, zum Testen verschiedener Strategien zur Schaltkreisgruppierung und so weiter.

In dieser Anleitung wird die Verwendung von und generate_boxing_pass_manager() seiner Argumente erläutert. Um die Auswirkungen der einzelnen Argumente der Funktion zu verdeutlichen, beziehen sich die folgenden Abschnitte hauptsächlich auf die unten abgebildete Schaltung.

from qiskit.circuit import Parameter, QuantumCircuit

circuit = QuantumCircuit(4, 7)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(1)
circuit.cx(1, 0)
circuit.cx(2, 3)
circuit.measure(range(1, 4), range(3))
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
for qubit in range(4):
    circuit.rz(Parameter(f"th_{qubit}"), qubit)
    circuit.rx(Parameter(f"phi_{qubit}"), qubit)
    circuit.rz(Parameter(f"lam_{qubit}"), qubit)
circuit.measure(range(4), range(3, 7))

circuit.draw("mpl", scale=0.8)
../_images/cf27e1ef03e51b2360c90165a4efeaa793da36083bb674774ca06c8181ef63dd.png

Vorgänge in Gruppen zusammenfassen

Das Argument enable_gates kann auf oder True gesetzt werden, False um festzulegen, ob die Zwei-Qubit-Gatter in Kästchen gruppiert werden sollen. Ebenso enable_measures legt fest, ob Messungen gruppiert werden sollen oder nicht. Der folgende Ausschnitt zeigt ein Beispiel, bei dem sowohl Gatter als auch Messungen gruppiert sind.

from samplomatic.transpiler import generate_boxing_pass_manager

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/b323dde8a5412e42359aef1831af92ed3702d2251547c663208dc084a2995276.png

Wie aus der Abbildung ersichtlich ist, erstellt der Pass-Manager zwei Arten von Blöcken: solche, die eine einzelne Schicht aus Zwei-Qubit-Gattern enthalten, und solche, die eine einzelne Schicht aus Messungen enthalten. Diese Trennung entspricht den gängigen Vorgehensweisen bei Protokollen zum Rauschlernen und zur Rauschminderung, die in der Regel auf Schichten mit homogenen Operationen abzielen. Die Zwei-Qubit-Gatter und -Messungen werden in das Feld ganz links platziert, in das sie passen, und jedes Einzel-Qubit-Gatter wird in dasselbe Feld platziert wie das Zwei-Qubit-Gatter oder die Messung, der es vorausgeht.

Der folgende Code zeigt ein Beispiel, in dem auf gesetzt enable_gates wird False. Wie man sieht, sind die Zwei-Qubit-Gatter nicht in Kästchen gruppiert, ebenso wenig wie die ihnen vorausgehenden Ein-Qubit-Gatter.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=False,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/3fc8e660b0c7eff1f5aa2e571d064f91b3eb2738c5990727e026a8ffe1101a1c.png

Wählen Sie aus, wie die Kästchen beschriftet werden sollen

Alle Zwei-Qubit-Gatter und Messboxen in der zurückgegebenen Schaltung sind mit „left-dressed“-Annotationen versehen. Insbesondere sind alle Kästchen, die Zwei-Qubit-Gatter enthalten, mit einem Twirl. gekennzeichnet. Bei Messfeldern können Benutzer zwischen Twirl, BasisTranform (wobei auf mode gesetzt ist "measure") oder beiden Optionen wählen. Der folgende Code erzeugt eine Schaltung, in der alle Kästchen gedreht sind und die Messkästchen zusätzlich mit beschriftet sind BasisTranform.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
    measure_annotations="all",
)
transpiled_circuit = boxing_pass_manager.run(circuit)

Bereiten Sie die Schaltung für die Rauscheinjektion vor

Damit inject_noise_targets können Sie festlegen, welche Felder eine Anmerkung InjectNoise erhalten sollen. Der folgende Code erzeugt beispielsweise einen Schaltkreis, in dem die 2-Qubit-Gatter-Felder eine Anmerkung InjectNoise enthalten, die Messfelder jedoch nicht.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
    inject_noise_targets="gates",
)
transpiled_circuit = boxing_pass_manager.run(circuit)

Enthält eine Schaltung zwei oder mehr Blöcke, die bis auf Einzel-Qubit-Gatter äquivalent sind, werden alle mit einer InjectNoise Anmerkung versehen, die denselben Wert aufweist ref. Somit entspricht die Anzahl der eindeutigen ref„s“ in der zurückgegebenen Schaltung der Anzahl der eindeutigen Kästchen.

Der Wert für wirkt inject_noise_strategy sich auf die Anmerkungen InjectNoise aus. Die folgenden Werte sind gültig:

  • modifier_ref='': Empfohlen, wenn Sie die Rauschkarten vor der Abtastung nicht ändern müssen.
  • modifier_ref=ref: Empfohlen, wenn alle Noise-Maps einheitlich um denselben Faktor skaliert werden sollen.
  • Ein eindeutiger Wert von modifier_ref: Empfohlen, wenn jede Rauschkarte mit einem anderen Faktor skaliert werden muss.

Der folgende Code erzeugt einen Schaltkreis, in dem die Kästchen der Zwei-Qubit-Gatter mit Anmerkungen InjectNoise versehen sind, die eindeutige Werte modifier_ref enthalten.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
    inject_noise_targets="gates",
    inject_noise_strategy="individual_modification",
)
transpiled_circuit = boxing_pass_manager.run(circuit)

Wähle eine Drehstrategie aus

Der Boxing-Pass-Manager beginnt damit, auszuwählen, welche Verflechtungsgatter und Messanweisungen in denselben Feldern erscheinen sollen. Sobald dies erreicht ist, werden die Felder in der Regel nicht mehr die volle Breite einnehmen – sie werden wahrscheinlich nur noch eine Teilmenge der Qubits enthalten, die in der Schaltung aktiv sind oder im Backend-Ziel vorhanden sind. In typischen Konfigurationen des „Boxing-Pass-Managers“ werden alle Qubits, auf die eine Box-Anweisung wirkt, durch Twirling beeinflusst (auch diejenigen, auf die keine Anweisung innerhalb der Box wirkt); daher wirken sich Boxen mit teilweiser Breite auf die rauschoptimierenden Effekte des Twirling aus. Die im Leerlauf befindlichen Qubits werden nicht gedreht und können beispielsweise kohärente Fehler aufbauen, während die Box angelegt wird. Die bietet twirling_strategy Möglichkeiten, die Anzahl der zu jeder Box gehörenden Qubits zu erweitern und dadurch die Anzahl der inaktivierten Qubits zu verändern:

  • "active" (Standard) erweitert die Boxen nicht auf inaktivierte Qubits.
  • "active_accum" erweitert alle Felder auf jene Qubits, auf die bereits durch einen vorherigen Befehl in der Schaltung eingewirkt wurde. Dies ist nützlich, wenn Sie die Qubits zu Beginn des Schaltkreises so lange wie möglich in ihrem Grundzustand belassen möchten, indem Sie sie erst dann in Rotation versetzen, wenn sie aktiv werden.
  • "active_circuit" erweitert alle Kästchen auf jene Qubits, auf die ein beliebiger Befehl im Schaltkreis einwirkt. Dies ist nützlich, wenn Sie eine strukturierte Schaltung mit sich wiederholenden Verschränkungsschichten haben und die Anzahl der einzelnen Boxen minimieren möchten, da für jede einzelne das Rauschen erlernt werden muss.
  • "all" erweitert alle Kästchen auf alle Qubits in allen Quantenregistern der Schaltung. Dies ist als Erweiterung nützlich, wenn "active_circuit" man möchte, dass alle Qubits in der QPU stets „twirled“ sind.

Im folgenden Beispiel wird jede der Strategien auf die Basisschaltung angewendet, anschließend werden die Ergebnisse dargestellt.

circuit = QuantumCircuit(5, 2)
circuit.h(range(4))
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.barrier()
circuit.h(range(4))
circuit.measure([1, 2], [0, 1])

circuit.draw("mpl", scale=0.6)
../_images/cdf089891dabe1650f1303c8c747aa8cf03cdde4dfc42c772fe2264bd7a49a1b.png
import matplotlib.pyplot as plt

plt.figure(figsize=(9, 6))
for idx, strategy in enumerate(["active", "active_accum", "active_circuit", "all"]):
    ax = plt.subplot(2, 2, idx + 1)
    pm = generate_boxing_pass_manager(twirling_strategy=strategy, remove_barriers="never")
    transpiled_circuit = pm.run(circuit)
    fig = transpiled_circuit.draw("mpl", scale=0.8, ax=ax)
    ax.set_title(f"twirling_strategy='{strategy}'")

plt.tight_layout()
../_images/b810ecc4c65822cf60b3d652792a4089c5f931ad1e8320eccdd89bc50a08ba6d.png

Legen Sie fest, wie mit Hindernissen umgegangen werden soll

Standardmäßig werden Barrieren aus dem Schaltkreis entfernt, nachdem alle Verschränkungsgatter und Messanweisungen wie angegeben in die Box aufgenommen wurden – einschließlich ihrer Erweiterungen aufgrund der Auswahl twirling_strategy –, jedoch bevor Einzelqubit-Gatter, sofern möglich, in die Box unmittelbar rechts davon eingefügt werden.

Die folgende Schaltung enthält beispielsweise eine Barriere:

circuit_with_barrier = QuantumCircuit(4)
circuit_with_barrier.h(range(4))
circuit_with_barrier.cz(0, 1)
circuit_with_barrier.barrier()
circuit_with_barrier.cz(2, 3)
circuit_with_barrier.measure_all()

circuit_with_barrier.draw("mpl", scale=0.8)
../_images/f2343807597155ab5301205803954b95d29ba0d7370440bd861439fd83d1a193.png

Das Standardverhalten verhindert, dass die beiden Verschränkungsgatter in derselben Box landen, lässt jedoch zu, dass die Einzel-Qubit-Gatter trotz der Barriere in die nächste verfügbare Box aufgenommen werden.

boxing_pass_manager = generate_boxing_pass_manager()
transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/7e0812ac6b3f2405c765338ab25c2e9179165a7257ef1d996bea99f94cb39ca6.png

Durch die Einstellung auf remove_barriers werden die Barrieren 'never' beibehalten, sodass Einzel-Qubit-Gatter eingeschränkt werden.

boxing_pass_manager = generate_boxing_pass_manager(remove_barriers="never")

transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/6b57e625509dd8ca55fbd96994398b03ac2fe6f76f21535db9bd042d4c68c987.png

Wenn schließlich die Barrieren sofort beseitigt werden, sind die Verwickelten nicht mehr dazu gezwungen, in getrennten Kisten zu landen.

boxing_pass_manager = generate_boxing_pass_manager(remove_barriers="immediately")

transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/9a44c59a05a44a3e8f0ce9b7edd3ccc2896c6a68fb00ed110a7e225f52a7ca73.png

So funktioniert die Schichtung

Der Boxing-Pass-Manager führt nacheinander zwei Schlüssel-Passes aus:

  1. GroupGatesIntoBoxes — Unterteilt verschachtelte Gatter in Blöcke. Einzel-Qubit-Gatter werden in dieser Phase außer Acht gelassen; es werden ausschließlich Zwei-Qubit-Gatter gruppiert.
  2. AbsorbSingleQubitGates — Ordnet jedes Einzel-Qubit-Gatter dem nächstgelegenen geeigneten Feld rechts davon zu.

Bei der Stratifizierung durchläuft GroupGatesIntoBoxes das Programm den DAG-Zyklus in topologischer Reihenfolge und ordnet jedes Zwei-Qubit-Gatter der frühesten Gruppe zu, deren Qubits noch nicht belegt sind. Drei Arten von Befehlen dienen als Trennzeichen, die die aktuelle Gruppe für ihre Qubits leeren und dadurch bewirken, dass nachfolgende Gatter auf diesen Qubits einer neuen Gruppe zugeordnet werden:

  • Hindernisse
  • Vorhandene Verpackungsanleitung
  • Messungen

Einzel-Qubit-Gatter sind keine Trennzeichen – sie haben keinen Einfluss auf die Gruppierung und werden AbsorbSingleQubitGates der überlassen. Derzeit wird keine Kommutativitätsanalyse durchgeführt; die Gruppierung berücksichtigt die topologische Reihenfolge des Schaltkreises unverändert.

Warum ist remove_barriers="after_stratification" das die empfohlene Standardeinstellung?

Der Standardmodus ist, "after_stratification" da er zwei Vorteile vereint:

  • Barrieren schränken die Gruppierung von Verschränkungsgattern ein (sie fungieren bei der Schichtung als Begrenzer).
  • Vor den Durchläufen AbsorbSingleQubitGates werden die Barrieren entfernt, sodass Einzel-Qubit-Gatter weiterhin über die ehemaligen Barrierepositionen hinwegfließen und von der nächstgelegenen Box aufgenommen werden können.

Im Gegensatz dazu:

  • "never" behält durchgehend Barrieren bei, wodurch Einzel-Qubit-Gatter zwischen einer Barriere und einer Box eingeschlossen werden, was deren Absorption verhindert und unverdreht bleibende Rotationen im Schaltkreis hinterlässt.
  • "immediately" beseitigt Hindernisse vor der Schichtung, sodass diese keinen Einfluss auf die Gruppeneinteilung haben.
  • "finally" Dies ist nützlich, wenn Sie Barrieren im Ausgangskreis wünschen (beispielsweise zur Visualisierung), wobei Einzel-Qubit-Gatter zwischen einer Barriere und einer Box jedoch nicht absorbiert werden.

Box-ISA-Schaltungen mit Barrieren

Schaltkreise mit einer sich wiederholenden Struktur – wie beispielsweise Simulationen mit dem Trotterisierten Hamilton-Operator, Variationsansätze und Fehlerverstärkungssequenzen – werden in der Regel programmgesteuert erstellt und anschließend in die ISA-Form transpiliert. Das manuelle Hinzufügen von Blöcken zu einer transpilierten Schaltung ist mühsam, daher sieht der natürliche Arbeitsablauf wie folgt aus:

  1. Bauen Sie die logische Schaltung auf und fügen Sie dabei Barrieren an den Schichtgrenzen ein, die zu Kastengrenzen werden sollen.
  2. In ISA-Schaltungen transpilieren. Der Transpiler von Qiskit bewahrt Barrieren durch Routing und Optimierung.
  3. Fahre generate_boxing_pass_manager() auf der ISA-Rennstrecke.

Barrieren sind erforderlich, wenn das Ende einer Schicht nicht alle Qubits abdeckt. Andernfalls würde der Greedy-Algorithmus Gatter aus der nächsten Schicht – auf diesen „freien“ Qubits – derselben Gruppe zuordnen. Betrachten wir als konkretes Beispiel eine zweistufige Heisenberg-ZZ-Simulation auf einem Ring aus 5 Qubits. Jeder Trotter-Schritt besteht aus drei Bindungsschichten: geraden Bindungen (0,1), (2,3), ungeraden (1,2), (3,4) Bindungen und der Wickelbindung (4,0). Nach der ungeraden Bindungsschicht sind die Qubits 0 und 4 beide frei. Ohne eine Barriere (4,0) wird die Wrap-Bindung mit Bindungen aus dem nächsten Trotter-Schritt, die diese freien Qubits nutzen, „gierig gruppiert“, wodurch Gatter aus verschiedenen Schritten in derselben Box vermischt werden.

from qiskit.circuit import Parameter, QuantumCircuit

n = 5  # 5-qubit ring: bonds (0,1), (1,2), (2,3), (3,4), (4,0)
dt = Parameter("dt")
heisenberg = QuantumCircuit(n)

for step in range(2):
    # Layer 1: even bonds (0,1) and (2,3)
    for idx in range(0, n - 1, 2):
        heisenberg.rzz(dt, idx, idx + 1)
    heisenberg.barrier()
    # Layer 2: odd bonds (1,2) and (3,4) -- qubits 0 and 4 are now free
    for idx in range(1, n - 1, 2):
        heisenberg.rzz(dt, idx, idx + 1)
    heisenberg.barrier()
    # Layer 3: wrap bond (4,0) -- both qubits were free after layer 2
    heisenberg.rzz(dt, n - 1, 0)
    if step < 1:
        heisenberg.barrier()  # separates the two Trotter steps

heisenberg.measure_all()

heisenberg.draw("mpl", scale=0.7)
../_images/172fa55c9f02964fef32fc045a50b86971126be728602b3f1e4b2437df2580b7.png

Der folgende Code transpiliert die Schaltung in die ISA-Form, wobei davon ausgegangen wird, dass das zugrunde liegende Gerät CX als nativen Entangler verwendet. Beachten Sie, dass die Barrieren die Transpilierung überstehen und im Ausgabekreis sichtbar bleiben.

from qiskit.transpiler import generate_preset_pass_manager

preset_pm = generate_preset_pass_manager(
    basis_gates=["rz", "sx", "cx"],
    coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]],
    optimization_level=1,
)
isa_circuit = preset_pm.run(heisenberg)
isa_circuit.draw("mpl", scale=0.5, fold=200)
../_images/e9a3fbd6ab1a91051c5cd0273d92ecebc6ae8ebbc370c30bc55daee1fd28bab7.png

Wenden Sie anschließend den Boxing-Pass-Manager auf die ISA-Schaltung an. Jede der drei Bindungsschichten wird in jedem Trotter-Schritt zu einer eigenen Box.

boxing_pm = generate_boxing_pass_manager()
boxed_isa = boxing_pm.run(isa_circuit)
boxed_isa.draw("mpl", scale=0.5, fold=100)
../_images/b59e3c047264b3340d9af0f1ca74ad9f7d4c1705d4a5176e441346db9b678213.png

Den Pass-Manager von Samplomatic in die voreingestellten Pass-Manager von Qiskit integrieren

Der folgende Code zeigt, wie man generate_boxing_pass_manager() in einen Qiskit-Preset-Pass-Manager integriert. Die transpilierte Schaltung ist eine ISA und enthält Blöcke.

from qiskit.transpiler import generate_preset_pass_manager

preset_pass_manager = generate_preset_pass_manager(
    basis_gates=["rz", "sx", "cx"],
    coupling_map=[[0, 1], [1, 2]],
    optimization_level=0,
)
boxing_pass_manager = generate_boxing_pass_manager()

# Run the boxing pass manager after the scheduling stage
preset_pass_manager.post_scheduling = boxing_pass_manager

circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(0, 2)
circuit.measure_all()

transpiled_circuit = preset_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/fe943b034588a5354a87abcf51b8d805c36f4f2b30a73a39c5886bce9e156b04.png

Dieses Muster lässt sich zudem nahtlos in den auf Barrieren basierenden ISA-Boxing-Workflow integrieren. Der Boxing-Pass-Manager wird als Schritt nach der Planung ausgeführt, nachdem die Barrieren aus dem ursprünglichen logischen Schaltkreis die Transpilierung überstanden haben, sodass sie die Schichtung wie erwartet steuern.

preset_pm = generate_preset_pass_manager(
    basis_gates=["rz", "sx", "cx"],
    coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]],
    optimization_level=1,
)
preset_pm.post_scheduling = generate_boxing_pass_manager()

boxed_heisenberg = preset_pm.run(heisenberg)
boxed_heisenberg.draw("mpl", scale=0.5, fold=100)
../_images/4905bf1df63cc83fa581ed006a93beb709be504a4f59fc7f663f4efde62da175.png

Anmerkungen prüfen und bearbeiten

Nach dem automatischen Einrahmen möchten Sie vielleicht die Beschriftungen bestimmter Rahmen überprüfen oder anpassen. Beispielsweise, um Rauschen einzuspielen, Anmerkungen zu entfernen oder eine sich drehende Gruppe zu ändern. Samplomatic stellt zu samplomatic.utils diesem Zweck eine Reihe von Hilfsfunktionen zur Verfügung. Sie alle geben einen neuen Schaltkreis zurück, ohne den ursprünglichen zu verändern.

extend_annotations() fügt jeder Box im Schaltkreis eine oder mehrere Anmerkungen hinzu.

from samplomatic import InjectNoise, Twirl
from samplomatic.utils import extend_annotations

boxed = generate_boxing_pass_manager().run(circuit_with_barrier)

# Add InjectNoise to every box
with_noise = extend_annotations(boxed, InjectNoise("my_noise_ref"))
with_noise.draw("mpl", scale=0.8)
../_images/7e0812ac6b3f2405c765338ab25c2e9179165a7257ef1d996bea99f94cb39ca6.png

filter_annotations() behält nur Anmerkungen bestimmter Typen bei und entfernt den Rest. replace_annotations() wendet eine aufrufbare Funktion auf jede Anmerkung an; die aufrufbare Funktion gibt eine Liste von Anmerkungen zurück, die an ihrer Stelle eingesetzt werden sollen (den Wert „return“, um [] sie zu entfernen, eine Liste mit einem Element, um sie zu ersetzen, oder eine Liste mit mehreren Elementen, um sie zu erweitern).

from samplomatic.utils import filter_annotations, replace_annotations

# Keep only Twirl annotations, drop everything else
twirl_only = filter_annotations(with_noise, Twirl)

# Replace the twirling group on every Twirl annotation
updated = replace_annotations(
    twirl_only, lambda a: [Twirl(group="local_c1")] if isinstance(a, Twirl) else [a]
)

Die Allzweckfunktion map_annotations() akzeptiert jedes aufrufbare Objekt, das eine Liste von Anmerkungen auf eine neue Liste abbildet, wodurch Sie die volle Kontrolle über die Transformationen für jedes einzelne Feld haben. strip_annotations() ist eine praktische Hülle, die alle Anmerkungen aus jeder Box entfernt.

Für Massenänderungen wie das Hinzufügen von InjectNoise zu allen Gate-Boxen ist generate_boxing_pass_manager() der Parameter inject_noise_targets von die prägnanteste Option. Diese Hilfsfunktionen sind nützlich für selektive Bearbeitungen auf Box-Ebene, die durch die Parameter des Pass-Managers nicht abgedeckt werden.


Eine Schaltung aufbauen

Jeder von erzeugte Pass-Manager liefert generate_boxing_pass_manager() Schaltkreise, die von erfolgreich in ein Template/Samplex-Paar umgewandelt werden können build(). Der folgende Code ruft beispielsweise die Funktion build() für einen Schaltkreis auf, der von einem Boxing-Pass-Manager erzeugt wurde.

from samplomatic import build

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)

template, samplex = build(transpiled_circuit)

Um sicherzustellen, dass jede transpilierte Schaltung erfolgreich erstellt werden kann, wissen die Pass-Manager, wie sie bei Bedarf zusätzliche Boxen einbinden können. Betrachten wir zum Beispiel die folgende Schaltung, die mit einem ungemessenen Qubit endet 0.

circuit_with_unmeasured_qubit = QuantumCircuit(4, 3)
circuit_with_unmeasured_qubit.cz(0, 1)
circuit_with_unmeasured_qubit.cz(2, 3)
for qubit in range(4):
    circuit_with_unmeasured_qubit.rz(Parameter(f"th_{qubit}"), qubit)
    circuit_with_unmeasured_qubit.rx(Parameter(f"phi_{qubit}"), qubit)
    circuit_with_unmeasured_qubit.rz(Parameter(f"lam_{qubit}"), qubit)
circuit_with_unmeasured_qubit.measure(range(1, 4), range(3))

circuit_with_unmeasured_qubit.draw("mpl", scale=0.8)
../_images/0452779799470d2deb4d32ce19f5aa28dfe8055a064e67c0e8e5684389845b0f.png

Würde man linksausgerichtete Kästchen um die Gatter und die Messungen zeichnen, ergäbe dies eine Schaltung mit nicht abgeholten virtuellen Gattern auf dem Qubit 0, und der Aufruf dieser build() Schaltung würde zu einem Fehler führen. Um dies zu vermeiden, generate_boxing_pass_manager() können die von zurückgegebenen Pass-Manager rechtsbündige Boxen hinzufügen, die als Sammler fungieren. Im folgenden Code 0 wird beispielsweise das Qubit durch einen Kasten mit rechter Ausrichtung abgeschlossen, der das nicht gesammelte virtuelle Gatter aufnimmt. Die auf das Qubit wirkenden Einzel-Qubit-Gatter 0 werden ebenfalls innerhalb des Kastens platziert, um die Tiefe der resultierenden Schaltung zu minimieren.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit_with_unmeasured_qubit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/711ee3c014edf6f70ede5dfa849494286e96870b3bd70a2a89f687d83cc5bfc1.png

In einem weiteren Beispiel wird eine „right-dressed“-Box hinzugefügt, um die virtuellen Gates zu erfassen, die andernfalls aufgrund der unboxed-Messungen unberücksichtigt bleiben würden.

boxing_pass_manager = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=False,
)
transpiled_circuit = boxing_pass_manager.run(circuit_with_unmeasured_qubit)
transpiled_circuit.draw("mpl", scale=0.8)
../_images/d59d0c0af3c47f474a964a455533b99e09a65364454ec04ced7581e6afac73ab.png
War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.