Skip to main content
IBM Quantum Platform

Transpiler-Einstellungen vergleichen

  • Der Code auf dieser Seite wurde unter Berücksichtigung der folgenden Anforderungen entwickelt. Wir empfehlen, diese Versionen oder neuere zu verwenden.

    qiskit[all]~=2.5.2
    qiskit-ibm-runtime~=0.47.0
    

Unterschiedliche Transpiler-Einstellungen bieten verschiedene Arten der Schaltungsoptimierung, oft auf Kosten einer längeren klassischen Verarbeitungszeit. Dieser Leitfaden führt Sie durch den gesamten Prozess der Erstellung, Transpilation und Einreichung von Schaltkreisen, um die Leistungsfähigkeit verschiedener Einstellungen zu demonstrieren.

Beachten Sie, dass dieselbe Einstellung die Ergebnisse eines Schaltkreises verbessern, während sie die eines anderen beeinträchtigen könnte. Überprüfen Sie unbedingt die resultierenden transpilierten Schaltkreise, bevor Sie sie auf der eigentlichen Hardware ausführen.


Einrichten und Erstellen einer Beispielschaltung

# Create circuit to test transpiler on
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import grover_operator, DiagonalGate

# Use Statevector object to calculate the ideal output
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_histogram
from qiskit.transpiler import PassManager

from qiskit.circuit.library import XGate
from qiskit.quantum_info import hellinger_fidelity

Erstellen Sie einen kleinen Schaltkreis, den der Transpiler optimieren soll. Dieses Beispiel erstellt einen Schaltkreis, der Grovers Algorithmus mit einem Orakel ausführt, das den Zustand 111markiert. Als Nächstes simulieren Sie die ideale Verteilung (das Ergebnis, das Sie erwarten würden, wenn Sie dies unendlich oft auf einem perfekten Quantencomputer ausführen würden) zum späteren Vergleich.

oracle = DiagonalGate([1] * 7 + [-1])
qc = QuantumCircuit(3)
qc.h([0, 1, 2])
qc = qc.compose(grover_operator(oracle))

qc.draw(output="mpl", style="iqp")

Output:

Output of the previous code cell
ideal_distribution = Statevector.from_instruction(qc).probabilities_dict()

plot_histogram(ideal_distribution)

Output:

Output of the previous code cell

Transpile

Als Nächstes transpilieren Sie die Schaltkreise für die QPU. Sie werden die Leistung des Transpilers mit optimization_level auf 0 (niedrigste Stufe) gegenüber 3 (höchste Stufe) vergleichen. Die niedrigste Optimierungsstufe führt das absolute Minimum aus, das erforderlich ist, um den Schaltkreis auf dem Gerät zum Laufen zu bringen. Sie ordnet die Qubits des Schaltkreises den Qubits des Geräts zu und fügt Swap-Gates hinzu, um alle Zwei-Qubit-Operationen zu ermöglichen. Die höchste Optimierungsstufe ist wesentlich intelligenter und nutzt zahlreiche Tricks, um die Gesamtanzahl der Gates zu reduzieren. Da Multi-Qubit-Gatter hohe Fehlerraten aufweisen und Qubits mit der Zeit dekoherieren, sollten kürzere Schaltkreise bessere Ergebnisse liefern.

Important

In diesem Beispiel wird die Hardware „ IBM Quantum® “ verwendet, Sie können es jedoch auf jeder Qiskit-kompatiblen QPU ausprobieren. Ihre Ergebnisse können davon abweichen.

Die folgende Zelle transpiliert qc für beide Werte von optimization_level, gibt die Anzahl der Zwei-Qubit-Gatter aus und fügt die transpilierten Schaltungen zu einer Liste hinzu. Einige der Algorithmen des Transpilers sind randomisiert, daher wird ein Startwert für die Reproduzierbarkeit festgelegt.

# Use IBM Quantum Compute Service to run jobs on hardware
from qiskit_ibm_runtime import (
    QiskitRuntimeService,
    SamplerV2 as Sampler,
)
# Select the backend with the fewest number of jobs in the queue
service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, simulator=False, min_num_qubits=127
)
backend.name

