Skip to main content
IBM Quantum Platform

Einführung in den Qiskit-Transpiler mit KI-Unterstützung

Geschätzte Nutzungsdauer: 5 Minuten auf IBM Heron (HINWEIS: Dies ist lediglich eine Schätzung. (Die Laufzeit kann variieren.)


Lernziele

  • So verwenden Sie den KI-gestützten Transpiler (generate_ai_pass_manager) als direkten Ersatz für den Standard-Transpiler
  • Wie schneidet der KI-gestützte Transpiler im Vergleich zum Standard-Transpiler hinsichtlich der Zwei-Qubit-Tiefe, der Gatteranzahl und der Transpilierungszeit ab?
  • Wie man Spiegel-Schaltkreise nutzt, um die Qualität der Transpilierung durch Hardware-Ausführung zu bewerten

Voraussetzungen


Hintergrund

Der Qiskit-Transpiler mit KI-Unterstützung führt auf maschinellem Lernen basierende Transpilierungsschritte ein, mit denen sich kürzere und hardwareeffizientere Schaltungen erzeugen lassen als mit herkömmlichen heuristischen Methoden wie SABRE. Kürzere Schaltkreise weisen weniger Rauschen auf, was die Ergebnisqualität auf realer Quantenhardware direkt verbessert.

In diesem Tutorial vergleichen wir zwei Transpilierungsstrategien:

Strategie
-API
Standardgenerate_preset_pass_manager(optimization_level=3, ...)
KIgenerate_ai_pass_manager(optimization_level=1, ai_optimization_level=3, ...)

Für jede Strategie messen wir drei Kennzahlen: die Tiefe der Zwei-Qubit-Gatter, die Gesamtzahl der Gatter und die Laufzeit der Transpilierung.

Benchmarks für KI-gestützte Transpiler

In Benchmark-Tests erzeugte der KI-gestützte Transpiler im Vergleich zum Standard-Qiskit-Transpiler durchweg flachere Schaltungen von höherer Qualität. Für diese Tests haben wir die Standard-Pass-Manager-Strategie von Qiskit verwendet, die mit generate_preset_pass_manager. konfiguriert wurde. Diese Standardstrategie ist zwar oft effektiv, kann jedoch bei größeren oder komplexeren Schaltungen an ihre Grenzen stoßen. Im Gegensatz dazu konnten KI-gestützte Durchläufe bei der Transpilation in die „Heavy-Hex“-Topologie der „ IBM Quantum® “-Hardware bei großen Schaltkreisen (100+ Qubits) eine durchschnittliche Reduzierung der Anzahl der Zwei-Qubit-Gates um 24 % und der Schaltungstiefe um 36 % erzielen. Weitere Informationen zu diesen Benchmarks finden Sie in diesem Blog.

Benchmarks für KI-gestützte Transpiler

In diesem Tutorial werden die wichtigsten Vorteile von KI-Pässen erläutert und ein Vergleich mit herkömmlichen Methoden angestellt.


Anforderungen

Bevor Sie mit dieser Anleitung beginnen, stellen Sie bitte sicher, dass Folgendes installiert ist:

  • Qiskit SDK v2.0 oder höher, mit Visual isierungsunterstützung
  • Qiskit Runtime pip install qiskit-ibm-runtimev0.22 oder höher
  • Qiskit- IBM -Transpiler mit lokalem KI-Modus (pip install 'qiskit-ibm-transpiler[ai-local-mode]')
  • Qiskit Aer (pip install qiskit-aer)

Einrichtung

from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_transpiler import generate_ai_pass_manager
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error
import matplotlib.pyplot as plt
from statistics import mean, stdev
import time
import logging

seed = 42


def transpile_with_metrics(pass_manager, circuit):
    """Transpile a circuit and return the result along with key metrics."""
    start = time.time()
    qc_out = pass_manager.run(circuit)
    elapsed = time.time() - start

    depth_2q = qc_out.depth(lambda x: x.operation.num_qubits == 2)
    gate_count = qc_out.size()

    return qc_out, {
        "depth_2q": depth_2q,
        "gate_count": gate_count,
        "time_s": round(elapsed, 3),
    }


def remap_to_contiguous(tqc):
    """Remap a transpiled circuit to use contiguous qubit indices.

    Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)
    on a large backend. This remaps them to 0, 1, 2, ... so Aer only
    simulates the active qubits."""
    active = sorted(
        {tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}
    )
    qubit_map = {old: new for new, old in enumerate(active)}
    new_qc = QuantumCircuit(len(active))
    for inst in tqc.data:
        old_indices = [tqc.find_bit(q).index for q in inst.qubits]
        new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])
    return new_qc


