Zum Hauptinhalt springen

Transpiler-Optimierungsstufe festlegen

Paketversionen

Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese Versionen oder neuere zu verwenden.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Echte Quantengeräte unterliegen Rauschen und Gate-Fehlern, weshalb die Optimierung von Schaltkreisen zur Reduzierung ihrer Tiefe und Gate-Anzahl die Ergebnisse, die durch das Ausführen dieser Schaltkreise erzielt werden, erheblich verbessern kann. Die Funktion generate_preset_pass_manager hat ein erforderliches Positionsargument, optimization_level, das steuert, wie viel Aufwand der Transpiler für die Optimierung von Schaltkreisen aufwendet. Dieses Argument kann eine ganze Zahl sein, die einen der Werte 0, 1, 2 oder 3 annimmt. Höhere Optimierungsstufen erzeugen stärker optimierte Schaltkreise auf Kosten längerer Kompilierungszeiten. Die folgende Tabelle erläutert die Optimierungen, die mit jeder Einstellung durchgeführt werden.

OptimierungsstufeBeschreibung
0

Keine Optimierung: typischerweise für Hardware-Charakterisierung verwendet

  • Grundlegende Übersetzung
  • Layout/Routing: TrivialLayout, wobei dieselben physikalischen Qubit-Nummern wie virtuelle ausgewählt und SWAPs eingefügt werden, um es zum Laufen zu bringen (unter Verwendung von SabreSwap)
1

Leichte Optimierung:

  • Layout/Routing: Das Layout wird zunächst mit TrivialLayout versucht. Wenn zusätzliche SWAPs erforderlich sind, wird ein Layout mit einer minimalen Anzahl von SWAPs mithilfe von SabreSwap gefunden, dann verwendet es VF2LayoutPostLayout, um die besten Qubits im Graphen auszuwählen.
  • InverseCancellation
  • 1Q-Gate-Optimierung
2

Mittlere Optimierung:

  • Layout/Routing: Optimierungsstufe 1 (ohne trivial) + heuristisch optimiert mit größerer Suchtiefe und Versuchen der Optimierungsfunktion. Da TrivialLayout nicht verwendet wird, wird kein Versuch unternommen, dieselben physikalischen und virtuellen Qubit-Nummern zu verwenden.
  • CommutativeCancellation
3

Hohe Optimierung:

  • Optimierungsstufe 2 + heuristisch weiter auf Layout/Routing optimiert mit größerem Aufwand/Versuchen
  • Resynthese von Zwei-Qubit-Blöcken unter Verwendung der Cartan KAK-Zerlegung.
  • Unitaritätsbrechende Passes:
    • OptimizeSwapBeforeMeasure: Verschiebt die Messungen, um SWAPs zu vermeiden
    • RemoveDiagonalGatesBeforeMeasure: Entfernt Gates vor Messungen, die die Messungen nicht beeinflussen würden

Optimierungsstufe in der Praxis

Da Zwei-Qubit-Gates typischerweise die bedeutendste Fehlerquelle sind, können wir die „Hardware-Effizienz" der Transpilierung annäherungsweise quantifizieren, indem wir die Anzahl der Zwei-Qubit-Gates im resultierenden Schaltkreis zählen. Hier probieren wir die verschiedenen Optimierungsstufen auf einem Eingangsschaltkreis aus, der aus einer zufälligen unitären Matrix gefolgt von einem SWAP-Gate besteht.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit
from qiskit.circuit.library import UnitaryGate
from qiskit.quantum_info import Operator, random_unitary

UU = random_unitary(4, seed=12345)
rand_U = UnitaryGate(UU)

qc = QuantumCircuit(2)
qc.append(rand_U, range(2))
qc.swap(0, 1)
qc.draw("mpl", style="iqp")

Output of the previous code cell

Wir verwenden das FakeSherbrooke-Mock-Backend in unseren Beispielen. Zuerst transpilieren wir mit Optimierungsstufe 0.

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()

pass_manager = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Der transpilierte Schaltkreis hat sechs Zwei-Qubit-ECR-Gates.

Wiederholen für Optimierungsstufe 1:

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()

pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Der transpilierte Schaltkreis hat immer noch sechs ECR-Gates, aber die Anzahl der Einzel-Qubit-Gates hat sich verringert.

Wiederholen für Optimierungsstufe 2:

pass_manager = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=12345
)
qc_t2_exact = pass_manager.run(qc)
qc_t2_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Das ergibt dieselben Ergebnisse wie Optimierungsstufe 1. Beachte, dass eine Erhöhung der Optimierungsstufe nicht immer einen Unterschied macht.

Nochmals wiederholen, mit Optimierungsstufe 3:

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=12345
)
qc_t3_exact = pass_manager.run(qc)
qc_t3_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Jetzt gibt es nur noch drei ECR-Gates. Dieses Ergebnis erhalten wir, weil Qiskit bei Optimierungsstufe 3 versucht, Zwei-Qubit-Gate-Blöcke neu zu synthetisieren, und jedes Zwei-Qubit-Gate mit höchstens drei ECR-Gates implementiert werden kann. Wir können sogar noch weniger ECR-Gates erhalten, wenn wir approximation_degree auf einen Wert kleiner als 1 setzen, sodass der Transpiler Näherungen vornehmen darf, die möglicherweise einen gewissen Fehler in der Gate-Zerlegung einführen (siehe Häufig verwendete Parameter für die Transpilierung):

pass_manager = generate_preset_pass_manager(
optimization_level=3,
approximation_degree=0.99,
backend=backend,
seed_transpiler=12345,
)
qc_t3_approx = pass_manager.run(qc)
qc_t3_approx.draw("mpl", idle_wires=False)

Output of the previous code cell

Dieser Schaltkreis hat nur zwei ECR-Gates, ist aber ein Näherungsschaltkreis. Um zu verstehen, wie sich seine Wirkung vom exakten Schaltkreis unterscheidet, können wir die Fidelität zwischen dem unitären Operator, den dieser Schaltkreis implementiert, und dem exakten Unitären berechnen. Vor der Berechnung reduzieren wir zunächst den transpilierten Schaltkreis, der 127 Qubits enthält, auf einen Schaltkreis, der nur die aktiven Qubits enthält, von denen es zwei gibt.

import numpy as np

def trace_to_fidelity_2q(trace: float) -> float:
return (4.0 + trace * trace.conjugate()) / 20.0

# Reduce circuits down to 2 qubits so they are easy to simulate
qc_t3_exact_small = QuantumCircuit.from_instructions(qc_t3_exact)
qc_t3_approx_small = QuantumCircuit.from_instructions(qc_t3_approx)

# Compute the fidelity
exact_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_exact_small).adjoint().data, UU))
)
approx_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_approx_small).adjoint().data, UU))
)
print(
f"Synthesis fidelity\nExact: {exact_fid:.3f}\nApproximate: {approx_fid:.3f}"
)
Synthesis fidelity
Exact: 1.000+0.000j
Approximate: 0.992+0.000j

Das Anpassen der Optimierungsstufe kann auch andere Aspekte des Schaltkreises verändern, nicht nur die Anzahl der ECR-Gates. Beispiele dafür, wie das Festlegen der Optimierungsstufe das Layout verändert, findest du unter Quantencomputer darstellen.

Nächste Schritte

Empfehlungen