Kapitel 4. Die Verwendung des Transpilers

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Wir haben die Klasse QuantumCircuit verwendet, um Quantenprogramme darzustellen. Der Zweck von Quantenprogrammen ist es, sie auf realen Geräten laufen zu lassen und Ergebnisse von ihnen zu erhalten. Bei der Programmierung kümmern wir uns normalerweise nicht um die gerätespezifischen Details und verwenden stattdessen High-Level-Operationen. Die meisten Geräte (und einige Simulatoren) können jedoch nur eine kleine Anzahl von Operationen ausführen und können Multiqubit-Gatter nur zwischen bestimmten Qubits durchführen. Das bedeutet, dass wir unsere Schaltung für das spezifische Gerät, auf dem wir arbeiten, umsetzen müssen.

Bei der Transpilierung werden die Operationen in der Schaltung in die vom Gerät unterstützten Operationen umgewandelt und die Qubits (über Swap-Gates) innerhalb der Schaltung ausgetauscht, um die begrenzte Qubit-Konnektivität zu überwinden. Der Transpiler von Qiskit übernimmt diese Aufgabe sowie einige Optimierungen, um die Anzahl der Gatter in der Schaltung zu reduzieren, wo es möglich ist.

Schnellstart mit Transpile

In diesem Abschnitt zeigen wir dir, wie du den Transpiler benutzt, um deine Schaltung gerätefähig zu machen. Wir geben einen kurzen Überblick über die Logik des Transpilers und darüber, wie wir die besten Ergebnisse mit ihm erzielen können.

Das einzige erforderliche Argument für transpile ist das Quantum​Cir⁠cuit, das wir umsetzen wollen. Wenn wir aber wollen, dass transpile etwas Interessantes tut, müssen wir ihm sagen, was es tun soll. Der einfachste Weg, deine Schaltung auf einem Gerät zum Laufen zu bringen, besteht darin, transpile das Backend-Objekt zu übergeben und es die benötigten Eigenschaften übernehmen zu lassen. transpile gibt ein neues QuantumCircuit Objekt zurück, das mit dem Backend kompatibel ist. Der folgende Codeschnipsel zeigt, wie die einfachste Anwendung des Transpilers aussieht:

from qiskit import transpile
transpiled_circuit = transpile(circuit, backend)

Hier erstellen wir zum Beispiel ein einfaches QuantumCircuit mit einem Qubit, einem YGate und zwei CXGates:

from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.y(0)
for t in range(2): qc.cx(0,t+1)
qc.draw()

Abbildung 4-1 zeigt die Ausgabe von qc.draw().

Abbildung 4-1. Einfache Schaltung mit einem Y-Gatter und zwei CX-Gattern

Im nächsten Codeschnipsel entscheiden wir uns, qc auf dem Mock-Backend FakeSantiago laufen zu lassen (ein Mock-Backend enthält die Eigenschaften und Geräuschmodelle eines realen Systems und verwendet AerSimulator, um dieses System zu simulieren). In der Ausgabe (nach dem Code) können wir sehen, dass FakeSantiago die Operation YGate nicht versteht:

from qiskit.test.mock import FakeSantiago
santiago = FakeSantiago()
santiago.configuration().basis_gates

['id', 'rz', 'sx', 'x', 'cx', 'reset']

Daher muss qc vor der Ausführung von transpiliert werden. Im nächsten Codeschnipsel werden wir sehen, was der Transpiler macht, wenn wir ihm qc geben und ihm sagen, dass er für santiago transpilieren soll:

t_qc = transpile(qc, santiago)
t_qc.draw()

Abbildung 4-2 zeigt die Ausgabe von t_qc.draw().

Abbildung 4-2. Ergebnis der Transpilierung eines einfachen Stromkreises

