{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "meta-header",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Einführung in den Qiskit-Transpiler mit KI-Unterstützung\"\n",
        "description: \"Erfahren Sie, wie sich der KI-gestützte Transpiler im Vergleich zur herkömmlichen Transpilierung mit Spiegel-Schaltungen schlägt.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize fontweight steelblue AITI */}\n",
        "\n",
        "<span id=\"qiskit-ai-powered-transpiler-introduction\" />\n",
        "\n",
        "# Einführung in den Qiskit-Transpiler mit KI-Unterstützung\n",
        "\n",
        "*Geschätzte Nutzungsdauer: 5 Minuten auf IBM Heron (HINWEIS: Dies ist lediglich eine Schätzung. (Die Laufzeit kann variieren.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "learning-outcomes",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Lernziele\n",
        "\n",
        "* So verwenden Sie den KI-gestützten Transpiler (`generate_ai_pass_manager`) als direkten Ersatz für den Standard-Transpiler\n",
        "* Wie schneidet der KI-gestützte Transpiler im Vergleich zum Standard-Transpiler hinsichtlich der Zwei-Qubit-Tiefe, der Gatteranzahl und der Transpilierungszeit ab?\n",
        "* Wie man Spiegel-Schaltkreise nutzt, um die Qualität der Transpilierung durch Hardware-Ausführung zu bewerten\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Voraussetzungen\n",
        "\n",
        "* [Schaltungen transpilieren](/docs/guides/transpile)\n",
        "* [Voreingestellte Pass-Manager konfigurieren](/docs/guides/transpile-with-pass-managers)\n",
        "* [Der KI-gestützte Transpiler besteht die Prüfung](/docs/guides/ai-transpiler-passes)\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Hintergrund\n",
        "\n",
        "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.\n",
        "\n",
        "In diesem Tutorial vergleichen wir zwei Transpilierungsstrategien:\n",
        "\n",
        "| Strategie    | -API                                                                           |\n",
        "| ------------ | ------------------------------------------------------------------------------ |\n",
        "| **Standard** | `generate_preset_pass_manager(optimization_level=3, ...)`                      |\n",
        "| **KI**       | `generate_ai_pass_manager(optimization_level=1, ai_optimization_level=3, ...)` |\n",
        "\n",
        "Für jede Strategie messen wir drei Kennzahlen: **die Tiefe der Zwei-Qubit-Gatter**, **die Gesamtzahl der Gatter** und **die Laufzeit der Transpilierung**.\n",
        "\n",
        "<span id=\"ai-powered-transpiler-benchmarks\" />\n",
        "\n",
        "### Benchmarks für KI-gestützte Transpiler\n",
        "\n",
        "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`](/docs/api/qiskit/qiskit.transpiler.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](https://www.ibm.com/quantum/blog/qiskit-performance).\n",
        "\n",
        "![Benchmarks für KI-gestützte Transpiler](https://quantum.cloud.ibm.com/docs/images/tutorials/ai-transpiler-introduction/ai-transpiler-benchmarks.avif)\n",
        "\n",
        "In diesem Tutorial werden die wichtigsten Vorteile von KI-Pässen erläutert und ein Vergleich mit herkömmlichen Methoden angestellt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "requirements",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Anforderungen\n",
        "\n",
        "Bevor Sie mit dieser Anleitung beginnen, stellen Sie bitte sicher, dass Folgendes installiert ist:\n",
        "\n",
        "* Qiskit SDK v2.0 oder höher, mit [Visual](/docs/api/qiskit/visualization) isierungsunterstützung\n",
        "* Qiskit Runtime `pip install qiskit-ibm-runtime`v0.22 oder höher\n",
        "* Qiskit- IBM -Transpiler mit lokalem KI-Modus (`pip install 'qiskit-ibm-transpiler[ai-local-mode]'`)\n",
        "* Qiskit Aer (`pip install qiskit-aer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "setup-header",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Einrichtung\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "setup-code",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.circuit.random import random_circuit\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n",
        "from qiskit_ibm_transpiler import generate_ai_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel, depolarizing_error\n",
        "import matplotlib.pyplot as plt\n",
        "from statistics import mean, stdev\n",
        "import time\n",
        "import logging\n",
        "\n",
        "seed = 42\n",
        "\n",
        "\n",
        "def transpile_with_metrics(pass_manager, circuit):\n",
        "    \"\"\"Transpile a circuit and return the result along with key metrics.\"\"\"\n",
        "    start = time.time()\n",
        "    qc_out = pass_manager.run(circuit)\n",
        "    elapsed = time.time() - start\n",
        "\n",
        "    depth_2q = qc_out.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    gate_count = qc_out.size()\n",
        "\n",
        "    return qc_out, {\n",
        "        \"depth_2q\": depth_2q,\n",
        "        \"gate_count\": gate_count,\n",
        "        \"time_s\": round(elapsed, 3),\n",
        "    }\n",
        "\n",
        "\n",
        "def remap_to_contiguous(tqc):\n",
        "    \"\"\"Remap a transpiled circuit to use contiguous qubit indices.\n",
        "\n",
        "    Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)\n",
        "    on a large backend. This remaps them to 0, 1, 2, ... so Aer only\n",
        "    simulates the active qubits.\"\"\"\n",
        "    active = sorted(\n",
        "        {tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}\n",
        "    )\n",
        "    qubit_map = {old: new for new, old in enumerate(active)}\n",
        "    new_qc = QuantumCircuit(len(active))\n",
        "    for inst in tqc.data:\n",
        "        old_indices = [tqc.find_bit(q).index for q in inst.qubits]\n",
        "        new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])\n",
        "    return new_qc\n",
        "\n",
        "\n",
        "def build_mirror_circuit(tqc, simulate=True):\n",
        "    \"\"\"Build a mirror circuit: U followed by U-dagger, with measurements.\n",
        "\n",
        "    The expected output is always |0...0>, so measuring the survival\n",
        "    probability reveals how much noise each transpilation strategy adds.\n",
        "\n",
        "    Args:\n",
        "        tqc: A transpiled circuit.\n",
        "        simulate: If True (default), remap to contiguous qubits so Aer\n",
        "            only simulates the active qubits. If False, keep the full\n",
        "            physical layout for hardware execution.\"\"\"\n",
        "    if simulate:\n",
        "        tqc = remap_to_contiguous(tqc)\n",
        "    mirror = tqc.compose(tqc.inverse())\n",
        "    mirror.measure_all()\n",
        "    return mirror\n",
        "\n",
        "\n",
        "def print_summary(results):\n",
        "    \"\"\"Print a summary of each metric as mean +/- stdev across all circuits,\n",
        "    along with the mean percentage improvement of AI over Default.\"\"\"\n",
        "    metrics = [\n",
        "        (\"Depth 2Q\", \"Depth 2Q (Default)\", \"Depth 2Q (AI)\"),\n",
        "        (\"Gate Count\", \"Gate Count (Default)\", \"Gate Count (AI)\"),\n",
        "        (\"Time (s)\", \"Time (Default)\", \"Time (AI)\"),\n",
        "    ]\n",
        "    header = (\n",
        "        f\"{'Metric':<12}{'Default (mean +/- std)':>24}\"\n",
        "        f\"{'AI (mean +/- std)':>22}{'AI % improvement':>22}\"\n",
        "    )\n",
        "    print(header)\n",
        "    print(\"-\" * len(header))\n",
        "    for label, col_def, col_ai in metrics:\n",
        "        defaults = [r[col_def] for r in results]\n",
        "        ais = [r[col_ai] for r in results]\n",
        "        pct = [(d - a) / d * 100 for d, a in zip(defaults, ais)]\n",
        "        default_str = f\"{mean(defaults):.1f} +/- {stdev(defaults):.1f}\"\n",
        "        ai_str = f\"{mean(ais):.1f} +/- {stdev(ais):.1f}\"\n",
        "        pct_str = f\"{mean(pct):+.1f}% +/- {stdev(pct):.1f}%\"\n",
        "        print(f\"{label:<12}{default_str:>24}{ai_str:>22}{pct_str:>22}\")\n",
        "\n",
        "\n",
        "def plot_metrics_and_pct(results, title_prefix):\n",
        "    \"\"\"Plot metric comparisons and percentage improvement of AI over Default.\"\"\"\n",
        "    qubits = [r[\"Qubits\"] for r in results]\n",
        "    metrics = [\n",
        "        (\"Depth 2Q (Default)\", \"Depth 2Q (AI)\", \"Two-Qubit Depth\"),\n",
        "        (\"Gate Count (Default)\", \"Gate Count (AI)\", \"Gate Count\"),\n",
        "        (\"Time (Default)\", \"Time (AI)\", \"Transpilation Time\"),\n",
        "    ]\n",
        "\n",
        "    # Row 1: raw metric comparison\n",
        "    fig, axs = plt.subplots(1, 3, figsize=(21, 5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: Metric Comparison\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "    for ax, (col_def, col_ai, label) in zip(axs, metrics):\n",
        "        ax.plot(qubits, [r[col_def] for r in results], \"o-\", label=\"Default\")\n",
        "        ax.plot(qubits, [r[col_ai] for r in results], \"s-\", label=\"AI\")\n",
        "        ax.set_title(label)\n",
        "        ax.set_xlabel(\"Number of Qubits\")\n",
        "        ax.set_ylabel(label)\n",
        "        ax.legend()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "    # Row 2: percentage improvement\n",
        "    fig, axs = plt.subplots(1, 3, figsize=(21, 5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: % Improvement of AI over Default\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "    for ax, (col_def, col_ai, label) in zip(axs, metrics):\n",
        "        pct = [(r[col_def] - r[col_ai]) / r[col_def] * 100 for r in results]\n",
        "        ax.axhline(\n",
        "            0, color=\"#1f77b4\", linewidth=2, label=\"Default (baseline)\"\n",
        "        )\n",
        "        ax.plot(qubits, pct, \"s-\", color=\"#ff7f0e\", label=\"AI\")\n",
        "        ax.fill_between(qubits, 0, pct, alpha=0.15, color=\"#ff7f0e\")\n",
        "        ax.set_title(label)\n",
        "        ax.set_xlabel(\"Number of Qubits\")\n",
        "        ax.set_ylabel(\"% Improvement\")\n",
        "        ax.legend()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "# Suppress verbose AI-powered transpiler logs\n",
        "logging.getLogger(\n",
        "    \"qiskit_ibm_transpiler.wrappers.ai_local_synthesis\"\n",
        ").setLevel(logging.WARNING)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-header",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Beispiel für einen Simulator im kleinen Maßstab\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step1-header",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen\n",
        "\n",
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "sim-step1-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created 20 circuits with qubit counts from 6 to 25\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step1-code-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_circuits_sim = 20\n",
        "depth_sim = 4\n",
        "qubit_range_sim = list(range(6, 26))\n",
        "\n",
        "circuits_sim = [\n",
        "    # We have only two qubit gates, as those test how well the\n",
        "    # transpiler can optimize the circuit.\n",
        "    random_circuit(\n",
        "        num_qubits=n,\n",
        "        depth=depth_sim,\n",
        "        max_operands=2,\n",
        "        num_operand_distribution={2: 1},\n",
        "        seed=seed + i,\n",
        "    )\n",
        "    for i, n in enumerate(qubit_range_sim)\n",
        "]\n",
        "\n",
        "print(\n",
        "    f\"Created {len(circuits_sim)} circuits with qubit counts \"\n",
        "    f\"from {qubit_range_sim[0]} to {qubit_range_sim[-1]}\"\n",
        ")\n",
        "circuits_sim[0].draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-header",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Schritt 2: Optimierung des Problems für die Ausführung auf Quantenhardware\n",
        "\n",
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "sim-step2-backend",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    min_num_qubits=100, operational=True, simulator=False\n",
        ")\n",
        "\n",
        "\n",
        "pm_default_sim = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    seed_transpiler=seed,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "sim-step2-transpile",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "bc06de12e386408abf67166416c2d1a0",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Fetching 4 files:   0%|          | 0/4 [00:00<?, ?it/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement\n",
            "--------------------------------------------------------------------------------\n",
            "Depth 2Q               33.0 +/- 12.9          26.4 +/- 8.0      +15.8% +/- 17.6%\n",
            "Gate Count           522.0 +/- 266.0       560.5 +/- 279.1        -9.0% +/- 9.0%\n",
            "Time (s)                 0.0 +/- 0.0           0.2 +/- 0.1    -893.6% +/- 362.9%\n"
          ]
        }
      ],
      "source": [
        "results_sim = []\n",
        "\n",
        "for i, qc in enumerate(circuits_sim):\n",
        "    n = qubit_range_sim[i]\n",
        "\n",
        "    qc_default, m_default = transpile_with_metrics(pm_default_sim, qc)\n",
        "\n",
        "    # Create a fresh AI pass manager each iteration to avoid stale layout state\n",
        "    pm_ai = generate_ai_pass_manager(\n",
        "        optimization_level=1,\n",
        "        ai_optimization_level=3,\n",
        "        backend=backend,\n",
        "    )\n",
        "    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)\n",
        "\n",
        "    results_sim.append(\n",
        "        {\n",
        "            \"Qubits\": n,\n",
        "            \"Depth 2Q (Default)\": m_default[\"depth_2q\"],\n",
        "            \"Depth 2Q (AI)\": m_ai[\"depth_2q\"],\n",
        "            \"Gate Count (Default)\": m_default[\"gate_count\"],\n",
        "            \"Gate Count (AI)\": m_ai[\"gate_count\"],\n",
        "            \"Time (Default)\": m_default[\"time_s\"],\n",
        "            \"Time (AI)\": m_ai[\"time_s\"],\n",
        "        }\n",
        "    )\n",
        "\n",
        "print_summary(results_sim)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-table-note",
      "metadata": {},
      "source": [
        "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.\n",
        "\n",
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "sim-step2-plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step2-plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step2-plot-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_metrics_and_pct(results_sim, \"Small-Scale Random Circuits\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-analysis",
      "metadata": {},
      "source": [
        "**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.\n",
        "\n",
        "**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.\n",
        "\n",
        "**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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step3-header",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Schritt 3: Mit „ Qiskit primitives “ ausführen\n",
        "\n",
        "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\\rangle^{\\otimes n}$ “ zu messen, zeigt, wie gut die jeweilige Transpilationsstrategie die Treue bewahrt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "sim-step3-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default: depth 84, gates 280\n",
            "AI:      depth 91, gates 343\n"
          ]
        }
      ],
      "source": [
        "# Use the 10-qubit circuit (index where qubits == 10)\n",
        "idx_10q = qubit_range_sim.index(10)\n",
        "\n",
        "qc_10q = circuits_sim[idx_10q]\n",
        "qc_default_10q, _ = transpile_with_metrics(pm_default_sim, qc_10q)\n",
        "\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    optimization_level=1,\n",
        "    ai_optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "qc_ai_10q, _ = transpile_with_metrics(pm_ai, qc_10q)\n",
        "\n",
        "tqc_methods = {\n",
        "    \"Default\": qc_default_10q,\n",
        "    \"AI\": qc_ai_10q,\n",
        "}\n",
        "\n",
        "print(\n",
        "    f\"Default: depth {qc_default_10q.depth()}, gates {qc_default_10q.size()}\"\n",
        ")\n",
        "print(f\"AI:      depth {qc_ai_10q.depth()}, gates {qc_ai_10q.size()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "sim-step3-run",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default   P(|00...0>) = 0.8460  (8460/10000)\n",
            "AI        P(|00...0>) = 0.8121  (8121/10000)\n"
          ]
        }
      ],
      "source": [
        "# Build a simple depolarizing noise model\n",
        "noise_model = NoiseModel()\n",
        "noise_model.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(0.001, 1),\n",
        "    [\"sx\", \"x\", \"rz\"],  # ~0.1% per 1Q gate\n",
        ")\n",
        "noise_model.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(0.01, 2),\n",
        "    [\"cx\", \"ecr\"],  # ~1% per 2Q gate\n",
        ")\n",
        "\n",
        "aer_sim = AerSimulator(noise_model=noise_model)\n",
        "\n",
        "shots = 10000\n",
        "survival_probs = {}\n",
        "\n",
        "for method, tqc in tqc_methods.items():\n",
        "    mirror = build_mirror_circuit(tqc, simulate=True)\n",
        "\n",
        "    sampler = SamplerV2(mode=aer_sim)\n",
        "    job = sampler.run([mirror], shots=shots)\n",
        "    counts = job.result()[0].data.meas.get_counts()\n",
        "\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    survival = counts.get(all_zeros, 0) / shots\n",
        "    survival_probs[method] = survival\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {survival:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step3-analysis",
      "metadata": {},
      "source": [
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step4-header",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Schritt 4: Nachbearbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format\n",
        "\n",
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "sim-step4-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step4-code-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.\n",
        "# A lower bar means the transpilation introduced less noise.\n",
        "error_probs = {method: 1 - p for method, p in survival_probs.items()}\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.bar(\n",
        "    error_probs.keys(),\n",
        "    error_probs.values(),\n",
        "    color=[\"steelblue\", \"coral\"],\n",
        ")\n",
        "ax.set_ylabel(\"1 - P(|0...0>)\")\n",
        "ax.set_title(\"Mirror Circuit Error (10-qubit, Aer Simulator)\")\n",
        "ax.set_ylim(0, 1)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step4-analysis",
      "metadata": {},
      "source": [
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-header",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Beispiel für groß angelegte Hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-steps-header",
      "metadata": {},
      "source": [
        "<span id=\"steps-1-4\" />\n",
        "\n",
        "### Schritte 1–4\n",
        "\n",
        "Hier werden all diese Details zu einem übersichtlichen Arbeitsablauf in größerem Maßstab zusammengefasst, der dann auf echter Quantenhardware ausgeführt wird.\n",
        "\n",
        "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.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "hw-step1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created 25 circuits with qubit counts from 26 to 50\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "num_circuits_hw = 25\n",
        "depth_hw = 8\n",
        "qubit_range_hw = list(range(26, 51))\n",
        "\n",
        "circuits_hw = [\n",
        "    # We have only two qubit gates, as those test how well the\n",
        "    # transpiler can optimize the circuit.\n",
        "    random_circuit(\n",
        "        num_qubits=n,\n",
        "        depth=depth_hw,\n",
        "        max_operands=2,\n",
        "        num_operand_distribution={2: 1},\n",
        "        seed=seed + i,\n",
        "    )\n",
        "    for i, n in enumerate(qubit_range_hw)\n",
        "]\n",
        "\n",
        "print(\n",
        "    f\"Created {len(circuits_hw)} circuits with qubit counts \"\n",
        "    f\"from {qubit_range_hw[0]} to {qubit_range_hw[-1]}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "hw-step2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement\n",
            "--------------------------------------------------------------------------------\n",
            "Depth 2Q              217.4 +/- 50.4        191.0 +/- 35.6      +10.9% +/- 10.7%\n",
            "Gate Count         4513.3 +/- 1394.3     5227.1 +/- 1536.4       -16.4% +/- 5.8%\n",
            "Time (s)                 0.1 +/- 0.0           3.5 +/- 1.5   -3588.2% +/- 643.6%\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 2-------------------------\n",
        "pm_default = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    seed_transpiler=seed,\n",
        ")\n",
        "\n",
        "results_hw = []\n",
        "\n",
        "for i, qc in enumerate(circuits_hw):\n",
        "    n = qubit_range_hw[i]\n",
        "\n",
        "    qc_default, m_default = transpile_with_metrics(pm_default, qc)\n",
        "\n",
        "    # Create a fresh AI pass manager each iteration to avoid stale layout state\n",
        "    pm_ai = generate_ai_pass_manager(\n",
        "        optimization_level=1,\n",
        "        ai_optimization_level=3,\n",
        "        backend=backend,\n",
        "    )\n",
        "    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)\n",
        "\n",
        "    results_hw.append(\n",
        "        {\n",
        "            \"Qubits\": n,\n",
        "            \"Depth 2Q (Default)\": m_default[\"depth_2q\"],\n",
        "            \"Depth 2Q (AI)\": m_ai[\"depth_2q\"],\n",
        "            \"Gate Count (Default)\": m_default[\"gate_count\"],\n",
        "            \"Gate Count (AI)\": m_ai[\"gate_count\"],\n",
        "            \"Time (Default)\": m_default[\"time_s\"],\n",
        "            \"Time (AI)\": m_ai[\"time_s\"],\n",
        "        }\n",
        "    )\n",
        "\n",
        "print_summary(results_hw)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "hw-step2-plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step2-plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step2-plot-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_metrics_and_pct(results_hw, \"Large-Scale Random Circuits\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "hw-step3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Mirror circuit (Default): depth 1577, gates 9672\n",
            "Mirror circuit (AI):      depth 1235, gates 11092\n",
            "Job submitted: d8gt7vm6983c73dqbg0g\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 3-------------------------\n",
        "# Build mirror circuits from the 26-qubit case\n",
        "idx_26q = qubit_range_hw.index(26)\n",
        "\n",
        "qc_26q = circuits_hw[idx_26q]\n",
        "qc_default_26q, _ = transpile_with_metrics(pm_default, qc_26q)\n",
        "\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    optimization_level=1,\n",
        "    ai_optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "qc_ai_26q, _ = transpile_with_metrics(pm_ai, qc_26q)\n",
        "\n",
        "mirror_default_hw = build_mirror_circuit(qc_default_26q, simulate=False)\n",
        "mirror_ai_hw = build_mirror_circuit(qc_ai_26q, simulate=False)\n",
        "\n",
        "# Re-transpile to basis gates (the inverse can introduce gates like sxdg)\n",
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=0,\n",
        "    backend=backend,\n",
        ")\n",
        "mirror_default_hw = pm_basis.run(mirror_default_hw)\n",
        "mirror_ai_hw = pm_basis.run(mirror_ai_hw)\n",
        "\n",
        "print(\n",
        "    f\"Mirror circuit (Default): depth {mirror_default_hw.depth()}, \"\n",
        "    f\"gates {mirror_default_hw.size()}\"\n",
        ")\n",
        "print(\n",
        "    f\"Mirror circuit (AI):      depth {mirror_ai_hw.depth()}, \"\n",
        "    f\"gates {mirror_ai_hw.size()}\"\n",
        ")\n",
        "\n",
        "# Submit to real hardware\n",
        "sampler_hw = SamplerV2(mode=backend)\n",
        "sampler_hw.options.environment.job_tags = [\"TUT_AITI\"]\n",
        "\n",
        "shots_hw = 500000\n",
        "job_hw = sampler_hw.run([mirror_default_hw, mirror_ai_hw], shots=shots_hw)\n",
        "print(f\"Job submitted: {job_hw.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "hw-step4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default   P(|00...0>) = 0.0005  (239/500000)\n",
            "AI        P(|00...0>) = 0.0050  (2516/500000)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step4-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# -------------------------Step 4-------------------------\n",
        "result_hw = job_hw.result()\n",
        "\n",
        "survival_probs_hw = {}\n",
        "for i, method in enumerate([\"Default\", \"AI\"]):\n",
        "    counts = result_hw[i].data.meas.get_counts()\n",
        "    mirror = [mirror_default_hw, mirror_ai_hw][i]\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    survival = counts.get(all_zeros, 0) / shots_hw\n",
        "    survival_probs_hw[method] = survival\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {survival:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots_hw})\"\n",
        "    )\n",
        "\n",
        "# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.\n",
        "# A lower bar means the transpilation introduced less noise.\n",
        "error_probs_hw = {method: 1 - p for method, p in survival_probs_hw.items()}\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.bar(\n",
        "    error_probs_hw.keys(),\n",
        "    error_probs_hw.values(),\n",
        "    color=[\"steelblue\", \"coral\"],\n",
        ")\n",
        "ax.set_ylabel(\"1 - P(|0...0>)\")\n",
        "ax.set_title(f\"Mirror Circuit Error (26-qubit, {backend.name})\")\n",
        "ax.set_ylim(0, 1)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-analysis",
      "metadata": {},
      "source": [
        "<span id=\"analysis-of-results\" />\n",
        "\n",
        "### Auswertung der Ergebnisse\n",
        "\n",
        "Die Ergebnisse aus der großräumigen Untersuchung bestätigen die im kleinräumigen Beispiel beobachteten Trends, nun in einem anspruchsvolleren Maßstab.\n",
        "\n",
        "**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.\n",
        "\n",
        "**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.\n",
        "\n",
        "**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.\n",
        "\n",
        "**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.\n",
        "\n",
        "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",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "next-steps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Nächste Schritte\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Empfehlungen\">\n",
        "  Wenn Sie diese Arbeit interessant fanden, könnten Sie auch an folgenden Materialien interessiert sein:\n",
        "\n",
        "  * [Optimierungen bei der Transpilation mit SABRE](/docs/tutorials/transpilation-optimizations-with-sabre)\n",
        "  * [Kompilierungsverfahren für Hamilton-Simulationsschaltungen](/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1,
    "qpuSeconds": 300
  },
  "nbformat": 4,
  "nbformat_minor": 5
}