Output:

'ibm_fez'
# Need to add measurements to the circuit
qc.measure_all()

# Find the correct two-qubit gate
twoQ_gates = set(["ecr", "cz", "cx"])
for gate in backend.basis_gates:
    if gate in twoQ_gates:
        twoQ_gate = gate

circuits = []
for optimization_level in [0, 3]:
    pm = generate_preset_pass_manager(
        optimization_level, backend=backend, seed_transpiler=0
    )
    t_qc = pm.run(qc)
    print(
        f"Two-qubit gates (optimization_level={optimization_level}): ",
        t_qc.count_ops()[twoQ_gate],
    )
    circuits.append(t_qc)

Output:

Two-qubit gates (optimization_level=0):  21
Two-qubit gates (optimization_level=3):  12

Da CNOTs in der Regel eine hohe Fehlerrate aufweisen, sollte die optimization_level=3 mit transpilierte Schaltung eine deutlich bessere Leistung erzielen.

Eine weitere Möglichkeit, die Leistung zu verbessern, ist die dynamische Entkopplung, bei der eine Abfolge von Schaltvorgängen auf inaktivierte Qubits angewendet wird. Dadurch werden einige unerwünschte Wechselwirkungen mit der Umgebung verhindert. Die folgende Zelle fügt dem mit transpilierten Schaltkreis eine optimization_level=3 dynamische Entkopplung hinzu und fügt ihn der Liste hinzu.

from qiskit_ibm_runtime.transpiler.passes.scheduling import (
    ASAPScheduleAnalysis,
    PadDynamicalDecoupling,
)

# Get gate durations so the transpiler knows how long each operation takes
durations = backend.target.durations()

# This is the sequence we'll apply to idling qubits
dd_sequence = [XGate(), XGate()]

# Run scheduling and dynamic decoupling passes on circuit
pm = PassManager(
    [
        ASAPScheduleAnalysis(durations),
        PadDynamicalDecoupling(durations, dd_sequence),
    ]
)
circ_dd = pm.run(circuits[1])

# Add this new circuit to our list
circuits.append(circ_dd)
circ_dd.draw(output="mpl", style="iqp", idle_wires=False)

Output:

Output of the previous code cell

Führen Sie den Parcours durch

Nun verfügen Sie über eine Liste von Schaltkreisen, die mit unterschiedlichen Einstellungen transpiliert wurden. Führen Sie anschließend diese Schaltkreise mit der Sampler-Primitive aus und speichern Sie die Ergebnisse in result.

sampler = Sampler(backend)
job = sampler.run(
    [(circuit) for circuit in circuits],  # sample all three circuits
    shots=8000,
)
result = job.result()

Ansichtsergebnisse

Zeichnen Sie schließlich die Ergebnisse der Gerätemessungen gegen die ideale Verteilung auf. Man sieht, dass die Ergebnisse bei aufgrund der geringeren Gate-Anzahl optimization_level=3 näher an der idealen Verteilung liegen, und optimization_level=3 + dd liegt aufgrund der dynamischen Entkopplung sogar noch näher daran.

binary_prob = [
    {
        k: v / res.data.meas.num_shots
        for k, v in res.data.meas.get_counts().items()
    }
    for res in result
]
plot_histogram(
    binary_prob + [ideal_distribution],
    bar_labels=False,
    legend=[
        "optimization_level=0",
        "optimization_level=3",
        "optimization_level=3 + dd",
        "ideal distribution",
    ],
)

Output:

Output of the previous code cell

Sie können dies überprüfen, indem Sie die Hellinger-Fidelity zwischen jedem Ergebnissatz und der idealen Verteilung berechnen (je höher desto besser, wobei 1 die perfekte Fidelity darstellt).

for prob in binary_prob:
    print(f"{hellinger_fidelity(prob, ideal_distribution):.3f}")

Output:

0.717
0.982
0.981

Nächste Schritte

Empfehlungen
War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.