In Abbildung 4-2 kannst du sehen, dass der Transpiler Folgendes getan hat:

  • Die (virtuellen) Qubits 0, 1 und 2 in qc werden den (physikalischen) Qubits 2, 1 und 0 in t_qc zugeordnet.

  • Drei weitere CXGates hinzugefügt, um die (physikalischen) Qubits 0und 1 zu tauschen

  • Ersetzte unser YGate durch ein RZGate und ein XGate

  • Zwei zusätzliche Qubits hinzugefügt (da santiago fünf Qubits hat)

Das meiste davon scheint ziemlich vernünftig zu sein, mit Ausnahme der Hinzufügung all dieser CXGates. CXGates sind in der Regel ziemlich teure Operationen, also wollen wir sie so weit wie möglich vermeiden. Warum also hat der Transpiler das getan? In einigen Quantensystemen, darunter santiago, können nicht alle Qubits direkt miteinander kommunizieren.

Wir können überprüfen, welche Qubits mit einem anderen sprechen können, indem wir die Kopplungskarte des Systems aufrufen (führe backend.configuration()​.cou⁠pling_map aus, um diese zu erhalten). Ein kurzer Blick auf die Kopplungskarte von santiagozeigt uns, dass das physische Qubit 2 nicht mit dem physischen Qubit 0 kommunizieren kann, also müssen wir irgendwo einen Tausch vornehmen.

Hier ist die Ausgabe von santiago.configuration().coupling_map:

[[0, 1], [1, 0], [1, 2], [2, 1], [2, 3],
 [3, 2], [3, 4], [4, 3]]

Wenn wir beim Aufruf von transpile initial_layout=[1,0,2] setzen, können wir die Art und Weise, wie qc auf das Backend abgebildet wird, ändern und unnötige Vertauschungen vermeiden. Hier steht der Index jedes Elements in der Liste für das virtuelle Qubit (in qc) und der Wert an diesem Index für das physische Qubit. Dieses verbesserte Layout setzt die Vermutung des Transpilers außer Kraft und er muss keine zusätzlichen CXGates einfügen. Der folgende Codeschnipsel zeigt dies:

t_qc = transpile(qc, santiago, initial_layout=
                                        [1,0,2])
t_qc.draw()

Abbildung 4-3 zeigt die Ausgabe von t_qc.draw() im vorangegangenen Codeschnipsel.

Da santiago nur fünf Qubits hat, war es relativ einfach, ein gutes Layout für diese Schaltung auf diesem Gerät zu finden. Bei größeren Schaltkreis/Geräte-Kombinationen müssen wir dies algorithmisch tun. Eine Möglichkeit ist, optimization_level=2 so einzustellen, dass der Transpiler einen intelligenteren (aber teureren) Algorithmus verwendet, um ein besseres Layout auszuwählen.

Abbildung 4-3. Ergebnis der Transpilierung einer einfachen Schaltung mit einem intelligenteren Ausgangslayout

Die Funktion transpile lässt vier mögliche Einstellungen füroptimization_level zu:

optimization_level=0

Der Transpiler tut nur das absolute Minimum, das nötig ist, um die Schaltung im Backend zum Laufen zu bringen. Das anfängliche Layout behält die Indizes der physischen und virtuellen Qubits bei, fügt alle erforderlichen Vertauschungen hinzu und wandelt alle Gatter in Basisgatter um.

optimization_level=1

Dies ist der Standardwert. Der Transpiler trifft intelligentere Entscheidungen. Wenn wir zum Beispiel weniger virtuelle als physische Qubits haben, wählt der Transpiler die am besten verbundene Teilmenge der physischen Qubits und ordnet die virtuellen Qubits diesen zu. Der Transpiler kombiniert/entfernt auch Sequenzen von Gattern, wo dies möglich ist (z. B. zwei CXGates, die sich gegenseitig aufheben).

optimization_level=2

Der Transpiler sucht nach einem anfänglichen Layout, das keine Swaps benötigt, um die Schaltung auszuführen, oder, wenn dies fehlschlägt, nach der am besten verbundenen Teilmenge von Qubits. Wie bei Stufe 1 versucht der Transpiler auch hier, Gatter zu kollabieren und aufzuheben, wo es möglich ist.