def build_mirror_circuit(tqc, simulate=True):
    """Build a mirror circuit: U followed by U-dagger, with measurements.

    The expected output is always |0...0>, so measuring the survival
    probability reveals how much noise each transpilation strategy adds.

    Args:
        tqc: A transpiled circuit.
        simulate: If True (default), remap to contiguous qubits so Aer
            only simulates the active qubits. If False, keep the full
            physical layout for hardware execution."""
    if simulate:
        tqc = remap_to_contiguous(tqc)
    mirror = tqc.compose(tqc.inverse())
    mirror.measure_all()
    return mirror


def print_summary(results):
    """Print a summary of each metric as mean +/- stdev across all circuits,
    along with the mean percentage improvement of AI over Default."""
    metrics = [
        ("Depth 2Q", "Depth 2Q (Default)", "Depth 2Q (AI)"),
        ("Gate Count", "Gate Count (Default)", "Gate Count (AI)"),
        ("Time (s)", "Time (Default)", "Time (AI)"),
    ]
    header = (
        f"{'Metric':<12}{'Default (mean +/- std)':>24}"
        f"{'AI (mean +/- std)':>22}{'AI % improvement':>22}"
    )
    print(header)
    print("-" * len(header))
    for label, col_def, col_ai in metrics:
        defaults = [r[col_def] for r in results]
        ais = [r[col_ai] for r in results]
        pct = [(d - a) / d * 100 for d, a in zip(defaults, ais)]
        default_str = f"{mean(defaults):.1f} +/- {stdev(defaults):.1f}"
        ai_str = f"{mean(ais):.1f} +/- {stdev(ais):.1f}"
        pct_str = f"{mean(pct):+.1f}% +/- {stdev(pct):.1f}%"
        print(f"{label:<12}{default_str:>24}{ai_str:>22}{pct_str:>22}")


def plot_metrics_and_pct(results, title_prefix):
    """Plot metric comparisons and percentage improvement of AI over Default."""
    qubits = [r["Qubits"] for r in results]
    metrics = [
        ("Depth 2Q (Default)", "Depth 2Q (AI)", "Two-Qubit Depth"),
        ("Gate Count (Default)", "Gate Count (AI)", "Gate Count"),
        ("Time (Default)", "Time (AI)", "Transpilation Time"),
    ]

    # Row 1: raw metric comparison
    fig, axs = plt.subplots(1, 3, figsize=(21, 5))
    fig.suptitle(
        f"{title_prefix}: Metric Comparison",
        fontsize=15,
        fontweight="bold",
        y=1.02,
    )
    for ax, (col_def, col_ai, label) in zip(axs, metrics):
        ax.plot(qubits, [r[col_def] for r in results], "o-", label="Default")
        ax.plot(qubits, [r[col_ai] for r in results], "s-", label="AI")
        ax.set_title(label)
        ax.set_xlabel("Number of Qubits")
        ax.set_ylabel(label)
        ax.legend()
    plt.tight_layout()
    plt.show()

    # Row 2: percentage improvement
    fig, axs = plt.subplots(1, 3, figsize=(21, 5))
    fig.suptitle(
        f"{title_prefix}: % Improvement of AI over Default",
        fontsize=15,
        fontweight="bold",
        y=1.02,
    )
    for ax, (col_def, col_ai, label) in zip(axs, metrics):
        pct = [(r[col_def] - r[col_ai]) / r[col_def] * 100 for r in results]
        ax.axhline(
            0, color="#1f77b4", linewidth=2, label="Default (baseline)"
        )
        ax.plot(qubits, pct, "s-", color="#ff7f0e", label="AI")
        ax.fill_between(qubits, 0, pct, alpha=0.15, color="#ff7f0e")
        ax.set_title(label)
        ax.set_xlabel("Number of Qubits")
        ax.set_ylabel("% Improvement")
        ax.legend()
    plt.tight_layout()
    plt.show()


