{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "alleged-prairie",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Dienstprogramm III\"\n",
        "description: \"In dieser Lektion geht es darum, eine GHZ-Schaltung für 20 Qubits oder mehr zu bauen, sodass bei der Messung die Genauigkeit des GHZ-Zustands über 0.5 liegt.\"\n",
        "---\n",
        "\n",
        "<span id=\"utility-scale-experiment-iii\" />\n",
        "\n",
        "# Großtechnisches Experiment III\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Toshinari Itoko, Tamiya Onodera, Kifumi Numata (19. Juli 2024)\n",
        "\n",
        "  [Laden Sie die PDF-Datei](https://ibm.ent.box.com/public/static/fw1538dogvyv0qbfqg8tan1k2fs27mfv.zip) der Originalvorlesung herunter. Beachten Sie, dass einige Codeschnipsel veraltet sein könnten, da es sich um statische Bilder handelt.\n",
        "\n",
        "  *Die ungefähre QPU-Zeit für die Durchführung dieses ersten Experiments beträgt 12 m 30 s. Im Folgenden finden Sie ein zusätzliches Experiment, für das etwa 4 m benötigt werden.*\n",
        "\n",
        "  (Hinweis: Dieses Notebook kann möglicherweise nicht in der für den offenen Plan vorgesehenen Zeit bewertet werden. Achten Sie darauf, dass Sie die Ressourcen des Quantencomputers sinnvoll einsetzen)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "20483d1a-6b31-4569-b817-c9faecc8823b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'2.0.2'"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qiskit\n",
        "\n",
        "qiskit.__version__"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "fa30363a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'0.40.1'"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import qiskit_ibm_runtime\n",
        "\n",
        "qiskit_ibm_runtime.__version__"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "78f23522",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.visualization import plot_histogram\n",
        "from qiskit.visualization import plot_gate_map\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.providers import BackendV2\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Sampler, Estimator, Batch, SamplerOptions"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "948a9ac1-2ad3-4969-8b39-bb3d697ad6bb",
      "metadata": {},
      "source": [
        "<span id=\"1-introduction\" />\n",
        "\n",
        "## 1. Einführung\n",
        "\n",
        "Betrachten wir kurz die GHZ-Zustände und die Art der Verteilung, die man von `Sampler` erwarten kann, wenn man sie anwendet. Dann werden wir das Ziel dieser Lektion klar formulieren.\n",
        "\n",
        "<span id=\"11-ghz-state\" />\n",
        "\n",
        "### 1.1 GHZ-Zustand\n",
        "\n",
        "Der GHZ-Zustand (Greenberger-Horne-Zeilinger-Zustand) für $n$ qubits ist definiert als\n",
        "\n",
        "$$\n",
        "\\frac{1}{\\sqrt 2}(|0\\rangle ^ {\\otimes n}+ |1\\rangle^ {\\otimes n})\n",
        "$$\n",
        "\n",
        "Natürlich kann sie mit der folgenden Quantenschaltung für 6 Qubits erstellt werden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "1bf914c5-e1c6-4664-99c2-69dea3961114",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/1bf914c5-e1c6-4664-99c2-69dea3961114-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "N = 6\n",
        "qc = QuantumCircuit(N, N)\n",
        "\n",
        "qc.h(0)\n",
        "for i in range(N - 1):\n",
        "    qc.cx(0, i + 1)\n",
        "\n",
        "# qc.measure_all()\n",
        "qc.barrier()\n",
        "qc.measure(list(range(N)), list(range(N)))\n",
        "\n",
        "qc.draw(output=\"mpl\", idle_wires=False, scale=0.5)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "7b4cb915-c901-4b6d-839b-2f1ae43601d7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 7\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", qc.depth())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c398d524",
      "metadata": {},
      "source": [
        "Die Tiefe ist nicht allzu groß, obwohl Sie aus früheren Lektionen wissen, dass Sie es besser können. Wählen wir ein Backend aus und übertragen wir diesen Schaltkreis.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "108bc369",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'ibm_kingston'"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "backend.name\n",
        "# or\n",
        "# backend = service.least_busy(operational=True)\n",
        "# backend.name"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "2e003078-3707-44d9-86e8-bbc66b095e84",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/2e003078-3707-44d9-86e8-bbc66b095e84-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(3, backend=backend)\n",
        "qc_transpiled = pm.run(qc)\n",
        "qc_transpiled.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "18d3524a-43ec-4163-8f53-8e9bf4421e06",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 27\n",
            "Two-qubit Depth: 11\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", qc_transpiled.depth())\n",
        "print(\n",
        "    \"Two-qubit Depth:\",\n",
        "    qc_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "109ff71e",
      "metadata": {},
      "source": [
        "Auch hier ist die transponierte Zwei-Qubit-Tiefe nicht allzu groß. Aber um mit einem GHZ-Zustand auf mehr Qubits zu arbeiten, muss man natürlich über eine Optimierung der Schaltung nachdenken. Führen wir dies mit `Sampler` durch und sehen wir, was ein echter Quantencomputer liefert.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "abd0654d-b2dc-4180-821c-6af4977a12e8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "d147y20n2txg008jvv70\n"
          ]
        }
      ],
      "source": [
        "sampler = Sampler(mode=backend)\n",
        "shots = 40000\n",
        "job = sampler.run([qc_transpiled], shots=shots)\n",
        "job_id = job.job_id()\n",
        "print(job_id)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "f0073d8b-1ad8-4bb7-a2d2-754ce2876ffd",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'DONE'"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "job.status()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "8ab5b37f-fb87-40a8-8e13-0c6cb3154f64",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/8ab5b37f-fb87-40a8-8e13-0c6cb3154f64-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "job = service.job(job_id)\n",
        "result = job.result()\n",
        "plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9965c7c1-babd-444d-8436-2a63bcbe79af",
      "metadata": {},
      "source": [
        "Dies ist das Ergebnis der 6-Qubit-GHZ-Schaltung. Wie Sie sehen können, dominieren die Zustände aller $|0\\rangle$ 's und aller $|1\\rangle$ 's, aber die Fehler sind beträchtlich. Versuchen wir einmal, wie groß die GHZ-Schaltung ist, die man mit einem Eagle-Gerät bauen kann, und wie groß die Wahrscheinlichkeit ist, dass die richtigen Zustände zu mehr als 50 % eintreten.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9942f911-bfbf-4a16-8349-05bc490885e6",
      "metadata": {},
      "source": [
        "<span id=\"12-your-goal\" />\n",
        "\n",
        "### 1.2 Ihr Ziel\n",
        "\n",
        "Bauen Sie einen GHZ-Schaltkreis für 20 Qubits oder mehr, so dass bei der Messung **die Zuverlässigkeit Ihres GHZ-Zustands > 0.5 ist**\n",
        "Hinweis:\n",
        "\n",
        "* Sie müssen ein Eagle-Gerät (`min_num_qubits=127`) verwenden und die Schusszahl auf 40.000 einstellen.\n",
        "* Führen Sie die GHZ-Schaltung mit der Funktion `execute_ghz_fidelity` aus, und berechnen Sie die Wiedergabetreue mit der Funktion `check_ghz_fidelity_from_jobs` .\n",
        "\n",
        "Dies ist als eigenständige Übung gedacht, bei der Sie das bisher in diesem Kurs Gelernte anwenden können.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a316416f-d6c5-4183-b4ef-d6170681d8a4",
      "metadata": {},
      "outputs": [],
      "source": [
        "def execute_ghz_fidelity(\n",
        "    ghz_circuit: QuantumCircuit,  # Quantum circuit to create GHZ state (Circuit after Routing or without Routing), Classical register name is \"c\"\n",
        "    physical_qubits: list[int],  # Physical qubits to represent GHZ state\n",
        "    backend: BackendV2,\n",
        "    sampler_options: dict | SamplerOptions | None = None,\n",
        "):\n",
        "    N_SHOTS = 40_000\n",
        "    N = len(physical_qubits)\n",
        "    base_circuit = ghz_circuit.remove_final_measurements(inplace=False)\n",
        "    # M_k measurement circuits\n",
        "    mk_circuits = []\n",
        "    for k in range(1, N + 1):\n",
        "        circuit = base_circuit.copy()\n",
        "        # change measurement basis\n",
        "        for q in physical_qubits:\n",
        "            circuit.rz(-k * np.pi / N, q)\n",
        "            circuit.h(q)\n",
        "        mk_circuits.append(circuit)\n",
        "\n",
        "    obs = SparsePauliOp.from_sparse_list(\n",
        "        [(\"Z\" * N, physical_qubits, 1)], num_qubits=backend.num_qubits\n",
        "    )\n",
        "    job_ids = []\n",
        "    pm1 = generate_preset_pass_manager(1, backend=backend)\n",
        "    org_transpiled = pm1.run(ghz_circuit)\n",
        "    mk_transpiled = pm1.run(mk_circuits)\n",
        "    with Batch(backend=backend):\n",
        "        sampler = Sampler(options=sampler_options)\n",
        "        sampler.options.twirling.enable_measure = True\n",
        "        job = sampler.run([org_transpiled], shots=N_SHOTS)\n",
        "        job_ids.append(job.job_id())\n",
        "        # print(f\"Sampler job id: {job.job_id()}, shots={N_SHOTS}\")\n",
        "        estimator = Estimator()  # TREX is applied as default\n",
        "        estimator.options.dynamical_decoupling.enable = True\n",
        "        estimator.options.execution.rep_delay = 0.0005\n",
        "        estimator.options.twirling.enable_measure = True\n",
        "        job2 = estimator.run([(circ, obs) for circ in mk_transpiled], precision=1 / 100)\n",
        "        job_ids.append(job2.job_id())\n",
        "        # print(\"Estimator job id:\", job2.job_id())\n",
        "        return [job.job_id(), job2.job_id()]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "f86e4288-9e65-4dd5-81dd-149f1e98d217",
      "metadata": {},
      "outputs": [],
      "source": [
        "def check_ghz_fidelity_from_jobs(\n",
        "    sampler_job,\n",
        "    estimator_job,\n",
        "    num_qubits,\n",
        "    shots=40_000,\n",
        "):\n",
        "    N = num_qubits\n",
        "    sampler_result = sampler_job.result()\n",
        "    counts = sampler_result[0].data.c.get_counts()\n",
        "    all_zero = counts.get(\"0\" * N, 0) / shots\n",
        "    all_one = counts.get(\"1\" * N, 0) / shots\n",
        "    top3 = sorted(counts, key=counts.get, reverse=True)[:3]\n",
        "    print(\n",
        "        f\"N={N}: |00..0>: {counts.get('0'*N, 0)}, |11..1>: {counts.get('1'*N, 0)}, |3rd>: {counts.get(top3[2], 0)} ({top3[2]})\"\n",
        "    )\n",
        "    print(f\"P(|00..0>)={all_zero}, P(|11..1>)={all_one}\")\n",
        "\n",
        "    estimator_result = estimator_job.result()\n",
        "    non_diagonal = (1 / N) * sum(\n",
        "        (-1) ** k * estimator_result[k - 1].data.evs for k in range(1, N + 1)\n",
        "    )\n",
        "    print(f\"REM: Coherence (non-diagonal): {non_diagonal:.6f}\")\n",
        "    fidelity = 0.5 * (all_zero + all_one + non_diagonal)\n",
        "    sigma = 0.5 * np.sqrt(\n",
        "        (1 - all_zero - all_one) * (all_zero + all_one) / shots\n",
        "        + sum(estimator_result[k].data.stds ** 2 for k in range(N)) / (N * N)\n",
        "    )\n",
        "    print(f\"GHZ fidelity = {fidelity:.6f} ± {sigma:.6f}\")\n",
        "    if fidelity - 2 * sigma > 0.5:\n",
        "        print(\"GME (genuinely multipartite entangled) test: Passed\")\n",
        "    else:\n",
        "        print(\"GME (genuinely multipartite entangled) test: Failed\")\n",
        "    return {\n",
        "        \"fidelity\": fidelity,\n",
        "        \"sigma\": sigma,\n",
        "        \"shots\": shots,\n",
        "        \"job_ids\": [sampler_job.job_id(), estimator_job.job_id()],\n",
        "    }"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad35a576-9ae4-4d00-af4d-3af0974101e9",
      "metadata": {},
      "source": [
        "In diesem Notizbuch werden wir drei Strategien anwenden, um gute GHZ-Zustände mit 16 Qubits und 30 Qubits zu erzeugen. Diese Ansätze bauen auf Strategien auf, die Sie bereits aus früheren Lektionen kennen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "371c0d79",
      "metadata": {},
      "source": [
        "<span id=\"2-strategy-1-noise-aware-qubit-selection\" />\n",
        "\n",
        "## 2. Strategie 1. Geräuschbewusste Qubit-Auswahl\n",
        "\n",
        "Zunächst legen wir ein Backend fest. Da wir ausgiebig mit den Eigenschaften eines bestimmten Backends arbeiten werden, ist es eine gute Idee, ein Backend anzugeben, anstatt die Option `least_busy` zu verwenden.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "119bd751",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Device ibm_strasbourg Loaded with 127 qubits\n",
            "Two Qubit Gate: ecr\n"
          ]
        }
      ],
      "source": [
        "backend = service.backend(\"ibm_strasbourg\")  # eagle\n",
        "twoq_gate = \"ecr\"\n",
        "print(f\"Device {backend.name} Loaded with {backend.num_qubits} qubits\")\n",
        "print(f\"Two Qubit Gate: {twoq_gate}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "997d7bc9",
      "metadata": {},
      "source": [
        "Wir werden eine Schaltung mit vielen Zwei-Qubit-Gattern bauen. Es ist sinnvoll, bei der Implementierung dieser Zwei-Qubit-Gatter die Qubits zu verwenden, die die geringsten Fehler aufweisen. Die Suche nach der besten \"Qubit-Kette\" auf der Grundlage der gemeldeten 2q-gate Fehler ist ein nicht triviales Problem. Aber wir können ein paar Funktionen definieren, die uns helfen, die besten Qubits zu bestimmen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "7bc47055",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = backend.target.build_coupling_map(twoq_gate)\n",
        "G = coupling_map.graph"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8615f6d8",
      "metadata": {},
      "outputs": [],
      "source": [
        "def to_edges(path):  # create edges list from node paths\n",
        "    edges = []\n",
        "    prev_node = None\n",
        "    for node in path:\n",
        "        if prev_node is not None:\n",
        "            if G.has_edge(prev_node, node):\n",
        "                edges.append((prev_node, node))\n",
        "            else:\n",
        "                edges.append((node, prev_node))\n",
        "        prev_node = node\n",
        "    return edges\n",
        "\n",
        "\n",
        "def path_fidelity(path, correct_by_duration: bool = True, readout_scale: float = None):\n",
        "    \"\"\"Compute an estimate of the total fidelity of 2-qubit gates on a path.\n",
        "    If `correct_by_duration` is true, each gate fidelity is worsen by\n",
        "    scale = max_duration / duration, that is, gate_fidelity^scale.\n",
        "    If `readout_scale` > 0 is supplied, readout_fidelity^readout_scale\n",
        "    for each qubit on the path is multiplied to the total fielity.\n",
        "    The path is given in node indices form, for example, [0, 1, 2].\n",
        "    An external function `to_edges` is used to obtain edge list, for example, [(0, 1), (1, 2)].\"\"\"\n",
        "    path_edges = to_edges(path)\n",
        "    max_duration = max(backend.target[twoq_gate][qs].duration for qs in path_edges)\n",
        "\n",
        "    def gate_fidelity(qpair):\n",
        "        duration = backend.target[twoq_gate][qpair].duration\n",
        "        scale = max_duration / duration if correct_by_duration else 1.0\n",
        "        # 1.25 = (d+1)/d with d = 4\n",
        "        return max(0.25, 1 - (1.25 * backend.target[twoq_gate][qpair].error)) ** scale\n",
        "\n",
        "    def readout_fidelity(qubit):\n",
        "        return max(0.25, 1 - backend.target[\"measure\"][(qubit,)].error)\n",
        "\n",
        "    total_fidelity = np.prod(\n",
        "        [gate_fidelity(qs) for qs in path_edges]\n",
        "    )  # two qubits gate fidelity for each path\n",
        "    if readout_scale:\n",
        "        total_fidelity *= (\n",
        "            np.prod([readout_fidelity(q) for q in path]) ** readout_scale\n",
        "        )  # multiply readout fidelity\n",
        "    return total_fidelity\n",
        "\n",
        "\n",
        "def flatten(paths, cutoff=None):  # cutoff is for not making run time too large\n",
        "    return [\n",
        "        path\n",
        "        for s, s_paths in paths.items()\n",
        "        for t, st_paths in s_paths.items()\n",
        "        for path in st_paths[:cutoff]\n",
        "        if s < t\n",
        "    ]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "be1971a0",
      "metadata": {},
      "outputs": [],
      "source": [
        "N = 16  # Number of qubits to use in the GHZ circuit\n",
        "num_qubits_in_chain = N"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d95a798a-90cc-42d8-9461-ab5bd5440aef",
      "metadata": {},
      "source": [
        "Wir werden die obigen Funktionen verwenden, um alle einfachen Pfade von N Qubits zwischen allen Knotenpaaren im Graphen zu finden (Referenz: [all\\_pairs\\_all\\_simple\\_paths](https://www.rustworkx.org/apiref/rustworkx.all_pairs_all_simple_paths.html#rustworkx-all-pairs-all-simple-paths) ).\n",
        "\n",
        "Mit Hilfe der oben erstellten Funktion `path_fidelity` finden wir dann die beste Qubit-Kette mit der größten Pfadtreue.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "e0b410e7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Selecting the best from 6046 candidate paths\n",
            "Predicted (best possible) process fidelity: 0.8929026784775056\n",
            "CPU times: user 284 ms, sys: 10.9 ms, total: 295 ms\n",
            "Wall time: 295 ms\n"
          ]
        }
      ],
      "source": [
        "from functools import partial\n",
        "\n",
        "%%time\n",
        "paths = rx.all_pairs_all_simple_paths(\n",
        "    G.to_undirected(multigraph=False),\n",
        "    min_depth=num_qubits_in_chain,\n",
        "    cutoff=num_qubits_in_chain,\n",
        ")\n",
        "paths = flatten(paths, cutoff=25)  # If you have time, you could set a larger cutoff.\n",
        "if not paths:\n",
        "    raise Exception(\n",
        "        f\"No qubit chain with length={num_qubits_in_chain} exists in {backend.name}. Try smaller num_qubits_in_chain.\"\n",
        "    )\n",
        "\n",
        "print(f\"Selecting the best from {len(paths)} candidate paths\")\n",
        "\n",
        "best_qubit_chain = max(\n",
        "    paths, key=partial(path_fidelity, correct_by_duration=True, readout_scale=1.0)\n",
        ")\n",
        "assert len(best_qubit_chain) == num_qubits_in_chain\n",
        "print(f\"Predicted (best possible) process fidelity: {path_fidelity(best_qubit_chain)}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "id": "efc3c14d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([55, 49, 48, 47, 46, 45, 54, 64, 65, 66, 73, 85, 86, 87, 88, 89],\n",
              "      dtype=uint64)"
            ]
          },
          "execution_count": 41,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.array(best_qubit_chain)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9d364986-c387-4d0d-874c-9a7f10634394",
      "metadata": {},
      "source": [
        "Zeichnen wir die beste Qubit-Kette, die in rosa dargestellt ist, in das Kopplungsdiagramm ein.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "a1c164a4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/a1c164a4-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 42,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qubit_color = []\n",
        "for i in range(133):\n",
        "    if i in best_qubit_chain:\n",
        "        qubit_color.append(\"#ff00dd\")  # pink\n",
        "    else:\n",
        "        qubit_color.append(\"#8c00ff\")  # purple\n",
        "plot_gate_map(\n",
        "    backend, qubit_color=qubit_color, qubit_size=50, font_size=25, figsize=(6, 6)\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4e66abc7",
      "metadata": {},
      "source": [
        "<span id=\"21-build-a-ghz-circuit-on-the-best-qubit-chain\" />\n",
        "\n",
        "### 2.1 Erstellen Sie eine GHZ-Schaltung auf der besten Qubit-Kette\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "273f469d-3273-40ee-922d-4ad43e1d53ad",
      "metadata": {},
      "source": [
        "Wir wählen ein Qubit in der Mitte der Kette, auf das wir zuerst das H-Gatter anwenden. Dadurch sollte sich die Tiefe der Schaltung um etwa die Hälfte verringern.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 43,
      "id": "1336fba0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/1336fba0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 43,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz1 = QuantumCircuit(max(best_qubit_chain) + 1, N)\n",
        "ghz1.h(best_qubit_chain[N // 2])\n",
        "for i in range(N // 2, 0, -1):\n",
        "    ghz1.cx(best_qubit_chain[i], best_qubit_chain[i - 1])\n",
        "for i in range(N // 2, N - 1, +1):\n",
        "    ghz1.cx(best_qubit_chain[i], best_qubit_chain[i + 1])\n",
        "ghz1.barrier()  # for visualization\n",
        "ghz1.measure(best_qubit_chain, list(range(N)))\n",
        "ghz1.draw(output=\"mpl\", idle_wires=False, scale=0.5, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 44,
      "id": "facf25d3-add9-42e2-aeba-506b24a8e516",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "10"
            ]
          },
          "execution_count": 44,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz1.depth()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "id": "71874b6d-eb9b-4ff5-b673-944191f67010",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/71874b6d-eb9b-4ff5-b673-944191f67010-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 45,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(1, backend=backend)\n",
        "ghz1_transpiled = pm.run(ghz1)\n",
        "ghz1_transpiled.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "id": "9e1609a4-2b1c-43dc-99e0-ed62dbeb89fb",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 27\n",
            "Two-qubit Depth: 8\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", ghz1_transpiled.depth())\n",
        "print(\n",
        "    \"Two-qubit Depth:\",\n",
        "    ghz1_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "id": "5a3ad726-e6fb-4de5-a674-f7373845c917",
      "metadata": {},
      "outputs": [],
      "source": [
        "opts = SamplerOptions()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0178e4f4-cc5f-44f9-97b7-7393765e53f1",
      "metadata": {},
      "outputs": [],
      "source": [
        "res = execute_ghz_fidelity(\n",
        "    ghz_circuit=ghz1,\n",
        "    physical_qubits=best_qubit_chain,\n",
        "    backend=backend,\n",
        "    sampler_options=opts,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "d6368f70-1fb7-45ad-bd90-f3ca1b1795dc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE DONE\n"
          ]
        }
      ],
      "source": [
        "job_s = service.job(res[0])  # Use your job id showed above.\n",
        "job_e = service.job(res[1])\n",
        "print(job_s.status(), job_e.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cfebc599-d70d-4f1d-95b2-4c2ec60b1ee5",
      "metadata": {},
      "source": [
        "Achten Sie darauf, die nächste Zelle auszuführen, nachdem die oben genannten Aufträge den Status \"DONE\" erreicht haben, um das Ergebnis mit der Funktion `check_ghz_fidelity_from_jobs` anzuzeigen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 56,
      "id": "7029c582-6b41-4df5-b175-e098e0ee2e74",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "N=16: |00..0>: 153, |11..1>: 8681, |3rd>: 2262 (1111111111101111)\n",
            "P(|00..0>)=0.003825, P(|11..1>)=0.217025\n",
            "REM: Coherence (non-diagonal): 0.073809\n",
            "GHZ fidelity = 0.147329 ± 0.002438\n",
            "GME (genuinely multipartite entangled) test: Failed\n"
          ]
        }
      ],
      "source": [
        "N = 16\n",
        "# Check fidelity from job IDs\n",
        "res = check_ghz_fidelity_from_jobs(\n",
        "    sampler_job=job_s,\n",
        "    estimator_job=job_e,\n",
        "    num_qubits=N,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 57,
      "id": "236126cb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/236126cb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 57,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = job_s.result()\n",
        "plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3af2c82d-183e-4df0-be45-17fd32ea6c2e",
      "metadata": {},
      "source": [
        "Dieses Ergebnis entspricht nicht den Kriterien. Lassen Sie uns zur nächsten Idee übergehen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ce16e4cb",
      "metadata": {},
      "source": [
        "<span id=\"3-strategy-2-balanced-tree-of-qubits\" />\n",
        "\n",
        "## 3. Strategie 2. Ausgeglichener Baum von Qubits\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "45d96784",
      "metadata": {},
      "source": [
        "Die nächste Idee ist, einen ausgewogenen Baum von Qubits zu finden. Durch die Verwendung des Baums anstelle der Kette sollte die Schaltungstiefe geringer werden. Zuvor entfernen wir Knoten mit \"schlechten\" Auslesefehlern und Kanten mit \"schlechten\" Gatterfehlern aus dem Kopplungsgraphen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 58,
      "id": "632768ac",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Bad readout qubits: [19, 28, 41, 72, 91, 114, 120]\n",
            "Bad ECR gates: []\n"
          ]
        }
      ],
      "source": [
        "BAD_READOUT_ERROR_THRESHOLD = 0.1\n",
        "BAD_ECRGATE_ERROR_THRESHOLD = 0.1\n",
        "bad_readout_qubits = [\n",
        "    q\n",
        "    for q in range(backend.num_qubits)\n",
        "    if backend.target[\"measure\"][(q,)].error > BAD_READOUT_ERROR_THRESHOLD\n",
        "]\n",
        "bad_ecrgate_edges = [\n",
        "    qpair\n",
        "    for qpair in backend.target[\"ecr\"]\n",
        "    if backend.target[\"ecr\"][qpair].error > BAD_ECRGATE_ERROR_THRESHOLD\n",
        "]\n",
        "print(\"Bad readout qubits:\", bad_readout_qubits)\n",
        "print(\"Bad ECR gates:\", bad_ecrgate_edges)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 59,
      "id": "6775ea6b",
      "metadata": {},
      "outputs": [],
      "source": [
        "g = backend.coupling_map.graph.copy().to_undirected()\n",
        "g.remove_edges_from(\n",
        "    bad_ecrgate_edges\n",
        ")  # remove edge first (otherwise might fail with a NoEdgeBetweenNodes error)\n",
        "g.remove_nodes_from(bad_readout_qubits)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2247eb6b-a909-4fa1-91f4-40dfdf5f8d77",
      "metadata": {},
      "source": [
        "Zeichnen wir den Kopplungsdiagramm-Graphen ohne die schlechten Kanten und schlechten Qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 60,
      "id": "18825a87-4679-4027-af58-31efd24a9a60",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/18825a87-4679-4027-af58-31efd24a9a60-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 60,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qubit_color = []\n",
        "for i in range(133):\n",
        "    if i in bad_readout_qubits:\n",
        "        qubit_color.append(\"#000000\")  # black\n",
        "    else:\n",
        "        qubit_color.append(\"#8c00ff\")  # purple\n",
        "line_color = []\n",
        "for e in backend.target.build_coupling_map().get_edges():\n",
        "    if e in bad_ecrgate_edges:\n",
        "        line_color.append(\"#ffffff\")  # white\n",
        "    else:\n",
        "        line_color.append(\"#888888\")  # gray\n",
        "plot_gate_map(\n",
        "    backend,\n",
        "    qubit_color=qubit_color,\n",
        "    line_color=line_color,\n",
        "    qubit_size=50,\n",
        "    font_size=25,\n",
        "    figsize=(6, 6),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5245dbee-e0fa-485d-a0fc-ce72b2739ccd",
      "metadata": {},
      "source": [
        "Wir versuchen, wie zuvor einen 16-Qubit-GHZ-Zustand zu erzeugen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 61,
      "id": "1affd16e",
      "metadata": {},
      "outputs": [],
      "source": [
        "N = 16"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b0648130-ab85-477e-b754-94a3e28e2134",
      "metadata": {},
      "source": [
        "Wir rufen die Funktion `betweenness_centrality` auf, um ein Qubit für den Wurzelknoten zu finden. Der Knoten mit dem höchsten Wert der Betweenness-Zentralität befindet sich in der Mitte des Diagramms. Referenz: [https://www.rustworkx.org/tutorial/betweenness\\_centrality.html](https://www.rustworkx.org/tutorial/betweenness_centrality.html)\n",
        "\n",
        "Oder Sie können sie manuell auswählen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 62,
      "id": "07ad8ee3-46f9-4c7b-ad96-5aaa45407999",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "66"
            ]
          },
          "execution_count": 62,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# central = 65 #Select the center node manually\n",
        "c_degree = dict(rx.betweenness_centrality(g))\n",
        "central = max(c_degree, key=c_degree.get)\n",
        "central"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5033164f-652a-4b72-a6f8-5fe52383e3f3",
      "metadata": {},
      "source": [
        "Erzeugen Sie ausgehend vom Wurzelknoten einen Baum durch BFS (breadth first search). Referenz: [https://qiskit.org/ecosystem/rustworkx/apiref/rustworkx.bfs\\_search.html #rustworkx-bfs-search](https://qiskit.org/ecosystem/rustworkx/apiref/rustworkx.bfs_search.html#rustworkx-bfs-search)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 63,
      "id": "9137e5c8",
      "metadata": {},
      "outputs": [],
      "source": [
        "class TreeEdgesRecorder(rx.visit.BFSVisitor):\n",
        "    def __init__(self, N):\n",
        "        self.edges = []\n",
        "        self.N = N\n",
        "\n",
        "    def tree_edge(self, edge):\n",
        "        self.edges.append(edge)\n",
        "        if len(self.edges) >= self.N - 1:\n",
        "            raise rx.visit.StopSearch()\n",
        "\n",
        "\n",
        "vis = TreeEdgesRecorder(N)\n",
        "rx.bfs_search(g, [central], vis)\n",
        "best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))\n",
        "# print('Tree edges:', vis.edges)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 64,
      "id": "69c447dc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Qubits selected: [54, 55, 63, 64, 65, 66, 67, 68, 69, 70, 73, 83, 84, 85, 86, 87]\n"
          ]
        }
      ],
      "source": [
        "print(\"Qubits selected:\", best_qubits)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ea5f9be4-fb66-4231-a9df-b49a510cceb9",
      "metadata": {},
      "source": [
        "Zeichnen wir die ausgewählten Qubits, die in rosa dargestellt sind, in das Kopplungsdiagramm ein.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 65,
      "id": "ef18e037-a3ec-4370-bee7-bd71dbc12795",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/ef18e037-a3ec-4370-bee7-bd71dbc12795-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 65,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qubit_color = []\n",
        "for i in range(133):\n",
        "    if i in bad_readout_qubits:\n",
        "        qubit_color.append(\"#000000\")  # black\n",
        "    elif i in best_qubits:\n",
        "        qubit_color.append(\"#ff00dd\")  # pink\n",
        "    else:\n",
        "        qubit_color.append(\"#8c00ff\")  # purple\n",
        "plot_gate_map(\n",
        "    backend,\n",
        "    qubit_color=qubit_color,\n",
        "    line_color=line_color,\n",
        "    qubit_size=50,\n",
        "    font_size=25,\n",
        "    figsize=(6, 6),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c766256c-a9b8-41c1-ad23-1862f1e36b38",
      "metadata": {},
      "source": [
        "Wir wollen die Baumstruktur der Qubits zeigen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 66,
      "id": "8758d3a6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/8758d3a6-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 66,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from rustworkx.visualization import graphviz_draw\n",
        "\n",
        "tree = rx.PyDiGraph()\n",
        "tree.extend_from_weighted_edge_list(vis.edges)\n",
        "tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])\n",
        "\n",
        "graphviz_draw(tree, method=\"dot\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 67,
      "id": "3bc4b071",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/3bc4b071-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 67,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz2 = QuantumCircuit(max(best_qubits) + 1, N)\n",
        "\n",
        "ghz2.h(tree.edge_list()[0][0])  # apply H-gate to the root node\n",
        "# Apply CNOT from the root node to the each edge.\n",
        "for u, v in tree.edge_list():\n",
        "    ghz2.cx(u, v)\n",
        "ghz2.barrier()  # for visualization\n",
        "ghz2.measure(best_qubits, list(range(N)))\n",
        "ghz2.draw(output=\"mpl\", idle_wires=False, scale=0.5)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 68,
      "id": "fd2978ee-b359-464e-bf7f-eed4a65b1203",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "8"
            ]
          },
          "execution_count": 68,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz2.depth()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 69,
      "id": "c490e37a-309a-43e3-9b18-3be7c7d8a957",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/c490e37a-309a-43e3-9b18-3be7c7d8a957-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 69,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(1, backend=backend)\n",
        "ghz2_transpiled = pm.run(ghz2)\n",
        "ghz2_transpiled.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 70,
      "id": "376dfe4e-53c9-4eb5-988e-2bc7674ddcad",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 22\n",
            "Two-qubit Depth: 6\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", ghz2_transpiled.depth())\n",
        "print(\n",
        "    \"Two-qubit Depth:\",\n",
        "    ghz2_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f5681336-b72d-4ec9-a60e-2101fa42d87e",
      "metadata": {},
      "source": [
        "Die Tiefe des Kreislaufs ist nun viel geringer als die der Kettenstruktur.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "c56889ab-b8cd-4a7e-90f0-8a05d12e9f3f",
      "metadata": {},
      "outputs": [],
      "source": [
        "res = execute_ghz_fidelity(\n",
        "    ghz_circuit=ghz2,\n",
        "    physical_qubits=best_qubits,\n",
        "    backend=backend,\n",
        "    sampler_options=opts,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 74,
      "id": "daf011fe-853c-4e55-89ff-eeab38cb0de4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE DONE\n"
          ]
        }
      ],
      "source": [
        "job_s = service.job(res[0])  # Use your job id showed above.\n",
        "job_e = service.job(res[1])\n",
        "print(job_s.status(), job_e.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 75,
      "id": "51aee0d4-c896-4d2b-843a-3c58ee7572bb",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "N=16: |00..0>: 9509, |11..1>: 10978, |3rd>: 1795 (1111110111111111)\n",
            "P(|00..0>)=0.237725, P(|11..1>)=0.27445\n",
            "REM: Coherence (non-diagonal): 0.606515\n",
            "GHZ fidelity = 0.559345 ± 0.003188\n",
            "GME (genuinely multipartite entangled) test: Passed\n"
          ]
        }
      ],
      "source": [
        "N = 16\n",
        "# Check fidelity from job IDs\n",
        "res = check_ghz_fidelity_from_jobs(\n",
        "    sampler_job=job_s,\n",
        "    estimator_job=job_e,\n",
        "    num_qubits=N,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7fe25d70-36e3-4442-b150-21ea47143050",
      "metadata": {},
      "source": [
        "Wir haben die Kriterien mit der ausgewogenen Baumstruktur erfolgreich bestanden!\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 76,
      "id": "cf8b4dac",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/cf8b4dac-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 76,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = job_s.result()\n",
        "plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "40bc77d5-be60-4360-9750-31d0f1459ab0",
      "metadata": {},
      "source": [
        "Nun wollen wir versuchen, einen größeren GHZ-Zustand zu erzeugen: einen 30-Qubit-GHZ-Zustand.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "816914ce-52dd-4df5-9943-0acc606240c3",
      "metadata": {},
      "source": [
        "<span id=\"31-n-=-30\" />\n",
        "\n",
        "### 3.1 N = 30\n",
        "\n",
        "Wir werden uns an den Rahmen der [Qiskit-Muster](/docs/guides/intro-to-patterns) halten.\n",
        "\n",
        "* Schritt 1: Abbildung des Problems auf Quantenschaltungen und -operatoren\n",
        "* Schritt 2: Optimieren für die Zielhardware\n",
        "* Schritt 3: Ausführen auf der Zielhardware\n",
        "* Schritt 4: Nachbearbeitung der Ergebnisse\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20e6b416-eee7-4728-9fef-0de34b5ecbac",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-problem-to-quantum-circuits-and-operators-and-step-2-optimize-for-target-hardware\" />\n",
        "\n",
        "#### Schritt 1: Problem auf Quantenschaltungen und -operatoren abbilden und Schritt 2: Für die Zielhardware optimieren\n",
        "\n",
        "Hier wählen wir den Wurzelknoten manuell aus.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 77,
      "id": "5fff1f72-062f-4c3c-84b9-87e3af368f31",
      "metadata": {},
      "outputs": [],
      "source": [
        "central = 62  # Select the center node manually\n",
        "# c_degree = dict(rx.betweenness_centrality(g))\n",
        "# central = max(c_degree, key=c_degree.get)\n",
        "# central"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 78,
      "id": "693e80bb-e967-41be-bd82-85e4346fd6aa",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Qubits selected: [34, 35, 42, 43, 44, 45, 46, 47, 48, 53, 54, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 71, 73, 77, 84, 85, 86]\n"
          ]
        }
      ],
      "source": [
        "N = 30\n",
        "\n",
        "vis = TreeEdgesRecorder(N)\n",
        "rx.bfs_search(g, [central], vis)\n",
        "best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))\n",
        "print(\"Qubits selected:\", best_qubits)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 79,
      "id": "12d3ac00-7dfc-4734-9881-4e6385f2d880",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/12d3ac00-7dfc-4734-9881-4e6385f2d880-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 79,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qubit_color = []\n",
        "for i in range(133):\n",
        "    if i in bad_readout_qubits:\n",
        "        qubit_color.append(\"#000000\")\n",
        "    elif i in best_qubits:\n",
        "        qubit_color.append(\"#ff00dd\")\n",
        "    else:\n",
        "        qubit_color.append(\"#8c00ff\")\n",
        "line_color = []\n",
        "for e in backend.target.build_coupling_map().get_edges():\n",
        "    if e in bad_ecrgate_edges:\n",
        "        line_color.append(\"#ffffff\")\n",
        "    else:\n",
        "        line_color.append(\"#888888\")\n",
        "plot_gate_map(\n",
        "    backend,\n",
        "    qubit_color=qubit_color,\n",
        "    line_color=line_color,\n",
        "    qubit_size=50,\n",
        "    font_size=25,\n",
        "    figsize=(6, 6),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 80,
      "id": "6afd6c54-b427-4f34-8c4c-285c0122aebd",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/6afd6c54-b427-4f34-8c4c-285c0122aebd-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 80,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from rustworkx.visualization import graphviz_draw\n",
        "\n",
        "tree = rx.PyDiGraph()\n",
        "tree.extend_from_weighted_edge_list(vis.edges)\n",
        "tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])\n",
        "\n",
        "graphviz_draw(tree, method=\"dot\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "70b0b915-478e-4d65-9840-fb3db3e0fd0e",
      "metadata": {},
      "source": [
        "Die Tiefe dieses Baumes beträgt 5.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 81,
      "id": "d36cdb58-59fe-474e-9a31-a2d9e806ab59",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/d36cdb58-59fe-474e-9a31-a2d9e806ab59-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 81,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz3 = QuantumCircuit(max(best_qubits) + 1, N)\n",
        "\n",
        "ghz3.h(tree.edge_list()[0][0])  # apply H-gate to the root node\n",
        "# Apply CNOT from the root node to the each edge.\n",
        "for u, v in tree.edge_list():\n",
        "    ghz3.cx(u, v)\n",
        "ghz3.barrier()  # for visualization\n",
        "ghz3.measure(best_qubits, list(range(N)))\n",
        "ghz3.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 82,
      "id": "1b1dfb32-bcbf-4107-80df-4b36c166b88b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11"
            ]
          },
          "execution_count": 82,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz3.depth()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 83,
      "id": "46df385d-dd28-4fab-8344-6ef2fd53906d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/46df385d-dd28-4fab-8344-6ef2fd53906d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 83,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(1, backend=backend)\n",
        "ghz3_transpiled = pm.run(ghz3)\n",
        "ghz3_transpiled.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 84,
      "id": "74af05ba-132e-480d-ac5d-7b3bb4f4f0e4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 31\n",
            "Two-qubit Depth: 9\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", ghz3_transpiled.depth())\n",
        "print(\n",
        "    \"Two-qubit Depth:\",\n",
        "    ghz3_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fb74efcc-9db1-49b0-9f53-f5de236c6f29",
      "metadata": {},
      "source": [
        "<span id=\"32-select-a-different-root-node-manually\" />\n",
        "\n",
        "### 3.2 Wählen Sie manuell einen anderen Stammknoten aus\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 85,
      "id": "fb72a71a-8176-4b62-ac51-5a75469f0256",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Qubits selected: [23, 24, 25, 34, 35, 42, 43, 44, 45, 46, 47, 48, 49, 50, 54, 55, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 73, 84, 85, 86]\n"
          ]
        }
      ],
      "source": [
        "central = 54\n",
        "\n",
        "vis = TreeEdgesRecorder(N)\n",
        "rx.bfs_search(g, [central], vis)\n",
        "best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))\n",
        "print(\"Qubits selected:\", best_qubits)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 86,
      "id": "5e2e2af5-34b5-4bb5-b393-18e87d56db08",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/5e2e2af5-34b5-4bb5-b393-18e87d56db08-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 86,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from rustworkx.visualization import graphviz_draw\n",
        "\n",
        "tree = rx.PyDiGraph()\n",
        "tree.extend_from_weighted_edge_list(vis.edges)\n",
        "tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])\n",
        "\n",
        "graphviz_draw(tree, method=\"dot\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0c481352-267a-4a34-85aa-32149bc4674a",
      "metadata": {},
      "source": [
        "Die Tiefe dieses Baumes beträgt 6.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 87,
      "id": "5d81d890-a939-455f-81a6-c7e671671ba5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/5d81d890-a939-455f-81a6-c7e671671ba5-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 87,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz3 = QuantumCircuit(max(best_qubits) + 1, N)\n",
        "\n",
        "ghz3.h(tree.edge_list()[0][0])  # apply H-gate to the root node\n",
        "# Apply CNOT from the root node to the each edge.\n",
        "for u, v in tree.edge_list():\n",
        "    ghz3.cx(u, v)\n",
        "ghz3.barrier()  # for visualization\n",
        "ghz3.measure(best_qubits, list(range(N)))\n",
        "ghz3.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 88,
      "id": "b3ae6f53-c321-4a31-b4c7-8dea94267503",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11"
            ]
          },
          "execution_count": 88,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz3.depth()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 89,
      "id": "b9823b6d-a658-40ef-a509-f03e33534af6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/b9823b6d-a658-40ef-a509-f03e33534af6-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 89,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(1, backend=backend)\n",
        "ghz3_transpiled = pm.run(ghz3)\n",
        "ghz3_transpiled.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 90,
      "id": "57a59793-c96b-4286-9ffc-76a3861f454c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 30\n",
            "Two-qubit Depth: 9\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", ghz3_transpiled.depth())\n",
        "print(\n",
        "    \"Two-qubit Depth:\",\n",
        "    ghz3_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1b80ecf5-2bc0-4fc4-835e-e4cf40bcf9d9",
      "metadata": {},
      "source": [
        "Überraschenderweise nahm die Baumtiefe von 5 auf 6 zu, während die Zwei-Qubit-Tiefe von 9 auf 8 sank! Verwenden wir also die letztere Schaltung.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7046ce9c-cce8-4fca-9875-f9ef78ccc488",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-on-target-hardware\" />\n",
        "\n",
        "#### Schritt 3: Auf der Zielhardware ausführen\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4ca22249-5efa-4ea1-a88e-f89d3235260b",
      "metadata": {},
      "outputs": [],
      "source": [
        "res = execute_ghz_fidelity(\n",
        "    ghz_circuit=ghz3,\n",
        "    physical_qubits=best_qubits,\n",
        "    backend=backend,\n",
        "    sampler_options=opts,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 95,
      "id": "bfbabf7e-421c-4ccd-8dfd-ec8cf5c14ebc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE DONE\n"
          ]
        }
      ],
      "source": [
        "job_s = service.job(res[0])  # Use your job id showed above.\n",
        "job_e = service.job(res[1])\n",
        "print(job_s.status(), job_e.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7615f4df-9efe-435a-9521-356041db7b8f",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-results\" />\n",
        "\n",
        "#### Schritt 4: Nachbearbeitung der Ergebnisse\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 96,
      "id": "670788ad-a209-4d0d-9b6d-9961b8d06900",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "N=30: |00..0>: 4, |11..1>: 218, |3rd>: 265 (111111111111111011111111111111)\n",
            "P(|00..0>)=0.0001, P(|11..1>)=0.00545\n",
            "REM: Coherence (non-diagonal): 0.187073\n",
            "GHZ fidelity = 0.096312 ± 0.003254\n",
            "GME (genuinely multipartite entangled) test: Failed\n"
          ]
        }
      ],
      "source": [
        "N = 30\n",
        "# Check fidelity from job IDs\n",
        "res = check_ghz_fidelity_from_jobs(\n",
        "    sampler_job=job_s,\n",
        "    estimator_job=job_e,\n",
        "    num_qubits=N,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6eb15797-36a9-4844-b7ec-14a607f6a784",
      "metadata": {},
      "source": [
        "Wie Sie sehen können, hat dieses Ergebnis die Kriterien nicht erfüllt.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 97,
      "id": "66c87870",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/66c87870-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 97,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# It will take some time\n",
        "result = job_s.result()\n",
        "plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "43a2f073-b19c-46b4-a7b4-40d2c705c3b7",
      "metadata": {},
      "source": [
        "<span id=\"4-strategy-3-run-with-the-error-suppression-options\" />\n",
        "\n",
        "## 4. Strategie 3. Mit den Optionen zur Fehlerunterdrückung ausführen\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2c40dc0f-93a0-4fae-980f-b7c5ab449a40",
      "metadata": {},
      "source": [
        "Sie können die Optionen zur Fehlerunterdrückung im Sampler unter „ V2 “ festlegen. Weitere Informationen finden Sie im Leitfaden [zum Rauschmanagement des](/docs/guides/sampler-noise-management) Samplers und in der [`ExecutionOptionsV2`](/docs/api/qiskit-ibm-runtime/options-models-execution-options) API-Referenz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 98,
      "id": "04f91676-6218-4875-bc44-5281b83a0718",
      "metadata": {},
      "outputs": [],
      "source": [
        "opts = SamplerOptions()\n",
        "opts.dynamical_decoupling.enable = True\n",
        "opts.execution.rep_delay = 0.0005\n",
        "opts.twirling.enable_gates = True"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8e630f61-ae43-46ee-85d6-daf0cd89f94d",
      "metadata": {},
      "outputs": [],
      "source": [
        "res = execute_ghz_fidelity(\n",
        "    ghz_circuit=ghz3,\n",
        "    physical_qubits=best_qubits,\n",
        "    backend=backend,\n",
        "    sampler_options=opts,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 105,
      "id": "42b659d3-e003-45d4-b460-8a3db6995e34",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE DONE\n"
          ]
        }
      ],
      "source": [
        "job_s = service.job(res[0])  # Use your job id showed above.\n",
        "job_e = service.job(res[1])\n",
        "print(job_s.status(), job_e.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 106,
      "id": "bf3cc6a7-4d02-4f90-a411-57fc2eac0513",
      "metadata": {},
      "outputs": [],
      "source": [
        "N = 30"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 107,
      "id": "1faa3e59-386e-47e3-ae96-101ecb3c08df",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "N=30: |00..0>: 1459, |11..1>: 1543, |3rd>: 359 (111111111111111111111111111110)\n",
            "P(|00..0>)=0.036475, P(|11..1>)=0.038575\n",
            "REM: Coherence (non-diagonal): 0.165532\n",
            "GHZ fidelity = 0.120291 ± 0.003369\n",
            "GME (genuinely multipartite entangled) test: Failed\n"
          ]
        }
      ],
      "source": [
        "# Check fidelity from job IDs\n",
        "res = check_ghz_fidelity_from_jobs(\n",
        "    sampler_job=job_s,\n",
        "    estimator_job=job_e,\n",
        "    num_qubits=N,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 108,
      "id": "166856be-ad3a-40d6-837c-3eedda374891",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/166856be-ad3a-40d6-837c-3eedda374891-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 108,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# It will take some time\n",
        "result = job_s.result()\n",
        "plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c8547268-3604-4fe9-b52c-f48f0d036647",
      "metadata": {},
      "source": [
        "Das Ergebnis hat sich verbessert, erfüllt aber immer noch nicht die Kriterien.\n",
        "\n",
        "Wir haben bisher drei Ideen gesehen. Sie können diese Ideen kombinieren und erweitern, oder Sie entwickeln Ihre eigenen Ideen, um eine bessere GHZ-Schaltung zu schaffen. Schauen wir uns das Ziel noch einmal an.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "70b460c8",
      "metadata": {},
      "source": [
        "<span id=\"5-your-goal-recap\" />\n",
        "\n",
        "## 5. Ihr Ziel (Zusammenfassung)\n",
        "\n",
        "Bauen Sie eine GHZ-Schaltung für 20 Qubits oder mehr, so dass das Messergebnis die Kriterien erfüllt: Die Wiedergabetreue des GHZ-Zustands > 0.5.\n",
        "\n",
        "* Sie müssen ein Eagle-Gerät (z. B. `ibm_brisbane`) verwenden und die Schusszahl auf 40.000 einstellen.\n",
        "* Führen Sie die GHZ-Schaltung mit der Funktion `execute_ghz_fidelity` aus, und berechnen Sie die Wiedergabetreue mit der Funktion `check_ghz_fidelity_from_jobs` .\n",
        "  Sie müssen die größte Qubits - GHZ-Schaltung finden, die die Kriterien erfüllt. Schreiben Sie Ihren Code unten, zeigen Sie das Ergebnis mit der Funktion `check_ghz_fidelity_from_jobs` .\n",
        "\n",
        "Jetzt implementieren wir den gleichen GHZ-Workflow wie im vorangegangenen Material, aber auf einem Heron-Gerät. Auf diese Weise können Sie einige Erfahrungen mit dem Layout und den Funktionen der Heron-Prozessoren sammeln. Es werden keine neuen Strategien eingeführt.\n",
        "\n",
        "*Die ungefähre QPU-Zeit für die Durchführung dieses nächsten Experiments beträgt 4 m 40 s.*\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2db80d2a-1c87-4d94-a2d5-17db3fddbb96",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Device ibm_kingston Loaded with 156 qubits\n",
            "Two Qubit Gate: cz\n"
          ]
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.backend(\"ibm_kingston\")\n",
        "# backend = service.backend(\"ibm_fez\")\n",
        "\n",
        "twoq_gate = \"cz\"\n",
        "print(f\"Device {backend.name} Loaded with {backend.num_qubits} qubits\")\n",
        "print(f\"Two Qubit Gate: {twoq_gate}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 141,
      "id": "5490bc61-0f0d-411b-b9bc-53d68ff2f877",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Bad readout qubits: [112, 113, 120, 131, 146]\n",
            "Bad CZ gates: [(111, 112), (112, 111), (112, 113), (113, 112), (120, 121), (121, 120), (130, 131), (131, 130), (145, 146), (146, 145), (146, 147), (147, 146)]\n"
          ]
        }
      ],
      "source": [
        "BAD_READOUT_ERROR_THRESHOLD = 0.1\n",
        "BAD_CZGATE_ERROR_THRESHOLD = 0.1\n",
        "bad_readout_qubits = [\n",
        "    q\n",
        "    for q in range(backend.num_qubits)\n",
        "    if backend.target[\"measure\"][(q,)].error > BAD_READOUT_ERROR_THRESHOLD\n",
        "]\n",
        "bad_czgate_edges = [\n",
        "    qpair\n",
        "    for qpair in backend.target[\"cz\"]\n",
        "    if backend.target[\"cz\"][qpair].error > BAD_CZGATE_ERROR_THRESHOLD\n",
        "]\n",
        "print(\"Bad readout qubits:\", bad_readout_qubits)\n",
        "print(\"Bad CZ gates:\", bad_czgate_edges)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 142,
      "id": "71df32ed-4b7c-4a7a-8fcf-bd508478b6ad",
      "metadata": {},
      "outputs": [],
      "source": [
        "g = backend.coupling_map.graph.copy().to_undirected()\n",
        "g.remove_edges_from(\n",
        "    bad_czgate_edges\n",
        ")  # remove edge first (otherwise might fail with a NoEdgeBetweenNodes error)\n",
        "g.remove_nodes_from(bad_readout_qubits)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 145,
      "id": "e4336cf4-95e6-41a2-b24c-74d1cec53c36",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/e4336cf4-95e6-41a2-b24c-74d1cec53c36-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 145,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qubit_color = []\n",
        "for i in range(backend.num_qubits):\n",
        "    if i in bad_readout_qubits:\n",
        "        qubit_color.append(\"#000000\")  # black\n",
        "    else:\n",
        "        qubit_color.append(\"#8c00ff\")  # purple\n",
        "line_color = []\n",
        "for e in backend.target.build_coupling_map().get_edges():\n",
        "    if e in bad_czgate_edges:\n",
        "        line_color.append(\"#ffffff\")  # white\n",
        "    else:\n",
        "        line_color.append(\"#888888\")  # gray\n",
        "plot_gate_map(\n",
        "    backend,\n",
        "    qubit_color=qubit_color,\n",
        "    line_color=line_color,\n",
        "    qubit_size=60,\n",
        "    font_size=30,\n",
        "    figsize=(10, 10),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 146,
      "id": "5afaf902-a59f-4ae9-b4fb-95b9d861676e",
      "metadata": {},
      "outputs": [],
      "source": [
        "N = 40\n",
        "central = 100  # Select the center node manually\n",
        "# c_degree = dict(rx.betweenness_centrality(g))\n",
        "# central = max(c_degree, key=c_degree.get)\n",
        "# central"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 147,
      "id": "24bf4ea2-dd6f-442e-a989-5c8b29e93847",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Qubits selected: [61, 65, 76, 77, 80, 81, 82, 83, 84, 85, 86, 87, 96, 97, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 116, 117, 121, 122, 123, 124, 125, 126, 127, 136, 140, 141, 142, 143, 144, 145]\n"
          ]
        }
      ],
      "source": [
        "class TreeEdgesRecorder(rx.visit.BFSVisitor):\n",
        "    def __init__(self, N):\n",
        "        self.edges = []\n",
        "        self.N = N\n",
        "\n",
        "    def tree_edge(self, edge):\n",
        "        self.edges.append(edge)\n",
        "        if len(self.edges) >= self.N - 1:\n",
        "            raise rx.visit.StopSearch()\n",
        "\n",
        "\n",
        "vis = TreeEdgesRecorder(N)\n",
        "rx.bfs_search(g, [central], vis)\n",
        "best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))\n",
        "print(\"Qubits selected:\", best_qubits)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 149,
      "id": "e684af6c-aa99-460d-b0f5-9d5042fb80b0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/e684af6c-aa99-460d-b0f5-9d5042fb80b0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 149,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qubit_color = []\n",
        "for i in range(backend.num_qubits):\n",
        "    if i in bad_readout_qubits:\n",
        "        qubit_color.append(\"#000000\")\n",
        "    elif i in best_qubits:\n",
        "        qubit_color.append(\"#ff00dd\")\n",
        "    else:\n",
        "        qubit_color.append(\"#8c00ff\")\n",
        "line_color = []\n",
        "for e in backend.target.build_coupling_map().get_edges():\n",
        "    if e in bad_czgate_edges:\n",
        "        line_color.append(\"#ffffff\")\n",
        "    else:\n",
        "        line_color.append(\"#888888\")\n",
        "plot_gate_map(\n",
        "    backend,\n",
        "    qubit_color=qubit_color,\n",
        "    line_color=line_color,\n",
        "    qubit_size=60,\n",
        "    font_size=30,\n",
        "    figsize=(10, 10),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 150,
      "id": "f3ff2dd9-a304-409c-a0e4-3b23e85f282e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/f3ff2dd9-a304-409c-a0e4-3b23e85f282e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 150,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from rustworkx.visualization import graphviz_draw\n",
        "\n",
        "tree = rx.PyDiGraph()\n",
        "tree.extend_from_weighted_edge_list(vis.edges)\n",
        "tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])\n",
        "\n",
        "graphviz_draw(tree, method=\"dot\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 151,
      "id": "60b6526c-55ed-4621-b91b-4ceeef34de62",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/60b6526c-55ed-4621-b91b-4ceeef34de62-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 151,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz_h = QuantumCircuit(max(best_qubits) + 1, N)\n",
        "\n",
        "ghz_h.h(tree.edge_list()[0][0])  # apply H-gate to the root node\n",
        "# Apply CNOT from the root node to the each edge.\n",
        "for u, v in tree.edge_list():\n",
        "    ghz_h.cx(u, v)\n",
        "ghz_h.barrier()  # for visualization\n",
        "ghz_h.measure(best_qubits, list(range(N)))\n",
        "ghz_h.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 152,
      "id": "1365cbef-f587-4a34-9d68-5f1d9afc0f43",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "15"
            ]
          },
          "execution_count": 152,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ghz_h.depth()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 153,
      "id": "b96e6342-e08b-4728-8985-7a015ee16797",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/b96e6342-e08b-4728-8985-7a015ee16797-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 153,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm = generate_preset_pass_manager(1, backend=backend)\n",
        "ghz_h_transpiled = pm.run(ghz_h)\n",
        "ghz_h_transpiled.draw(output=\"mpl\", idle_wires=False, fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 154,
      "id": "614a2f14-aff8-4942-8709-9a40c5e041d9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Depth: 45\n",
            "Two-qubit Depth: 13\n"
          ]
        }
      ],
      "source": [
        "print(\"Depth:\", ghz_h_transpiled.depth())\n",
        "print(\n",
        "    \"Two-qubit Depth:\",\n",
        "    ghz_h_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 155,
      "id": "da2e2f98-b79b-42bd-8fbd-3ccb824c6729",
      "metadata": {},
      "outputs": [],
      "source": [
        "opts = SamplerOptions()\n",
        "opts.dynamical_decoupling.enable = True\n",
        "opts.execution.rep_delay = 0.0005\n",
        "opts.twirling.enable_gates = True"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0d659b1b-fe1a-4e42-ac8c-08b05514590b",
      "metadata": {},
      "outputs": [],
      "source": [
        "res = execute_ghz_fidelity(\n",
        "    ghz_circuit=ghz_h,\n",
        "    physical_qubits=best_qubits,\n",
        "    backend=backend,\n",
        "    sampler_options=opts,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 174,
      "id": "0162d22f-899c-4fed-86b8-dbb45673c2c0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "RUNNING RUNNING\n"
          ]
        }
      ],
      "source": [
        "job_s = service.job(res[0])  # Use your job id showed above.\n",
        "job_e = service.job(res[1])\n",
        "print(job_s.status(), job_e.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 170,
      "id": "c6d1bad8-381c-4ba9-be2f-b1575741af53",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "N=40: |00..0>: 3186, |11..1>: 3277, |3rd>: 620 (1111111011111111111111111111111111111111)\n",
            "P(|00..0>)=0.07965, P(|11..1>)=0.081925\n",
            "REM: Coherence (non-diagonal): 0.029987\n",
            "GHZ fidelity = 0.095781 ± 0.002619\n",
            "GME (genuinely multipartite entangled) test: Failed\n"
          ]
        }
      ],
      "source": [
        "# Check fidelity from job IDs\n",
        "N = 40\n",
        "res = check_ghz_fidelity_from_jobs(\n",
        "    sampler_job=job_s,\n",
        "    estimator_job=job_e,\n",
        "    num_qubits=N,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 171,
      "id": "671f57ea-7117-4d17-998f-73baa6f4463e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-iii/extracted-outputs/671f57ea-7117-4d17-998f-73baa6f4463e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 171,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# It will take some time\n",
        "result = job_s.result()\n",
        "plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c817f572-1c67-4ed8-b466-ecdf5c41d88b",
      "metadata": {},
      "source": [
        "Glückwunsch! Sie haben Ihre Einführung in das Quantencomputing im großen Maßstab abgeschlossen! Sie sind nun in der Lage, sinnvolle Beiträge zum Streben nach einem Quantenvorteil zu leisten! Vielen Dank, dass Sie IBM Quantum® zu einem Teil Ihrer persönlichen Quantenreise gemacht haben.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ddd145f",
      "metadata": {},
      "source": [
        "<span id=\"post-course-survey\" />\n",
        "\n",
        "## Umfrage nach dem Kurs\n",
        "\n",
        "Herzlichen Glückwunsch zum Abschluss dieses Kurses! Bitte nehmen Sie sich einen Moment Zeit und helfen Sie uns, unseren Kurs zu verbessern, indem Sie die folgende [kurze Umfrage](https://your.feedback.ibm.com/jfe/form/SV_5vvmdT5yFFetkgK) ausfüllen. Ihr Feedback wird zur Verbesserung unseres Inhaltsangebots und der Benutzerfreundlichkeit verwendet. Danke!\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"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}