optimization_level=3

Dies ist der höchste Wert, den wir einstellen können. Der Transpiler wird zusätzlich zu den Maßnahmen, die mit optimization_level=2 ergriffen werden, intelligentere Algorithmen verwenden, um Gates auszulöschen.

Transpiler Pässe

Je nach Anwendungsfall ist der Transpiler oft unsichtbar. Funktionen wie execute rufen ihn automatisch auf, und dank des Transpilers können wir bei der Erstellung von Schaltkreisen in der Regel das spezifische Gerät ignorieren, an dem wir gerade arbeiten. Trotz dieser Unauffälligkeit kann der Transpiler einen großen Einfluss auf die Leistung eines Schaltkreises haben. In diesem Abschnitt sehen wir uns die Entscheidungen an, die der Transpiler trifft, und erfahren, wie wir sein Verhalten ändern können, wenn es nötig ist.

Der PassManager

Wir bauen eine Transpilationsroutine aus einer Reihe kleinerer "Durchgänge" auf. Jeder Durchlauf ist ein Programm, das eine kleine Aufgabe ausführt (z. B. die Entscheidung über das anfängliche Layout oder das Einfügen von Swap-Gates), und wir verwenden ein PassManager Objekt, um unsere Abfolge von Durchläufen zu organisieren. In diesem Abschnitt zeigen wir ein einfaches Beispiel mit dem BasicSwap Durchlauf.

Zuerst brauchen wir einen Quantenschaltkreis zum Umsetzen. Der folgende Codeschnipsel erstellt einen einfachen Schaltkreis, den wir als Beispiel verwenden:

from qiskit import QuantumCircuit
qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0, 2)
qc.cx(2, 1)
qc.draw()

Abbildung 4-4 zeigt die Ausgabe von qc.draw().

Abbildung 4-4. Einfache Schaltung mit zwei CX-Gattern

Als Nächstes müssen wir die PassManager und die Pässe, die wir verwenden wollen, importieren und konstruieren. Der Konstruktor von BasicSwap fragt nach der Kopplungskarte des Geräts, auf dem wir unsere Schaltung ausführen wollen. Im folgenden Codeschnipsel tun wir so, als ob wir die Schaltung auf einem Gerät ausführen wollen, bei dem Qubit 0 nicht mit Qubit 2 interagieren kann (aber Qubit 1 kann mit beiden interagieren). Der Konstruktor PassManager fragt nach den Übergängen, die wir auf unseren Stromkreis anwenden wollen. In diesem Fall ist das nur der basic_swap Übergang, den wir in der vorherigen Zeile erstellt haben:

from qiskit.transpiler import PassManager,
                                   CouplingMap
from qiskit.transpiler.passes import BasicSwap

coupling_map = CouplingMap([[0,1], [1,2]])
basic_swap_pass = BasicSwap(coupling_map)
pm = PassManager(basic_swap_pass)

Nachdem wir nun unsere Transpilierungsprozedur erstellt haben, können wir sie mit dem folgenden Codeschnipsel auf den Stromkreis anwenden:

routed_qc = pm.run(qc)
routed_qc.draw()

Abbildung 4-5 zeigt die Ausgabe von routed_qc.draw().

Abbildung 4-5. Einfache Schaltung mit zwei CX-Gattern und zwei Swaps, die zur Ausführung in der Hardware benötigt werden

In Abbildung 4-5 sehen wir, dass der basic_swap Durchgang zwei Tauschgatter hinzugefügt hat, um die CXGates auszuführen. Beachte jedoch, dass die Qubits nicht in ihrer ursprünglichen Reihenfolge an zurückgegeben wurden.

Kompilieren/Übersetzen von Pässen