# Suppress verbose AI-powered transpiler logs
logging.getLogger(
    "qiskit_ibm_transpiler.wrappers.ai_local_synthesis"
).setLevel(logging.WARNING)

Beispiel für einen Simulator im kleinen Maßstab

Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen

Wir generieren 20 zufällige Schaltkreise der Tiefe 4, wobei die Anzahl der Qubits zwischen sechs und 25 liegt. Diese Schaltungen dienen uns als Testfälle für den Vergleich verschiedener Transpilierungsstrategien.

num_circuits_sim = 20
depth_sim = 4
qubit_range_sim = list(range(6, 26))

circuits_sim = [
    # We have only two qubit gates, as those test how well the
    # transpiler can optimize the circuit.
    random_circuit(
        num_qubits=n,
        depth=depth_sim,
        max_operands=2,
        num_operand_distribution={2: 1},
        seed=seed + i,
    )
    for i, n in enumerate(qubit_range_sim)
]

print(
    f"Created {len(circuits_sim)} circuits with qubit counts "
    f"from {qubit_range_sim[0]} to {qubit_range_sim[-1]}"
)
circuits_sim[0].draw(output="mpl", fold=-1)

Output:

Created 20 circuits with qubit counts from 6 to 25
Output of the previous code cell

Schritt 2: Optimierung des Problems für die Ausführung auf Quantenhardware

Wir erstellen den Standard-Pass-Manager (SABRE) für das ausgewählte Backend. Beide Transpilierungsstrategien zielen auf die vollständige Kopplungskarte des Backends ab. Die lokale Simulation bleibt auch später überschaubar, da im Simulationsschritt jede transpilierte Schaltung so remap_to_contiguous umbenannt wird, dass sie nur noch ihre aktiven Qubits umfasst, sodass Aer nur diese Qubits simuliert und nicht das gesamte Bauelement.

service = QiskitRuntimeService()
backend = service.least_busy(
    min_num_qubits=100, operational=True, simulator=False
)


pm_default_sim = generate_preset_pass_manager(
    optimization_level=3,
    backend=backend,
    seed_transpiler=seed,
)
results_sim = []

for i, qc in enumerate(circuits_sim):
    n = qubit_range_sim[i]

    qc_default, m_default = transpile_with_metrics(pm_default_sim, qc)

    # Create a fresh AI pass manager each iteration to avoid stale layout state
    pm_ai = generate_ai_pass_manager(
        optimization_level=1,
        ai_optimization_level=3,
        backend=backend,
    )
    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)

    results_sim.append(
        {
            "Qubits": n,
            "Depth 2Q (Default)": m_default["depth_2q"],
            "Depth 2Q (AI)": m_ai["depth_2q"],
            "Gate Count (Default)": m_default["gate_count"],
            "Gate Count (AI)": m_ai["gate_count"],
            "Time (Default)": m_default["time_s"],
            "Time (AI)": m_ai["time_s"],
        }
    )

print_summary(results_sim)

Output:

Fetching 4 files:   0%|          | 0/4 [00:00<?, ?it/s]
Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement
--------------------------------------------------------------------------------
Depth 2Q               33.0 +/- 12.9          26.4 +/- 8.0      +15.8% +/- 17.6%
Gate Count           522.0 +/- 266.0       560.5 +/- 279.1        -9.0% +/- 9.0%
Time (s)                 0.0 +/- 0.0           0.2 +/- 0.1    -893.6% +/- 362.9%

Die Übersichtstabelle zeigt den Mittelwert und die Standardabweichung jeder Kennzahl über alle 20 Schaltkreise hinweg sowie die durchschnittliche prozentuale Verbesserung des KI-gestützten Transpilers gegenüber der Standardeinstellung. Positive Werte zeigen an, dass der KI-gestützte Transpiler bessere Ergebnisse erzielt hat; negative Werte deuten darauf hin, dass die Standardeinstellung besser war.

