{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "2abbf9e4",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Quantumphasenschätzung mit dem Q-CTRL- Qiskit Functions\"\n",
        "description: \"Implementierung des Quantenphasen-Schätzalgorithmus auf 35 Qubits unter Verwendung der Q-CTRL Qiskit-Funktion\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore eigenphase topk ylab boxstyle fontsize */}\n",
        "\n",
        "<span id=\"quantum-phase-estimation-with-q-ctrls-qiskit-functions\" />\n",
        "\n",
        "# Quantumphasenschätzung mit dem Q-CTRL- Qiskit Functions\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4170a8f8",
      "metadata": {},
      "source": [
        "*Geschätzte Nutzungsdauer: 40 Sekunden auf einem Heron r2 Prozessor. (HINWEIS: Dies ist nur eine Schätzung. Ihre Laufzeit kann variieren.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b2f4a24e",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Hintergrund\n",
        "\n",
        "Die Quantenphasenschätzung (Quantum Phase Estimation, QPE) ist ein grundlegender Algorithmus in der Quanteninformatik, der die Basis für viele wichtige Anwendungen wie den Shor-Algorithmus, die Abschätzung der Grundzustandsenergie in der Quantenchemie und Eigenwertprobleme bildet.  QPE schätzt die Phase $\\varphi$, die mit einem Eigenzustand eines unitären Operators verbunden ist, kodiert in der Beziehung\n",
        "\n",
        "$U \\lvert \\varphi \\rangle = e^{2\\pi i \\varphi} \\lvert \\varphi \\rangle,$\n",
        "\n",
        "und bestimmt es mit einer Genauigkeit von $\\epsilon = O(1/2^m)$ unter Verwendung von $m$ Zählqubits [\\[1\\]](#references). Indem man diese Qubits in Überlagerung präpariert, kontrollierte Potenzen von $U$ anwendet und dann die inverse Quanten-Fourier-Transformation (QFT) nutzt, um die Phase in binär kodierte Messergebnisse zu extrahieren, erzeugt QPE eine Wahrscheinlichkeitsverteilung, die bei Bitstrings gipfelt, deren binäre Brüche sich $\\varphi$ annähern. Im Idealfall entspricht das wahrscheinlichste Messergebnis direkt der binären Erweiterung der Phase, während die Wahrscheinlichkeit anderer Ergebnisse schnell mit der Anzahl der zählenden Qubits abnimmt. Der Betrieb tiefer QPE-Schaltkreise auf Hardware stellt jedoch eine Herausforderung dar: Die große Anzahl von Qubits und Verschränkungsoperationen machen den Algorithmus sehr anfällig für Dekohärenz und Gatterfehler. Dies führt zu verbreiterten und verschobenen Verteilungen von Bitstrings, die die wahre Eigenphase verdecken. Dies hat zur Folge, dass die Bitfolge mit der höchsten Wahrscheinlichkeit nicht mehr der korrekten binären Erweiterung von $\\varphi$ entspricht.\n",
        "\n",
        "In diesem Tutorial stellen wir eine Implementierung des QPE-Algorithmus unter Verwendung von Q-CTRLs Fire Opal-Fehlerunterdrückungs- und Leistungsmanagement-Tools vor, die als Qiskit-Funktion angeboten werden (siehe die [Fire Opal-Dokumentation](/docs/guides/q-ctrl-performance-management) ). Fire Opal wendet automatisch fortschrittliche Optimierungen an, einschließlich dynamischer Entkopplung, Verbesserungen des Qubit-Layouts und Fehlerunterdrückungstechniken, was zu realitätsgetreueren Ergebnissen führt. Diese Verbesserungen bringen die Hardware-Bitstring-Verteilungen näher an die in rauschfreien Simulationen erhaltenen Verteilungen heran, so dass Sie auch unter Rauscheffekten zuverlässig die richtige Eigenphase identifizieren können.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5019f20e",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Anforderungen\n",
        "\n",
        "Bevor Sie mit diesem Tutorial beginnen, sollten Sie sicherstellen, dass Sie Folgendes installiert haben:\n",
        "\n",
        "* Qiskit SDK v1.4 oder später, mit [Visual](/docs/api/qiskit/visualization) isierungsunterstützung\n",
        "* Qiskit Runtime v0.40 oder später (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions Catalog v0.9.0 (`pip install qiskit-ibm-catalog`)\n",
        "* Fire Opal SDK v9.0.2 oder höher (`pip install fire-opal`)\n",
        "* Q-CTRL Visualizer v8.0.2 oder höher (`pip install qctrl-visualizer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "360f9871",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Einrichtung\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46eddb0f",
      "metadata": {},
      "source": [
        "Melden Sie sich zunächst mit Ihrem [API-Schlüssel für „ IBM Quantum “](http://quantum.cloud.ibm.com/) an. Wählen Sie anschließend die Qiskit-Funktion wie folgt aus. (Dieser Code setzt voraus, dass Sie [Ihr Konto](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) bereits in Ihrer lokalen Umgebung gespeichert haben.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "39896f05",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import qiskit\n",
        "from qiskit import qasm2\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "import qctrlvisualizer as qv\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "\n",
        "plt.style.use(qv.get_qctrl_style())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9abf8775",
      "metadata": {},
      "outputs": [],
      "source": [
        "catalog = QiskitFunctionsCatalog(channel=\"ibm_quantum_platform\")\n",
        "\n",
        "# Access Function\n",
        "perf_mgmt = catalog.load(\"q-ctrl/performance-management\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6337a6c2",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## Schritt 1: Klassische Eingaben einem Quantenproblem zuordnen\n",
        "\n",
        "In diesem Tutorial veranschaulichen wir QPE, um die Eigenphase eines bekannten Ein-Qubit-Unitariums wiederherzustellen. Die Einheit, deren Phase wir schätzen wollen, ist das Ein-Qubit-Phasentor, das auf das Ziel-Qubit angewendet wird:\n",
        "\n",
        "$$\n",
        "U(\\theta)=\n",
        "\\begin{pmatrix}\n",
        "1 & 0\\\\[2pt]\n",
        "0 & e^{i\\theta}\n",
        "\\end{pmatrix}\n",
        "= e^{i\\theta\\,|1\\rangle\\!\\langle 1|}.\n",
        "$$\n",
        "\n",
        "Wir bereiten seinen Eigenzustand $|\\psi\\rangle=|1\\rangle$ vor. Da $|1\\rangle$ ein Eigenvektor von $U(\\theta)$ mit dem Eigenwert $e^{i\\theta}$ ist, lautet die zu schätzende Eigenphase:\n",
        "\n",
        "$$\n",
        "\\varphi = \\frac{\\theta}{2\\pi} \\pmod{1}\n",
        "$$\n",
        "\n",
        "Wir stellen $\\theta=\\tfrac{1}{6}\\cdot 2\\pi$ ein, so dass die Phase der Grundwahrheit $\\varphi=1/6$ ist. Der QPE-Schaltkreis implementiert die kontrollierten Potenzen $U^{2^k}$ durch Anwendung kontrollierter Phasendrehungen mit Winkeln $\\theta\\cdot2^k$, wendet dann die inverse QFT auf das Zählregister an und misst sie. Die resultierenden Bitstrings konzentrieren sich auf die binäre Darstellung von $1/6$.\n",
        "\n",
        "Die Schaltung verwendet $m$ Zählende Qubits (zur Einstellung der Schätzgenauigkeit) plus ein Ziel-Qubit. Wir beginnen mit der Definition der Bausteine, die für die Implementierung von QPE erforderlich sind: die Quanten-Fourier-Transformation (QFT) und ihre Umkehrung, Hilfsfunktionen zur Umrechnung zwischen Dezimal- und Binärbruchteilen der Eigenphase und Hilfsfunktionen zur Normalisierung von Rohwerten in Wahrscheinlichkeiten für den Vergleich von Simulations- und Hardwareergebnissen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "4c5cd3d4",
      "metadata": {},
      "outputs": [],
      "source": [
        "def inverse_quantum_fourier_transform(quantum_circuit, number_of_qubits):\n",
        "    \"\"\"\n",
        "    Apply an inverse Quantum Fourier Transform the first `number_of_qubits` qubits in the\n",
        "    `quantum_circuit`.\n",
        "    \"\"\"\n",
        "    for qubit in range(number_of_qubits // 2):\n",
        "        quantum_circuit.swap(qubit, number_of_qubits - qubit - 1)\n",
        "    for j in range(number_of_qubits):\n",
        "        for m in range(j):\n",
        "            quantum_circuit.cp(-np.pi / float(2 ** (j - m)), m, j)\n",
        "        quantum_circuit.h(j)\n",
        "    return quantum_circuit"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6d56f133",
      "metadata": {},
      "outputs": [],
      "source": [
        "def bitstring_count_to_probabilities(data, shot_count):\n",
        "    \"\"\"\n",
        "    This function turns an unsorted dictionary of bitstring counts into a sorted dictionary\n",
        "    of probabilities.\n",
        "    \"\"\"\n",
        "    # Turn the bitstring counts into probabilities.\n",
        "    probabilities = {\n",
        "        bitstring: bitstring_count / shot_count\n",
        "        for bitstring, bitstring_count in data.items()\n",
        "    }\n",
        "\n",
        "    sorted_probabilities = dict(\n",
        "        sorted(probabilities.items(), key=lambda x: x[1], reverse=True)\n",
        "    )\n",
        "\n",
        "    return sorted_probabilities"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d33b21fd",
      "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 bauen die QPE-Schaltung auf, indem wir die zählenden Qubits in Überlagerung vorbereiten, kontrollierte Phasendrehungen anwenden, um die Zieleigenphase zu kodieren, und mit einer inversen QFT vor der Messung abschließen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "79d62a79",
      "metadata": {},
      "outputs": [],
      "source": [
        "def quantum_phase_estimation_benchmark_circuit(\n",
        "    number_of_counting_qubits, phase\n",
        "):\n",
        "    \"\"\"\n",
        "    Create the circuit for quantum phase estimation.\n",
        "\n",
        "    Parameters\n",
        "    ----------\n",
        "    number_of_counting_qubits : The number of qubits in the circuit.\n",
        "    phase : The desired phase.\n",
        "\n",
        "    Returns\n",
        "    -------\n",
        "    QuantumCircuit\n",
        "        The quantum phase estimation circuit for `number_of_counting_qubits` qubits.\n",
        "    \"\"\"\n",
        "    qc = QuantumCircuit(\n",
        "        number_of_counting_qubits + 1, number_of_counting_qubits\n",
        "    )\n",
        "    target = number_of_counting_qubits\n",
        "\n",
        "    # |1> eigenstate for the single-qubit phase gate\n",
        "    qc.x(target)\n",
        "\n",
        "    # Hadamards on counting register\n",
        "    for q in range(number_of_counting_qubits):\n",
        "        qc.h(q)\n",
        "\n",
        "    # ONE controlled phase per counting qubit: cp(phase * 2**k)\n",
        "    for k in range(number_of_counting_qubits):\n",
        "        qc.cp(phase * (1 << k), k, target)\n",
        "\n",
        "    qc.barrier()\n",
        "\n",
        "    # Inverse QFT on counting register\n",
        "    inverse_quantum_fourier_transform(qc, number_of_counting_qubits)\n",
        "\n",
        "    qc.barrier()\n",
        "    for q in range(number_of_counting_qubits):\n",
        "        qc.measure(q, q)\n",
        "    return qc"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e550ad5",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Schritt 3: Mit „ Qiskit primitives “ ausführen\n",
        "\n",
        "Wir legen die Anzahl der Schüsse und Qubits für das Experiment fest und kodieren die Zielphase $\\varphi = 1/6$ mit $m$ Binärziffern. Mit diesen Parametern erstellen wir den QPE-Schaltkreis, der auf der Simulation, der Standardhardware und den mit Fire Opal erweiterten Backends ausgeführt wird.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "19e6dd45",
      "metadata": {},
      "outputs": [],
      "source": [
        "shot_count = 10000\n",
        "num_qubits = 35\n",
        "phase = (1 / 6) * 2 * np.pi\n",
        "circuits_quantum_phase_estimation = (\n",
        "    quantum_phase_estimation_benchmark_circuit(\n",
        "        number_of_counting_qubits=num_qubits, phase=phase\n",
        "    )\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6c53f759",
      "metadata": {},
      "source": [
        "<span id=\"run-mps-simulation\" />\n",
        "\n",
        "### MPS-Simulation ausführen\n",
        "\n",
        "Zunächst erzeugen wir mit dem `matrix_product_state` Simulator eine Referenzverteilung und wandeln die Zählungen in normalisierte Wahrscheinlichkeiten um, um sie später mit den Hardware-Ergebnissen zu vergleichen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "1fabc3a2",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Run the algorithm on the IBM Aer simulator.\n",
        "aer_simulator = AerSimulator(method=\"matrix_product_state\")\n",
        "\n",
        "# Transpile the circuits for the simulator.\n",
        "transpiled_circuits = qiskit.transpile(\n",
        "    circuits_quantum_phase_estimation, aer_simulator\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "99eca1f1",
      "metadata": {},
      "outputs": [],
      "source": [
        "simulated_result = (\n",
        "    aer_simulator.run(transpiled_circuits, shots=shot_count)\n",
        "    .result()\n",
        "    .get_counts()\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "263624e9",
      "metadata": {},
      "outputs": [],
      "source": [
        "simulated_result_probabilities = []\n",
        "\n",
        "simulated_result_probabilities.append(\n",
        "    bitstring_count_to_probabilities(\n",
        "        simulated_result,\n",
        "        shot_count=shot_count,\n",
        "    )\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "df203619",
      "metadata": {},
      "source": [
        "<span id=\"run-on-hardware\" />\n",
        "\n",
        "### Auf Hardware ausführen\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "41bf548b",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits_quantum_phase_estimation)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "b2bdb8b8",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Run the algorithm with IBM default.\n",
        "sampler = Sampler(backend)\n",
        "\n",
        "# Run all circuits using IBM Quantum Compute Service.\n",
        "ibm_default_job = sampler.run([isa_circuits], shots=shot_count)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "945967d0",
      "metadata": {},
      "source": [
        "<span id=\"run-on-hardware-with-fire-opal\" />\n",
        "\n",
        "### Auf Hardware mit Fire Opal ausführen\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ca892a3c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Run the circuit using Sampler\n",
        "fire_opal_job = perf_mgmt.run(\n",
        "    primitive=\"sampler\",\n",
        "    pubs=[qasm2.dumps(circuits_quantum_phase_estimation)],\n",
        "    backend_name=backend.name,\n",
        "    options={\"default_shots\": shot_count},\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "000a119c",
      "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"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "dff71917",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve results.\n",
        "ibm_default_result = ibm_default_job.result()\n",
        "ibm_default_probabilities = []\n",
        "\n",
        "for idx, pub_result in enumerate(ibm_default_result):\n",
        "    ibm_default_probabilities.append(\n",
        "        bitstring_count_to_probabilities(\n",
        "            pub_result.data.c0.get_counts(),\n",
        "            shot_count=shot_count,\n",
        "        )\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "be4e50e5",
      "metadata": {},
      "outputs": [],
      "source": [
        "fire_opal_result = fire_opal_job.result()\n",
        "\n",
        "fire_opal_probabilities = []\n",
        "for idx, pub_result in enumerate(fire_opal_result):\n",
        "    fire_opal_probabilities.append(\n",
        "        bitstring_count_to_probabilities(\n",
        "            pub_result.data.c0.get_counts(),\n",
        "            shot_count=shot_count,\n",
        "        )\n",
        "    )"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "c0134366",
      "metadata": {},
      "outputs": [],
      "source": [
        "data = {\n",
        "    \"simulation\": simulated_result_probabilities,\n",
        "    \"default\": ibm_default_probabilities,\n",
        "    \"fire_opal\": fire_opal_probabilities,\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "a6dc6744",
      "metadata": {},
      "outputs": [],
      "source": [
        "def plot_distributions(\n",
        "    data,\n",
        "    number_of_counting_qubits,\n",
        "    top_k=None,\n",
        "    by=\"prob\",\n",
        "    shot_count=None,\n",
        "):\n",
        "    def nrm(d):\n",
        "        s = sum(d.values())\n",
        "        return {k: (v / s if s else 0.0) for k, v in d.items()}\n",
        "\n",
        "    def as_float(d):\n",
        "        return {k: float(v) for k, v in d.items()}\n",
        "\n",
        "    def to_space(d):\n",
        "        if by == \"prob\":\n",
        "            return nrm(as_float(d))\n",
        "        else:\n",
        "            if shot_count and 0.99 <= sum(d.values()) <= 1.01:\n",
        "                return {\n",
        "                    k: v * float(shot_count) for k, v in as_float(d).items()\n",
        "                }\n",
        "            else:\n",
        "                return as_float(d)\n",
        "\n",
        "    def topk(d, k):\n",
        "        items = sorted(d.items(), key=lambda kv: kv[1], reverse=True)\n",
        "        return items[: (k or len(d))]\n",
        "\n",
        "    phase = \"1/6\"\n",
        "\n",
        "    sim = to_space(data[\"simulation\"])\n",
        "    dft = to_space(data[\"default\"])\n",
        "    qct = to_space(data[\"fire_opal\"])\n",
        "\n",
        "    correct = max(sim, key=sim.get) if sim else None\n",
        "    print(\"Correct result:\", correct)\n",
        "\n",
        "    sim_items = topk(sim, top_k)\n",
        "    dft_items = topk(dft, top_k)\n",
        "    qct_items = topk(qct, top_k)\n",
        "\n",
        "    sim_keys, y_sim = zip(*sim_items) if sim_items else ([], [])\n",
        "    dft_keys, y_dft = zip(*dft_items) if dft_items else ([], [])\n",
        "    qct_keys, y_qct = zip(*qct_items) if qct_items else ([], [])\n",
        "\n",
        "    fig, axes = plt.subplots(3, 1, layout=\"constrained\")\n",
        "    ylab = \"Probabilities\"\n",
        "\n",
        "    def panel(ax, keys, ys, title, color):\n",
        "        x = np.arange(len(keys))\n",
        "        bars = ax.bar(x, ys, color=color)\n",
        "        ax.set_title(title)\n",
        "        ax.set_ylabel(ylab)\n",
        "        ax.set_xticks(x)\n",
        "        ax.set_xticklabels(keys, rotation=90)\n",
        "        ax.set_xlabel(\"Bitstrings\")\n",
        "        if correct in keys:\n",
        "            i = keys.index(correct)\n",
        "            bars[i].set_edgecolor(\"black\")\n",
        "            bars[i].set_linewidth(2)\n",
        "        return max(ys, default=0.0)\n",
        "\n",
        "    c_sim, c_dft, c_qct = (\n",
        "        qv.QCTRL_STYLE_COLORS[5],\n",
        "        qv.QCTRL_STYLE_COLORS[1],\n",
        "        qv.QCTRL_STYLE_COLORS[0],\n",
        "    )\n",
        "    m1 = panel(axes[0], list(sim_keys), list(y_sim), \"Simulation\", c_sim)\n",
        "    m2 = panel(axes[1], list(dft_keys), list(y_dft), \"Default\", c_dft)\n",
        "    m3 = panel(axes[2], list(qct_keys), list(y_qct), \"Q-CTRL\", c_qct)\n",
        "\n",
        "    for ax, m in zip(axes, (m1, m2, m3)):\n",
        "        ax.set_ylim(0, 1.05 * (m or 1.0))\n",
        "\n",
        "    for ax in axes:\n",
        "        ax.label_outer()\n",
        "    fig.suptitle(\n",
        "        rf\"{number_of_counting_qubits} counting qubits, $2\\pi\\varphi$={phase}\"\n",
        "    )\n",
        "    fig.set_size_inches(20, 10)\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "593334d2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Correct result: 00101010101010101010101010101010101\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/quantum-phase-estimation-qctrl/extracted-outputs/593334d2-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "experiment_index = 0\n",
        "phase_index = 0\n",
        "\n",
        "distributions = {\n",
        "    \"simulation\": data[\"simulation\"][phase_index],\n",
        "    \"default\": data[\"default\"][phase_index],\n",
        "    \"fire_opal\": data[\"fire_opal\"][phase_index],\n",
        "}\n",
        "\n",
        "plot_distributions(\n",
        "    distributions, num_qubits, top_k=100, by=\"prob\", shot_count=shot_count\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5199fb3",
      "metadata": {},
      "source": [
        "Die Simulation legt die Basis für die richtige Eigenphase fest. Standard-Hardwareläufe zeigen Rauschen, das dieses Ergebnis verschleiert, da das Rauschen die Wahrscheinlichkeit über viele falsche Bitstrings verteilt. Mit Q-CTRL Performance Management wird die Verteilung schärfer und das korrekte Ergebnis wird wiederhergestellt, was eine zuverlässige QPE in diesem Umfang ermöglicht.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c72c6bb0",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Referenzen\n",
        "\n",
        "\\[1] Vorlesung 7: [Phasenabschätzung und Faktorisierung](/learning/courses/fundamentals-of-quantum-algorithms/phase-estimation-and-factoring/introduction). IBM Quantum Learning - Grundlagen der Quantenalgorithmen. Abgerufen am 3. Oktober 2025.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4760c8b9",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Tutorial-Umfrage\n",
        "\n",
        "Bitte nehmen Sie sich eine Minute Zeit, um uns Ihr Feedback zu diesem Tutorial zu geben. Ihre Erkenntnisse helfen uns, unser Inhaltsangebot und unsere Nutzererfahrung zu verbessern.\n",
        "\n",
        "[Link zur Umfrage](https://your.feedback.ibm.com/jfe/form/SV_3BLFkNVEuh0QBWm)\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": 40
  },
  "nbformat": 4,
  "nbformat_minor": 5
}