Um eine Schaltung auf einem Gerät zum Laufen zu bringen, brauchen wir , um alle Operationen in unserer Schaltung in Anweisungen umzuwandeln, die das Gerät unterstützt. Dazu müssen wir entweder High-Level-Gatter in Low-Level-Gatter zerlegen (eine Form des Kompilierens) oder einen Satz von Low-Level-Gattern in einen anderen übersetzen. Abbildung 4-6 zeigt, wie der Transpiler ein Multicontrolled-X-Gatter in kleinere Gatter zerlegen kann.

Abbildung 4-6. Beispiel für ein multigesteuertes X-Gatter, das in H-, Phasen- und CX-Gatter zerlegt wird

Zum Zeitpunkt der Erstellung dieses Artikels gibt es in Qiskit zwei Möglichkeiten, ein Gate in kleinere Gates aufzuteilen. Die erste Möglichkeit ist das definition Attribut des Gates. Wenn es gesetzt ist, enthält dieses Attribut ein QuantumCircuit, das dem Gate entspricht. Die Durchläufe Decompose und Unroller nutzen diese Definition, um Schaltkreise zu erweitern. Der Decompose Durchgang erweitert den Schaltkreis nur um eine Ebene, d.h. er versucht nicht, die Definitionen zu zerlegen, durch die wir jedes Tor ersetzt haben. Die Methode .decompose() der Klasse QuantumCircuit verwendet den Passus Decompose. Der Unroller Pass ist ähnlich, aber er zerlegt die Definitionen der einzelnen Gatter rekursiv, bis der Schaltkreis nur noch die Basisgatter enthält, die wir bei der Konstruktion angegeben haben.

Die zweite Möglichkeit, Gatter zu zerlegen, besteht darin, eine EquivalenceLibrary zu konsultieren. Diese Bibliothek kann viele Definitionsschaltungen für jede Anweisung speichern, so dass Pässe wählen können, wie sie jede Schaltung zerlegen. Das hat den Vorteil, dass man nicht an einen bestimmten Satz von Basisgattern gebunden ist. Der BasisTranslator Konstruktor benötigt eine EquivalenceLibrary und eine Liste von Gatterbezeichnungen. Wenn der Schaltkreis Gatter enthält, die nicht in der Äquivalenzbibliothek enthalten sind, haben wir keine andere Wahl, als die eingebauten Definitionen dieser Gatter zu verwenden. Der UnrollCustomDefinitions Durchlauf sieht sich die EquivalenceLibrary an, und wenn jedes Gatter keinen Eintrag in der Bibliothek hat, rollt er dieses Gatter mit seinem .definition Attribut aus. In den voreingestellten Transpiler-Routinen (die wir später in diesem Kapitel sehen werden), sehen wir normalerweise den UnrollCustomDefinitions Durchgang direkt vor dem BasisTranslator Durchgang.

Routing-Pässe

Einige Geräte können Multibit-Gatter nur zwischen bestimmten Teilmengen von Qubits durchführen. Die IBM-Hardware erlaubt in der Regel nur ein Multiqubit-Gatter (das CXGate) und kann diese Gatter nur zwischen bestimmten Qubit-Paaren ausführen. Wir nennen eine Liste mit jedem Paar möglicher Zwei-Qubit-Wechselwirkungen eine Kopplungskarte. Ein Beispiel dafür haben wir in "Der PassManager" gesehen . In diesem Beispiel haben wir diese Einschränkung überwunden, indem wir die Qubits in der Kopplungskarte mit Hilfe von Swap-Gates verschoben haben. Abbildung 4-7 zeigt ein Beispiel für eine Kopplungskarte.

Abbildung 4-7. Zeichnung einer Kopplungskarte: [[0, 1], [1, 2], [2, 3], [3, 1]]

Qiskit hat einige Algorithmen, um diese Swap Gates hinzuzufügen. Tabelle 4-1 listet alle verfügbaren Swap-Passes mit einer kurzen Beschreibung des Passes auf.