In diesem Beispiel im kleinen Maßstab erreicht der KI-gestützte Transpiler im Durchschnitt eine um etwa 16 % geringere Zwei-Qubit-Tiefe, allerdings auf Kosten einer um etwa 9 % höheren Gatteranzahl. Dies verdeutlicht einen entscheidenden Kompromiss bei der Wahl zwischen den beiden Strategien: Der KI-gestützte Transpiler legt den Schwerpunkt auf die Reduzierung der Tiefe (weniger aufeinanderfolgende Schichten aus Zwei-Qubit-Gattern), während der Standard-Transpiler (SABRE) die Minimierung der Gesamtzahl der Gatter (weniger SWAP-Einfügungen) in den Vordergrund stellt. Je nach Anwendungsfall kann eine Kennzahl wichtiger sein als die andere.

plot_metrics_and_pct(results_sim, "Small-Scale Random Circuits")

Output:

Output of the previous code cell Output of the previous code cell

Zwei-Qubit-Tiefe: Der KI-gestützte Transpiler erzeugt in der Regel Schaltungen mit geringerer Zwei-Qubit-Tiefe. Die Tiefe ist eine der wichtigsten Kennzahlen, auf deren Optimierung das KI-Routing-Modell trainiert wurde, und die Verbesserung ist bei den meisten Schaltungsgrößen erkennbar, auch wenn SABRE bei einzelnen Schaltungen gleichwertige oder bessere Ergebnisse erzielt.

Anzahl der Gatter: Bei dieser Größenordnung liegen die Ergebnisse sehr nah beieinander, wobei SABRE insgesamt einen leichten Vorsprung hat. Die Routing-Heuristik von SABRE ist darauf ausgelegt, die Anzahl der eingefügten SWAP-Gatter zu minimieren, was direkt zu einer Verringerung der Gatteranzahl führt. Bei kleinen Schaltungsgrößen ist der Unterschied gering.

Transpiliierungszeit: Die Laufzeit von SABRE ist unabhängig von der Anzahl der Qubits nahezu konstant, sodass die Schaltkreisgröße in diesem Maßstab kaum Auswirkungen auf die Transpiliierungszeit hat. Die zentrale Routing-Logik von SABRE ist hochgradig optimiert (und größtenteils in Rust implementiert). Der KI-gestützte Transpiler benötigt deutlich mehr Zeit und die Laufzeit steigt mit der Schaltungsgröße, wobei die absoluten Laufzeiten für den interaktiven Einsatz jedoch im Rahmen bleiben.

Schritt 3: Mit „ Qiskit primitives “ ausführen

Um den Einfluss der Transpilation auf die Schaltungsgenauigkeit zu bewerten, erstellen Sie Spiegelschaltungen ausgehend vom 10-Qubit-Fall und führen Sie diese auf dem Aer-Simulator mit einem einfachen Rauschmodell aus. Die erwartete Ausgabe einer Spiegel-Schaltung ist immer die Bitzfolge „all-zeros“, sodass die Wahrscheinlichkeit, „ ∣0⟩⊗n|0\rangle^{\otimes n} “ zu messen, zeigt, wie gut die jeweilige Transpilationsstrategie die Treue bewahrt.

# Use the 10-qubit circuit (index where qubits == 10)
idx_10q = qubit_range_sim.index(10)

qc_10q = circuits_sim[idx_10q]
qc_default_10q, _ = transpile_with_metrics(pm_default_sim, qc_10q)

pm_ai = generate_ai_pass_manager(
    optimization_level=1,
    ai_optimization_level=3,
    backend=backend,
)
qc_ai_10q, _ = transpile_with_metrics(pm_ai, qc_10q)

tqc_methods = {
    "Default": qc_default_10q,
    "AI": qc_ai_10q,
}

print(
    f"Default: depth {qc_default_10q.depth()}, gates {qc_default_10q.size()}"
)
print(f"AI:      depth {qc_ai_10q.depth()}, gates {qc_ai_10q.size()}")

Output:

Default: depth 84, gates 280
AI:      depth 91, gates 343
# Build a simple depolarizing noise model
noise_model = NoiseModel()
noise_model.add_all_qubit_quantum_error(
    depolarizing_error(0.001, 1),
    ["sx", "x", "rz"],  # ~0.1% per 1Q gate
)
noise_model.add_all_qubit_quantum_error(
    depolarizing_error(0.01, 2),
    ["cx", "ecr"],  # ~1% per 2Q gate
)

aer_sim = AerSimulator(noise_model=noise_model)

shots = 10000
survival_probs = {}

for method, tqc in tqc_methods.items():
    mirror = build_mirror_circuit(tqc, simulate=True)

    sampler = SamplerV2(mode=aer_sim)
    job = sampler.run([mirror], shots=shots)
    counts = job.result()[0].data.meas.get_counts()

    all_zeros = "0" * mirror.num_qubits
    survival = counts.get(all_zeros, 0) / shots
    survival_probs[method] = survival
    print(
        f"{method:8s}  P(|00...0>) = {survival:.4f}  "
        f"({counts.get(all_zeros, 0)}/{shots})"
    )

Output:

Default   P(|00...0>) = 0.8460  (8460/10000)
AI        P(|00...0>) = 0.8121  (8121/10000)

Wir haben beide Spiegelkreise mit einem einfachen Modell für depolarisierendes Rauschen im Aer-Simulator durchgespielt. Die Überlebenswahrscheinlichkeit, definiert als der Anteil der Durchläufe, bei denen die Bitfolge „nur Nullen“ zurückgegeben wird, quantifiziert, wie viel Rauschen die jeweilige Transpilationsstrategie einbringt.

Schritt 4: Nachbearbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format

Wir berechnen die Wahrscheinlichkeit, dass bei beiden Durchläufen die Bitfolge, die ausschließlich aus Nullen besteht, gemessen wird. Eine höhere Überlebenswahrscheinlichkeit deutet auf eine höhere Genauigkeit hin, was bedeutet, dass bei der Transpilierung weniger Rauschen entstanden ist. Die folgende Grafik zeigt das Komplement, 1 - P(|0...0>), sodass ein tieferer Balken eine höhere Wiedergabetreue anzeigt und kleine Fehlerunterschiede leichter zu erkennen sind.

# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.
# A lower bar means the transpilation introduced less noise.
error_probs = {method: 1 - p for method, p in survival_probs.items()}

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(
    error_probs.keys(),
    error_probs.values(),
    color=["steelblue", "coral"],
)
ax.set_ylabel("1 - P(|0...0>)")
ax.set_title("Mirror Circuit Error (10-qubit, Aer Simulator)")
ax.set_ylim(0, 1)
plt.tight_layout()
plt.show()

Output:

Output of the previous code cell

In diesem Fall hat der Standard-Transpiler für diese spezielle 10-Qubit-Instanz sowohl eine flachere als auch eine kleinere Schaltung erzeugt, sodass eine höhere Genauigkeit zu erwarten ist. Die Ergebnisse variieren je nach Schaltung: Wie die obige Übersichtstabelle zeigt, liegt der Vorteil des KI-gestützten Transpilers im Durchschnitt in einer geringeren Zwei-Qubit-Tiefe, nicht jedoch bei jeder einzelnen Schaltung. Welche Strategie eine höhere Genauigkeit liefert, hängt von der Größe der Abweichung bei den einzelnen Messgrößen, den Rauscheigenschaften der Hardware und der Schaltungsstruktur ab. Bei einem einheitlichen depolarisierenden Rauschmodell hat die Gesamtzahl der Gates oft einen direkteren Einfluss auf den kumulierten Fehler als die Tiefe allein.


Beispiel für groß angelegte Hardware

Schritte 1–4

Hier werden all diese Details zu einem übersichtlichen Arbeitsablauf in größerem Maßstab zusammengefasst, der dann auf echter Quantenhardware ausgeführt wird.

Der folgende Code generiert 25 zufällige Schaltkreise mit einer Tiefe von 8, wobei die Anzahl der Qubits zwischen 26 und 50 liegt. Diese Schaltkreise werden anschließend mit beiden Strategien transpiliert, und es werden dieselben Metriken erfasst. Anschließend erstellen wir Spiegel-Schaltungen auf der Grundlage des 26-Qubit-Falls und übermitteln diese an das eigentliche Backend.

