Transpiler-Stufen
Der Code auf dieser Seite wurde unter Berücksichtigung der folgenden Anforderungen entwickelt. Wir empfehlen die Verwendung dieser oder neuerer Versionen.
qiskit[all]~=2.5.2
Diese Seite beschreibt die Stufen der vorgefertigten Transpilierungspipeline im Qiskit SDK. Es gibt sechs Stufen:
initlayoutroutingtranslationoptimizationscheduling
Die Funktion generate_preset_pass_manager funktion erstellt einen voreingestellten gestuften Durchlaufmanager, der aus diesen Stufen besteht. Die spezifischen Durchläufe, aus denen die einzelnen Stufen bestehen, hängen von den Argumenten ab, die an generate_preset_pass_manager übergeben werden. optimization_level ist ein Positionsargument, das angegeben werden muss; es ist eine ganze Zahl, die 0, 1, 2 oder 3 sein kann. Höhere Werte deuten auf eine stärkere, aber kostspieligere Optimierung hin (siehe Transpilationsvorgaben und Konfigurationsoptionen ).
Es wird empfohlen, einen Schaltkreis zu transpilieren, indem Sie einen voreingestellten gestaffelten Passmanager erstellen und diesen dann auf dem Schaltkreis ausführen, wie in Transpilieren mit Passmanagern beschrieben. Eine einfachere, aber weniger anpassbare Alternative ist jedoch die Verwendung der transpile funktion. Diese Funktion nimmt den Stromkreis direkt als Argument an. Wie bei generate_preset_pass_manager hängt es von den Argumenten ab, die an transpile übergeben werden, z. B. optimization_level, welche Transpilerpässe verwendet werden. Die Funktion transpile ruft intern generate_preset_pass_manager auf, um einen voreingestellten, gestaffelten Durchlaufmanager zu erstellen, und führt ihn auf dem Schaltkreis aus.
Init-Phase
Diese erste Stufe tut standardmäßig sehr wenig und ist vor allem dann nützlich, wenn Sie Ihre eigenen anfänglichen Optimierungen vornehmen wollen. Da die meisten Layout- und Routing-Algorithmen nur für Ein- und Zwei-Qubit-Gatter ausgelegt sind, werden in dieser Phase auch alle Gatter, die mit mehr als zwei Qubits arbeiten, in Gatter übersetzt, die nur mit einem oder zwei Qubits arbeiten.
Weitere Informationen zur Implementierung Ihrer eigenen anfänglichen Optimierungen für diese Phase finden Sie im Abschnitt über Plugins und die Anpassung von Pass-Managern.
Layoutphase
Der nächste Schritt betrifft den Aufbau bzw. die Anbindung des Backends, an das eine Schaltung gesendet wird. Im Allgemeinen sind Quantenschaltungen abstrakte Einheiten, deren Qubits „virtuelle“ oder „logische“ Darstellungen der tatsächlichen Qubits sind, die bei Berechnungen verwendet werden. Um eine Folge von Operationen auszuführen, ist eine Eins-zu-Eins-Zuordnung zwischen den „virtuellen“ Qubits und den „physikalischen“ Qubits in einem tatsächlichen Quantenbauelement erforderlich. Diese Zuordnung wird als Objekt Layout gespeichert und ist Teil der Einschränkungen, die in der Befehlssatzarchitektur (ISA) eines Backends definiert sind.
Die Wahl des Mappings ist äußerst wichtig, um die Anzahl der SWAP-Operationen zu minimieren, die für die Abbildung der Eingangsschaltung auf die Gerätetopologie erforderlich sind, und um sicherzustellen, dass die am besten kalibrierten Qubits verwendet werden. Aufgrund der Bedeutung dieser Phase probieren die Manager für die Vorgabe des Passes verschiedene Methoden aus, um das beste Layout zu finden. In der Regel umfasst dies zwei Schritte: Zunächst wird versucht, ein "perfektes" Layout zu finden (ein Layout, das keine SWAP-Operationen erfordert), und dann wird in einem heuristischen Durchgang versucht, das beste Layout zu finden, das zu verwenden ist, wenn kein perfektes Layout gefunden werden kann. Für diesen ersten Schritt werden in der Regel zwei Passes verwendet:
TrivialLayout: Ordnet jedes virtuelle Qubit naiv dem gleich nummerierten physischen Qubit auf dem Gerät zu (d. h. [0,1,1,3] -> [0,1,1,3] ). Dies ist ein historisches Verhalten, das nur inoptimzation_level=1um zu versuchen, ein perfektes Layout zu finden. Gelingt dies nicht, wird als nächstesVF2Layoutversucht.VF2Layout: Dies ist eineAnalysisPass, die ein ideales Layout auswählt, indem sie diese Phase als ein Untergraphen-Isomorphismus-Problem behandelt, das durch den VF2++ Algorithmus gelöst wird. Wenn mehr als ein Layout gefunden wird, wird eine Bewertungsheuristik durchgeführt, um die Abbildung mit dem geringsten durchschnittlichen Fehler auszuwählen.
Für die heuristische Phase werden dann standardmäßig zwei Durchgänge verwendet:
DenseLayout: Findet den Untergraphen des Geräts mit der größten Konnektivität und der gleichen Anzahl von Qubits wie die Schaltung (wird für die Optimierungsstufe 1 verwendet, wenn in der Schaltung Kontrollflussoperationen (wie IfElseOp ) vorhanden sind).SabreLayoutDieser Durchlauf wählt ein Layout aus, indem er von einem anfänglichen zufälligen Layout ausgeht und denSabreSwapAlgorithmus wiederholt ausführt. Dieser Durchlauf wird nur in den Optimierungsstufen 1, 2 und 3 verwendet, wenn über denVF2LayoutDurchlauf kein perfektes Layout gefunden wurde. Weitere Einzelheiten zu diesem Algorithmus finden Sie in dem Artikel „ arXiv:1809.02573 ” (Ein Algorithmus zur Ermittlung der optimalen Anzahl von Schichten für die Schichtung von Schichten).
Routing-Phase
Um ein Zwei-Qubit-Gatter zwischen Qubits zu implementieren, die auf einem Quantenbauelement nicht direkt verbunden sind, müssen ein oder mehrere SWAP-Gatter in die Schaltung eingefügt werden, um die Qubit-Zustände zu verschieben, bis sie auf der Gatterkarte des Bauelements nebeneinander liegen. Jedes SWAP-Gatter ist ein teurer und geräuschvoller Vorgang, der durchgeführt werden muss. Daher ist die Ermittlung der Mindestanzahl von SWAP-Gattern, die erforderlich sind, um eine Schaltung auf ein bestimmtes Bauelement abzubilden, ein wichtiger Schritt im Transpilationsprozess. Aus Gründen der Effizienz wird diese Stufe normalerweise zusammen mit der Layout-Stufe berechnet, aber sie sind logisch voneinander getrennt. Die Layout-Stufe wählt die zu verwendenden Hardware-Qubits aus, während die Routing-Stufe die entsprechende Anzahl von SWAP-Gattern einfügt, um die Schaltungen mit dem gewählten Layout auszuführen.
Es ist jedoch schwierig, die optimale SWAP-Zuordnung zu finden. Tatsächlich handelt es sich um ein NP-schweres Problem, dessen Berechnung für alle außer den kleinsten Quantengeräten und Eingabeschaltungen unerschwinglich ist. Um dies zu umgehen, verwendet Qiskit einen stochastischen heuristischen Algorithmus namens SabreSwap , um eine gute, aber nicht unbedingt optimale SWAP-Zuordnung zu berechnen. Die Verwendung einer stochastischen Methode bedeutet, dass die erzeugten Schaltkreise bei wiederholten Durchläufen nicht garantiert dieselben sind. Die wiederholte Ausführung derselben Schaltung führt zu einer Verteilung der Schaltungstiefen und der Anzahl der Gatter am Ausgang. Aus diesem Grund entscheiden sich viele Benutzer dafür, die Routing-Funktion (oder die gesamte StagedPassManager) mehrmals auszuführen und die Schaltungen mit der geringsten Tiefe aus der Verteilung der Ausgänge auszuwählen.
Nehmen wir zum Beispiel eine 15-Qubit-GHZ-Schaltung, die 100-mal ausgeführt wird, und zwar mit einer "schlechten" (nicht angeschlossenen) initial_layout.
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(15)
ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))
depths = []
for seed in range(100):
pass_manager = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
layout_method="trivial", # Fixed layout mapped in circuit order
seed_transpiler=seed, # For reproducible results
)
depths.append(pass_manager.run(ghz).depth())
plt.figure(figsize=(8, 6))
plt.hist(depths, align="left", color="#AC557C")
plt.xlabel("Depth", fontsize=14)
plt.ylabel("Counts", fontsize=14)Output:
Text(0, 0.5, 'Counts')
Diese breite Streuung zeigt, wie schwierig es für den SWAP-Mapper ist, das beste Mapping zu berechnen. Um einen Einblick zu bekommen, sollten wir uns sowohl die Schaltung, die ausgeführt wird, als auch die Qubits, die auf der Hardware gewählt wurden, ansehen.
ghz.draw("mpl", idle_wires=False)Output:
from qiskit.visualization import plot_circuit_layout
# Plot the hardware graph and indicate which hardware qubits were chosen to run the circuit
transpiled_circ = pass_manager.run(ghz)
plot_circuit_layout(transpiled_circ, backend)Output:
Wie Sie sehen, muss diese Schaltung ein Zwei-Qubit-Gatter zwischen den Qubits 0 und 14 ausführen, die auf dem Konnektivitätsgraphen sehr weit voneinander entfernt sind. Die Ausführung dieser Schaltung erfordert daher das Einfügen von SWAP-Gattern, um alle Zwei-Qubit-Gatter unter Verwendung des SabreSwap -Durchgangs auszuführen.
Beachten Sie auch, dass sich der Algorithmus SabreSwap von der größeren Methode SabreLayout in der vorherigen Phase unterscheidet. Standardmäßig führt SabreLayout sowohl das Layout als auch das Routing aus und gibt die transformierte Schaltung zurück. Dies geschieht aus einigen besonderen technischen Gründen, die auf der API-Referenzseite des Passes aufgeführt sind.
Übersetzungsphase
Beim Schreiben eines Quantenschaltkreises können Sie beliebige Quanten-Gatter (unitäre Operationen) verwenden, zusammen mit einer Reihe von Operationen, die keine Gatter sind, wie beispielsweise Qubit-Messungen oder Rücksetzbefehle. Die meisten Quantenvorrichtungen unterstützen jedoch von Haus aus nur eine Handvoll Quanten- und Nicht-Quantenoperationen. Diese nativen Gatter sind Teil der Definition der ISA eines Ziels, und in dieser Phase der Voreinstellung PassManagers werden die in einer Schaltung angegebenen Gatter in die nativen Basisgatter eines bestimmten Backends übersetzt (oder entrollt ). Dies ist ein wichtiger Schritt, da dadurch die Schaltung vom Backend ausgeführt werden kann, führt jedoch in der Regel zu einer Zunahme der Schaltungstiefe und der Anzahl der Logikgatter.
Zwei Sonderfälle sind besonders hervorzuheben, um zu verdeutlichen, was diese Phase bewirkt.
- Wenn ein SWAP-Gate kein natives Gate für das Ziel-Backend ist, sind drei CNOT-Gates erforderlich:
print("native gates:" + str(sorted(backend.operation_names)))
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")Output:
native gates:['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Als Produkt von drei CNOT-Gattern ist ein SWAP eine teure Operation, die auf verrauschten Quantengeräten durchgeführt werden muss. Solche Operationen sind jedoch in der Regel notwendig, um eine Schaltung in die begrenzten Gate-Verbindungen vieler Bauelemente einzubetten. Daher ist die Minimierung der Anzahl von SWAP-Gattern in einer Schaltung ein Hauptziel des Transpilationsprozesses.
- Ein Toffoli-Gatter oder kontrolliert-kontrolliert-nicht-Gatter (
ccx) ist ein Drei-Qubit-Gatter. Da unsere Basisgattermenge nur Ein- und Zwei-Qubit-Gatter umfasst, muss diese Operation zerlegt werden. Sie ist jedoch recht kostspielig:
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.decompose().draw("mpl")Output:
Für jedes Toffoli-Gatter in einer Quantenschaltung kann die Hardware bis zu sechs CNOT-Gatter und eine Handvoll Ein-Qubit-Gatter ausführen. Dieses Beispiel zeigt, dass jeder Algorithmus, der mehrere Toffoli-Gatter verwendet, in einer Schaltung mit großer Tiefe endet und daher erheblich durch Rauschen beeinträchtigt wird.
Optimierungsphase
In dieser Phase geht es um die Zerlegung von Quantenschaltungen in den Basis-Gate-Satz des Zielbauelements und um den Kampf gegen die erhöhte Tiefe der Layout- und Routing-Phasen. Glücklicherweise gibt es viele Routinen zur Optimierung von Schaltungen durch Kombination oder Eliminierung von Gattern. In einigen Fällen sind diese Methoden so effektiv, dass die Ausgangsschaltungen eine geringere Tiefe haben als die Eingänge, selbst nach dem Layout und dem Routing zur Hardwaretopologie. In anderen Fällen kann nicht viel getan werden, und die Berechnungen können auf lauten Geräten schwierig durchzuführen sein. In dieser Phase beginnen sich die verschiedenen Optimierungsstufen zu unterscheiden.
- Für
optimization_level=1bereitet diese Phase dieOptimize1qGatesDecompositionundCXCancellationvor, die Ketten von Ein-Qubit-Gattern kombinieren und alle hintereinander geschalteten CNOT-Gatter aufheben. - Für
optimization_level=2verwendet diese Stufe denCommutativeCancellationdurchgang anstelle vonCXCancellation, der überflüssige Gatter durch Ausnutzung von Kommutierungsbeziehungen entfernt. - Für
optimization_level=3bereitet diese Phase die folgenden Durchgänge vor:
Außerdem werden in dieser Phase noch einige abschließende Prüfungen durchgeführt, um sicherzustellen, dass alle Anweisungen in der Schaltung aus den auf dem Ziel-Backend verfügbaren Basisgattern bestehen.
Das folgende Beispiel mit einem GHZ-Zustand zeigt die Auswirkungen verschiedener Optimierungseinstellungen auf die Schaltungstiefe und die Anzahl der Gatter.
Die Transpilationsleistung variiert aufgrund des stochastischen SWAP-Mappers. Daher werden sich die Zahlen unten wahrscheinlich jedes Mal ändern, wenn Sie den Code ausführen.

Der folgende Code konstruiert einen 15-Qubit-GHZ-Zustand und vergleicht die optimization_levels der Transpilierung in Bezug auf die resultierende Schaltungstiefe, die Anzahl der Gatter und die Anzahl der Multi-QuBit-Gatter.
ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))
depths = []
gate_counts = []
multiqubit_gate_counts = []
levels = [str(x) for x in range(4)]
for level in range(4):
pass_manager = generate_preset_pass_manager(
optimization_level=level,
backend=backend,
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
depths.append(circ.depth())
gate_counts.append(sum(circ.count_ops().values()))
multiqubit_gate_counts.append(circ.count_ops()["cx"])
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.bar(levels, depths, label="Depth")
ax1.set_xlabel("Optimization Level")
ax1.set_ylabel("Depth")
ax1.set_title("Output Circuit Depth")
ax2.bar(levels, gate_counts, label="Number of Circuit Operations")
ax2.bar(levels, multiqubit_gate_counts, label="Number of CX gates")
ax2.set_xlabel("Optimization Level")
ax2.set_ylabel("Number of gates")
ax2.legend()
ax2.set_title("Number of output circuit gates")
fig.tight_layout()
plt.show()Output:
Zeitplanung
Diese letzte Phase wird nur ausgeführt, wenn sie ausdrücklich gefordert wird (ähnlich wie die Init-Phase), und wird nicht standardmäßig ausgeführt (obwohl eine Methode angegeben werden kann, indem das Argument scheduling_method beim Aufruf von generate_preset_pass_manager gesetzt wird). Die Scheduling-Phase wird in der Regel eingesetzt, nachdem die Schaltung in die Zielbasis übersetzt, auf das Gerät abgebildet und optimiert wurde. Bei diesen Durchläufen geht es darum, die gesamte Leerlaufzeit in einem Stromkreis zu berücksichtigen. Auf einer hohen Ebene kann man sich den Scheduling-Durchlauf so vorstellen, dass explizit Verzögerungsanweisungen eingefügt werden, um die Leerlaufzeit zwischen den Gatterausführungen zu berücksichtigen und zu prüfen, wie lange die Schaltung auf dem Backend laufen wird.
Beispiel:
ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0, range(1, 5))
# Use fake backend
backend = GenericBackendV2(5)
# Run with optimization level 3 and 'asap' scheduling pass
pass_manager = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
scheduling_method="asap",
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
circ.draw(output="mpl", idle_wires=False)Output:
Der Transpiler fügte Delay Anweisungen ein, um die Leerlaufzeit auf jedem Qubit zu berücksichtigen. Um eine bessere Vorstellung vom Timing der Schaltung zu bekommen, können wir sie auch mit der Funktion timeline.draw() betrachten:
Die Planung eines Schaltkreises besteht aus zwei Teilen: der Analyse und der Zuordnung von Einschränkungen, gefolgt von einem Auffüllungsdurchgang. Der erste Teil erfordert die Ausführung eines Zeitplan-Analysedurchlaufs (standardmäßig ist dies ALAPSchedulingAnalysis), der die Schaltung analysiert und die Startzeit jeder Anweisung in der Schaltung in einem Zeitplan aufzeichnet. Sobald der Schaltkreis einen anfänglichen Zeitplan hat, können zusätzliche Durchläufe durchgeführt werden, um etwaige zeitliche Beschränkungen des Ziel-Backends zu berücksichtigen. Schließlich wird ein Auffüllungspassus wie PadDelay oder PadDynamicalDecoupling ausgeführt werden.
Nächste Schritte
- Wenn Sie wissen möchten, wie Sie die Funktion
generate_preset_passmanagerverwenden, beginnen Sie mit dem Thema Transpilations-Standardeinstellungen und Konfigurationsoptionen. - Erfahren Sie mehr über die Transpilierung mit dem Thema „Transpiler mit Passmanagern“.
- Probieren Sie die Anleitung zum Vergleichen der Transpiler-Einstellungen aus.
- Siehe die Transpile-API-Dokumentation.