Tabelle 4-1. In Qiskit verfügbare Transpilerpässe austauschen
Name Erläuterung

BasicSwap

Dieser Durchlauf macht die geringste Rechenarbeit, die nötig ist, um den Kreislauf im Backend zum Laufen zu bringen.

LookaheadSwap

Im Gegensatz zu BasicSwap verwendet dieser Durchgang einen intelligenteren Algorithmus, um die Anzahl der Swap-Gates zu reduzieren. Er führt eine Best-First-Suche durch alle möglichen Kombinationen von Swaps durch.

StochasticSwap

Dies ist der Swap-Durchgang, der in den voreingestellten Durchgangsmanagern verwendet wird. Dieser Durchlauf ist nicht deterministisch, d.h. er erzeugt möglicherweise nicht jedes Mal den gleichen Stromkreis.

SabreSwap

In diesem Durchgang wird der SABRE-Algorithmus (SWAP-based BidiREctional heuristic search) verwendet, um die Anzahl der benötigten Swaps zu reduzieren.

BIPMapping

Dieser Durchlauf löst gleichzeitig das ursprüngliche Layout und die Swaps. Der Durchlauf bildet diese Probleme auf ein BIP-Problem (Binary Integer Programming) ab, das mit externen Programmen (docplex und CPLEX) gelöst wird, die du installieren musst. Außerdem kommt dieser Pass nicht gut mit großen Kopplungskarten (>~ 10 Qubits) zurecht.

Optimierung Pässe

Der Transpiler fungiert teilweise als Compiler, und wie die meisten Compiler enthält er auch einige Optimierungsläufe. Das größte Problem bei modernen Quantencomputern ist das Rauschen, und der Schwerpunkt dieser Optimierungsläufe liegt darauf, das Rauschen in der Ausgangsschaltung so weit wie möglich zu reduzieren. Die meisten dieser Optimierungsläufe versuchen, das Rauschen und die Laufzeit zu reduzieren, indem sie die Anzahl der Gatter minimieren.

Die einfachsten Optimierungen suchen nach Sequenzen von Gattern, die keine Auswirkungen haben, sodass wir sie sicher entfernen können. Zwei CXGates hintereinander hätten zum Beispiel keine Auswirkungen auf die unitäre Matrix der Schaltung, daher werden sie mit CXCancellation entfernt. Ähnlich verhält es sich mit dem Pass RemoveDiagonalGatesBeforeMeasure, der alle Gatter mit diagonalen Unitarien unmittelbar vor einer Messung entfernt (da sie die Messungen in der Berechnungsgrundlage nicht verändern). Der Durchlauf OptimizeSwapBeforeMeasure entfernt SWAP-Gatter unmittelbar vor einer Messung und ordnet die Messungen dem klassischen Register zu, um die Ausgangs-Bitfolge zu erhalten.

Qiskit verfügt auch über intelligentere Optimierungsläufe, die versuchen, Gruppen von Gattern durch kleinere oder effizientere Gruppen von Gattern zu ersetzen. Wir können zum Beispiel ganz einfach Sequenzen von Ein-Qubit-Gattern sammeln und sie durch ein einziges UGate ersetzen, das wir dann wieder in eine effiziente Gruppe von Basisgattern zerlegen können. Die Durchläufe Optimize1qGates und Optimize1qGatesDecomposition tun dies für verschiedene Sätze von Ausgangsgattern. Das Gleiche können wir auch für Zwei-Qubit-Gatter tun: Collect2qBlocks und ConsolidateBlocks finden Sequenzen von Zwei-Qubit-Gattern und fassen sie zu einer Zwei-Qubit-Unitaritätsmatrix zusammen. Der UnitarySynthesis -Pass kann diese dann wieder auf die Basisgatter unserer Wahl herunterbrechen.

Abbildung 4-8 zeigt zum Beispiel zwei Schaltungen mit identischen Einheiten, aber unterschiedlicher Anzahl von Gattern.