# -------------------------Step 1-------------------------
num_circuits_hw = 25
depth_hw = 8
qubit_range_hw = list(range(26, 51))

circuits_hw = [
    # We have only two qubit gates, as those test how well the
    # transpiler can optimize the circuit.
    random_circuit(
        num_qubits=n,
        depth=depth_hw,
        max_operands=2,
        num_operand_distribution={2: 1},
        seed=seed + i,
    )
    for i, n in enumerate(qubit_range_hw)
]

print(
    f"Created {len(circuits_hw)} circuits with qubit counts "
    f"from {qubit_range_hw[0]} to {qubit_range_hw[-1]}"
)

Output:

Created 25 circuits with qubit counts from 26 to 50
# -------------------------Step 2-------------------------
pm_default = generate_preset_pass_manager(
    optimization_level=3,
    backend=backend,
    seed_transpiler=seed,
)

results_hw = []

for i, qc in enumerate(circuits_hw):
    n = qubit_range_hw[i]

    qc_default, m_default = transpile_with_metrics(pm_default, qc)

    # Create a fresh AI pass manager each iteration to avoid stale layout state
    pm_ai = generate_ai_pass_manager(
        optimization_level=1,
        ai_optimization_level=3,
        backend=backend,
    )
    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)

    results_hw.append(
        {
            "Qubits": n,
            "Depth 2Q (Default)": m_default["depth_2q"],
            "Depth 2Q (AI)": m_ai["depth_2q"],
            "Gate Count (Default)": m_default["gate_count"],
            "Gate Count (AI)": m_ai["gate_count"],
            "Time (Default)": m_default["time_s"],
            "Time (AI)": m_ai["time_s"],
        }
    )

print_summary(results_hw)

Output:

Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement
--------------------------------------------------------------------------------
Depth 2Q              217.4 +/- 50.4        191.0 +/- 35.6      +10.9% +/- 10.7%
Gate Count         4513.3 +/- 1394.3     5227.1 +/- 1536.4       -16.4% +/- 5.8%
Time (s)                 0.1 +/- 0.0           3.5 +/- 1.5   -3588.2% +/- 643.6%
plot_metrics_and_pct(results_hw, "Large-Scale Random Circuits")

Output:

Output of the previous code cell Output of the previous code cell
# -------------------------Step 3-------------------------
# Build mirror circuits from the 26-qubit case
idx_26q = qubit_range_hw.index(26)

qc_26q = circuits_hw[idx_26q]
qc_default_26q, _ = transpile_with_metrics(pm_default, qc_26q)

pm_ai = generate_ai_pass_manager(
    optimization_level=1,
    ai_optimization_level=3,
    backend=backend,
)
qc_ai_26q, _ = transpile_with_metrics(pm_ai, qc_26q)

mirror_default_hw = build_mirror_circuit(qc_default_26q, simulate=False)
mirror_ai_hw = build_mirror_circuit(qc_ai_26q, simulate=False)

# Re-transpile to basis gates (the inverse can introduce gates like sxdg)
pm_basis = generate_preset_pass_manager(
    optimization_level=0,
    backend=backend,
)
mirror_default_hw = pm_basis.run(mirror_default_hw)
mirror_ai_hw = pm_basis.run(mirror_ai_hw)

print(
    f"Mirror circuit (Default): depth {mirror_default_hw.depth()}, "
    f"gates {mirror_default_hw.size()}"
)
print(
    f"Mirror circuit (AI):      depth {mirror_ai_hw.depth()}, "
    f"gates {mirror_ai_hw.size()}"
)

# Submit to real hardware
sampler_hw = SamplerV2(mode=backend)
sampler_hw.options.environment.job_tags = ["TUT_AITI"]

shots_hw = 500000
job_hw = sampler_hw.run([mirror_default_hw, mirror_ai_hw], shots=shots_hw)
print(f"Job submitted: {job_hw.job_id()}")

Output:

Mirror circuit (Default): depth 1577, gates 9672
Mirror circuit (AI):      depth 1235, gates 11092
Job submitted: d8gt7vm6983c73dqbg0g
# -------------------------Step 4-------------------------
result_hw = job_hw.result()