Abbildung 4-8. Beispiel desselben Stromkreises, nachdem er zwei verschiedene Transpilierungsprozesse durchlaufen hat

Anfängliche Layout-Auswahl geht durch

Wie beim Routing müssen wir auch hier entscheiden, wie wir unsere virtuellen Schaltkreis-Qubits den physischen Geräte-Qubits zuordnen. Tabelle 4-2 zeigt einige Algorithmen zur Auswahl des Layouts, die Qiskit anbietet.

Tabelle 4-2. Ursprüngliche Layout-Transpiler-Passagen, die in Qiskit verfügbar sind
Name Erläuterung

Trivial​Lay⁠out

In diesem Durchgang werden die Circuit-Qubits einfach über ihre Indizes auf die physikalischen Qubits abgebildet. Zum Beispiel wird das Circuit-Qubit mit dem Index 3 auf das Device-Qubit mit dem Index 3 abgebildet.

Dense​Lay⁠out

Dieser Durchgang findet die am besten verbundene Gruppe von physischen Qubits und ordnet die Schaltkreis-Qubits dieser Gruppe zu.

Noise​Adap⁠tive​Layout

Dieser Durchlauf verwendet Informationen über die Geräuscheigenschaften des Geräts, um ein Layout auszuwählen.

Sabre​Lay⁠out

In diesem Durchgang wird der SABRE-Algorithmus verwendet, um ein Anfangslayout zu finden, das so wenig SWAPs wie möglich erfordert.

CSPLayout

Dieser Pass wandelt die Layoutauswahl in ein Constraint Satisfaction Problem (CSP) um. Der Durchlauf verwendet dann die RecursiveBacktrackingSolver des Moduls constraint, um das beste Layout zu finden.

Voreingestellte PassManager

Als wir zuvor die High-Level-Funktion transpile verwendet haben, haben wir uns nicht um die einzelnen Pässe gekümmert und stattdessen den Parameter optimization_level gesetzt. Dieser Parameter weist den Transpiler an, einen von vier voreingestellten Pass-Managern zu verwenden. Qiskit erstellt diese voreingestellten Passmanager durch Funktionen, die Konfigurationseinstellungen übernehmen und ein PassManager Objekt zurückgeben. Da wir nun einige Übergänge verstehen, können wir uns ansehen, was die verschiedenen Transpilierungsroutinen tun.

Im Folgenden findest du den Code, mit dem wir die Pässe für eine einfache Transpilationsroutine extrahiert haben, falls du sie nachbauen möchtest:

from qiskit.transpiler import (PassManagerConfig,
                                      CouplingMap)
from qiskit.transpiler.preset_passmanagers import\
                              level_0_pass_manager
from qiskit.test.mock import FakeSantiago

sys_conf = FakeSantiago().configuration()
pm_conf = PassManagerConfig(
    basis_gates=sys_conf.basis_gates,
    coupling_map=CouplingMap(sys_conf.coupling_map))

for i, step in enumerate(
    level_0_pass_manager(pm_conf).passes()):
    print(f'Step {i}:')
    for transpiler_pass in step['passes']:
        print(f'  {transpiler_pass.name()}')

Einige der folgenden Durchläufe haben wir in diesem Kapitel nicht behandelt, weil es sich um Analysedurchläufe handelt, die keinen Einfluss auf die Schaltung haben, oder weil es sich um Bereinigungsdurchläufe handelt, für die wir keinen Algorithmus auswählen können. Es ist unwahrscheinlich, dass diese Durchläufe einen vermeidbaren, negativen Einfluss auf die Leistung unserer Schaltungen haben. Außerdem haben wir einige Durchläufe auf Impulsebene nicht behandelt, die nicht in den Rahmen dieses Kapitels fallen:

Step 0:
  SetLayout
Step 1:
  TrivialLayout
Step 2:
  FullAncillaAllocation
  EnlargeWithAncilla
  ApplyLayout
Step 3:
  Unroll3qOrMore
Step 4:
  CheckMap
Step 5:
  BarrierBeforeFinalMeasurements
  StochasticSwap
Step 6:
  UnrollCustomDefinitions
  BasisTranslator
Step 7:
  TimeUnitConversion
Step 8:
  ValidatePulseGates
  AlignMeasures

Erinnere dich daran, dass optimization_level=0 nur das Nötigste tut, um die Schaltung auf dem Gerät zum Laufen zu bringen. Wir können sehen, dass TrivialLayout verwendet wird, um ein erstes Layout auszuwählen und dann die Schaltung so zu erweitern, dass sie die gleiche Anzahl von Qubits hat wie das Gerät. Der Transpiler rollt dann die Schaltung zu Ein- und Zwei-Qubit-Gattern aus und verwendet StochasticSwap für das Routing von . Schließlich rollt er alles so weit wie möglich ab und übersetzt die Schaltung in die Basisgatter des Geräts.

Für optimization_level=3 hingegen enthält die PassManager die folgenden Durchgänge:

Step 0:
  Unroll3qOrMore
Step 1:
  RemoveResetInZeroState
  OptimizeSwapBeforeMeasure
  RemoveDiagonalGatesBeforeMeasure
Step 2:
  SetLayout
Step 3:
  TrivialLayout
  Layout2qDistance
Step 4:
  CSPLayout
Step 5:
  DenseLayout
Step 6:
  FullAncillaAllocation
  EnlargeWithAncilla
  ApplyLayout
Step 7:
  CheckMap
Step 8:
  BarrierBeforeFinalMeasurements
  StochasticSwap
Step 9:
  UnrollCustomDefinitions
  BasisTranslator
Step 10:
  RemoveResetInZeroState
Step 11:
  Depth
  FixedPoint
  Collect2qBlocks
  ConsolidateBlocks
  UnitarySynthesis
  Optimize1qGatesDecomposition
  CommutativeCancellation
  UnrollCustomDefinitions
  BasisTranslator
Step 12:
  TimeUnitConversion
Step 13:
  ValidatePulseGates
  AlignMeasures

Diese PassManager ist ganz anders. Nach dem Abrollen zu Ein- und Zwei-Qubit-Gattern können wir bereits in Schritt 1 einige Optimierungsläufe sehen, bei denen unnötige Gatter entfernt werden. Der Transpiler probiert dann verschiedene Ansätze zur Layoutauswahl aus. Zuerst prüft er, ob TrivialLayout optimal ist (d. h., ob keine SWAPs eingefügt werden müssen, um auf dem Gerät ausgeführt zu werden). Wenn das nicht der Fall ist, versucht der Transpiler, mit CSPLayout ein Layout zu finden. Wenn CSPLayout keine Lösung findet, verwendet der Transpiler den Algorithmus DenseLayout. Im nächsten Schritt (Schritt 6) fügt der Transpiler zusätzliche Qubits hinzu (falls nötig), damit die Schaltkreise die gleiche Anzahl an Qubits haben wie das Gerät. Dann verwendet er den StochasticSwap Algorithmus, um alle Zwei-Qubit-Gatter auf der Kopplungskarte des Geräts zu ermöglichen. Nachdem das Routing erledigt ist, übersetzt der Transpiler den Schaltkreis in die Basisgatter des Geräts, bevor er in Schritt 11 einige letzte Optimierungen vornimmt.

Wenn wir uns die optimization_level=3 Durchläufe ansehen, sehen wir, dass der Transpiler ein sehr anspruchsvolles Programm ist, das einen großen Einfluss auf das Verhalten deiner Schaltungen haben kann. Zum Glück verstehst du jetzt die Probleme , die der Transpiler lösen muss, und einige der Algorithmen, die er dazu verwendet.

Get Qiskit Pocket Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.