survival_probs_hw = {}
for i, method in enumerate(["Default", "AI"]):
    counts = result_hw[i].data.meas.get_counts()
    mirror = [mirror_default_hw, mirror_ai_hw][i]
    all_zeros = "0" * mirror.num_qubits
    survival = counts.get(all_zeros, 0) / shots_hw
    survival_probs_hw[method] = survival
    print(
        f"{method:8s}  P(|00...0>) = {survival:.4f}  "
        f"({counts.get(all_zeros, 0)}/{shots_hw})"
    )

# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.
# A lower bar means the transpilation introduced less noise.
error_probs_hw = {method: 1 - p for method, p in survival_probs_hw.items()}

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(
    error_probs_hw.keys(),
    error_probs_hw.values(),
    color=["steelblue", "coral"],
)
ax.set_ylabel("1 - P(|0...0>)")
ax.set_title(f"Mirror Circuit Error (26-qubit, {backend.name})")
ax.set_ylim(0, 1)
plt.tight_layout()
plt.show()

Output:

Default   P(|00...0>) = 0.0005  (239/500000)
AI        P(|00...0>) = 0.0050  (2516/500000)
Output of the previous code cell

Auswertung der Ergebnisse

Die Ergebnisse aus der großräumigen Untersuchung bestätigen die im kleinräumigen Beispiel beobachteten Trends, nun in einem anspruchsvolleren Maßstab.

Zwei-Qubit-Tiefe: Der KI-gestützte Transpiler weist über den gesamten Bereich der Schaltungsgrößen hinweg weiterhin eine deutlich geringere Zwei-Qubit-Tiefe auf. Die Tiefenoptimierung ist eines der Hauptziele, auf die das KI-Routing-Modell trainiert wird, und der Vorteil kommt bei einer höheren Qubit-Anzahl deutlicher zum Tragen, da das Routing-Problem für heuristische Methoden dort schwieriger wird.

Gatteranzahl: Der Standard-Transpiler (SABRE) erzeugt über alle Schaltungsgrößen in diesem Bereich hinweg durchweg Schaltungen mit einer geringeren Gatteranzahl. Die Heuristik von SABRE ist speziell darauf ausgelegt, die Anzahl der Gatter zu minimieren, und in dieser Größenordnung ist der Vorteil eindeutig und durchgängig.

Transpilationszeit: Der Unterschied in der Transpilationszeit vergrößert sich bei größeren Maßstäben. SABRE bleibt nahezu konstant, während die Laufzeit des KI-gestützten Transpilers stärker ansteigt. Dennoch ist die Laufzeit des KI-gestützten Transpilers für die meisten Arbeitsabläufe nach wie vor praktikabel.

Genauigkeit der Spiegel-Schaltung: Beide Methoden liefern bei diesem Maßstab Überlebenswahrscheinlichkeiten von deutlich unter 1 %, sodass kaum ein verwertbares Signal übrig bleibt. Bei einer Gesamtanzahl von etwa 10.000 Gates und einer Tiefe von über 1.000 bei Zwei-Qubit-Operationen überlagert das im Spiegelkreis akkumulierte Depolarisationsrauschen den größten Teil des Signals. Dies verdeutlicht eine wesentliche Einschränkung des Spiegel-Schaltungsansatzes: Er ist zwar einfach und erfordert keine klassische Simulation, lässt sich jedoch nicht gut auf große oder tiefe Schaltungen skalieren, bei denen beide Methoden nahe an die Rauschgrenze stoßen und das schwache, noch vorhandene Signal von kumulierten Fehlern dominiert wird.

Diese Ergebnisse unterstreichen zwar die Wirksamkeit des KI-gestützten Transpilers, doch ist es wichtig, seine Grenzen zu beachten. Das KI-Syntheseverfahren steht derzeit nur für bestimmte Kopplungskarten zur Verfügung, was seine breitere Anwendbarkeit einschränken könnte. Diese Einschränkung sollte bei der Bewertung des Einsatzes in verschiedenen Szenarien berücksichtigt werden.


Nächste Schritte

Empfehlungen

Wenn Sie diese Arbeit interessant fanden, könnten Sie auch an folgenden Materialien interessiert sein:

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