{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "alleged-prairie",
      "metadata": {},
      "source": [
        "---\n",
        "title: Utility III\n",
        "description: This lesson is about to build a GHZ circuit for 20 qubits or more so that, upon measurement, the fidelity of GHZ state over 0.5.\n",
        "---\n",
        "\n",
        "# Utility-scale experiment III\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Toshinari Itoko, Tamiya Onodera, Kifumi Numata (19 July 2024)\n",
        "\n",
        "  [Download the pdf](https://ibm.ent.box.com/public/static/fw1538dogvyv0qbfqg8tan1k2fs27mfv.zip) of the original lecture. Note that some code snippets might become deprecated since these are static images.\n",
        "\n",
        "  *Approximate QPU time to run this first experiment is 12 m 30 s. There is an additional experiment below that requires approximately 4 m.*\n",
        "\n",
        "  (Note: this notebook might not evaluate in the time allowed on the Open Plan. Be sure to use quantum computing resources wisely.)\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": [
        "## 1. Introduction\n",
        "\n",
        "Let us briefly review GHZ states, and what sort of distribution you might expect from `Sampler` applied to one. Then we will spell out the goal of this lesson explicitly.\n",
        "\n",
        "### 1.1 GHZ state\n",
        "\n",
        "The GHZ state (Greenberger-Horne-Zeilinger state) for $n$ qubits is defined as\n",
        "\n",
        "$$\n",
        "\\frac{1}{\\sqrt 2}(|0\\rangle ^ {\\otimes n}+ |1\\rangle^ {\\otimes n})\n",
        "$$\n",
        "\n",
        "Naturally, it can be created for 6 qubits with the following quantum circuit.\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": [
        "The depth is not too large, though you know from previous lessons that you can do better. Let's pick a backend and transpile this circuit.\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": [
        "Again the transpiled two-qubit depth is not too large. But to work with a GHZ state on more qubits, you will clearly need to think about optimizing the circuit. Let's run this using `Sampler` and see what a real quantum computer returns.\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": [
        "This is the result of the 6-qubit GHZ circuit. As you can see, the states of all $|0\\rangle$'s and all $|1\\rangle$'s do dominate, but the errors are substantial. Let's try to see how large a GHZ circuit you can make with an Eagle device, while still getting results where the correct states are at least more than 50% likely.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9942f911-bfbf-4a16-8349-05bc490885e6",
      "metadata": {},
      "source": [
        "### 1.2 Your goal\n",
        "\n",
        "Build a GHZ circuit for 20 qubits or more so that, upon measurement, **the fidelity of your GHZ state > 0.5.**\n",
        "Note:\n",
        "\n",
        "* You need to use an Eagle device (`min_num_qubits=127`) and set the shots number as 40,000.\n",
        "* You should execute the GHZ circuit using the `execute_ghz_fidelity` function, and calculate the fidelity using the `check_ghz_fidelity_from_jobs` function.\n",
        "\n",
        "This is intended as an independent exercise, in which you leverage what you have learned so far in this course.\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 this notebook, we will apply three strategies to creating good GHZ states using 16 qubits and 30 qubits. These approaches build on strategies you already know from previous lessons.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "371c0d79",
      "metadata": {},
      "source": [
        "## 2. Strategy 1. Noise-aware qubit selection\n",
        "\n",
        "We first specify a backend. Because we will be working extensively with the properties of a specific backend, it is a good idea to specify one backend, as opposed to using the `least_busy` option.\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": [
        "We are going to build a circuit involving many two-qubit gates. It makes sense for us to use the qubits that have the lowest errors when implementing those two-qubit gates. Finding the best \"qubit chain\" based on the reported 2q-gate errors is a nontrivial problem. But we can define a few functions to help us determine the best qubits to use.\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": [
        "We will use the functions above to find all the simple paths of N qubits between all pairs of nodes in the graph (Reference: [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",
        "Then, using the `path_fidelity` function created above, we will find the best qubit chain which has the largest path fidelity.\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": [
        "Let's plot the best qubit chain, shown in pink, in the coupling map diagram.\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": [
        "### 2.1 Build a GHZ circuit on the best qubit chain\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "273f469d-3273-40ee-922d-4ad43e1d53ad",
      "metadata": {},
      "source": [
        "We choose a qubit in the middle of the chain to first apply the H gate to. This should reduce the depth of the circuit by about half.\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": [
        "Be careful to execute the next cell after the above jobs statuses have become 'DONE', to show the result using the `check_ghz_fidelity_from_jobs` function.\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": [
        "This result does not meet the criteria. Let's move to the next idea.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ce16e4cb",
      "metadata": {},
      "source": [
        "## 3. Strategy 2. Balanced tree of qubits\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "45d96784",
      "metadata": {},
      "source": [
        "The next idea is to find a balanced tree of qubits. Using the tree rather than the chain, the circuit depth should become lower. Before that, we remove nodes with \"bad\" readout errors and edges with \"bad\" gate errors from the coupling graph.\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": [
        "Let's draw the coupling map graph without the bad edges and bad 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": [
        "We try to create a 16-qubit GHZ state as before.\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": [
        "We call the `betweenness_centrality` function to find a qubit for the root node. The node with the highest value of betweenness centrality is at the center of the graph. Reference: [https://www.rustworkx.org/tutorial/betweenness\\_centrality.html](https://www.rustworkx.org/tutorial/betweenness_centrality.html)\n",
        "\n",
        "Or you can select it manually.\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": [
        "Starting from the root node, generate a tree by breadth first search (BFS). Reference: [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": [
        "Let us plot the selected qubits, shown in pink, in the coupling map diagram.\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": [
        "Let us show the tree structure of qubits.\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": [
        "The depth of the circuit has now become much lower than that in the chain structure.\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": [
        "We have successfully passed the criteria with the balanced tree structure!\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": [
        "Now, let us try to create a larger GHZ state: a 30-qubit GHZ state.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "816914ce-52dd-4df5-9943-0acc606240c3",
      "metadata": {},
      "source": [
        "### 3.1 N = 30\n",
        "\n",
        "We will follow the [Qiskit patterns](/docs/guides/intro-to-patterns) framework.\n",
        "\n",
        "* Step 1: Map problem to quantum circuits and operators\n",
        "* Step 2: Optimize for target hardware\n",
        "* Step 3: Execute on target hardware\n",
        "* Step 4: Post-process results\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20e6b416-eee7-4728-9fef-0de34b5ecbac",
      "metadata": {},
      "source": [
        "#### Step 1: Map problem to quantum circuits and operators and Step 2: Optimize for target hardware\n",
        "\n",
        "Here we select the root node manually.\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": [
        "The depth of this tree is 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": [
        "### 3.2 Select a different root node manually\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": [
        "The depth of this tree is 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": [
        "Surprisingly, while the tree depth increased from 5 to 6, the two-qubit depth decreased from 9 to 8! So let us use the latter circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7046ce9c-cce8-4fca-9875-f9ef78ccc488",
      "metadata": {},
      "source": [
        "#### Step 3: Execute on target hardware\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": [
        "#### Step 4: Post-process results\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": [
        "As you can see, this result has not met the criteria.\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": [
        "## 4. Strategy 3. Run with the error suppression options\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2c40dc0f-93a0-4fae-980f-b7c5ab449a40",
      "metadata": {},
      "source": [
        "You can set the error suppression options in Sampler V2. Refer to the [Sampler noise management](/docs/guides/sampler-noise-management) guide and the [`ExecutionOptionsV2`](/docs/api/qiskit-ibm-runtime/options-execution-options-v2) API reference for more information.\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": [
        "The result has improved but still has not met the criteria.\n",
        "\n",
        "We have seen three ideas so far. You can combine and expand these ideas or you come up with your own ideas to create a better GHZ circuit. Now let's review the goal again.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "70b460c8",
      "metadata": {},
      "source": [
        "## 5. Your goal (recap)\n",
        "\n",
        "Build a GHZ circuit for 20 qubits or more so that the measurement result meets the criteria: The fidelity of your GHZ state > 0.5.\n",
        "\n",
        "* You need to use an Eagle device (such as `ibm_brisbane`) and set the shots number as 40,000.\n",
        "* You should execute the GHZ circuit using the `execute_ghz_fidelity` function, and calculate the fidelity using the `check_ghz_fidelity_from_jobs` function.\n",
        "  You need to find the biggest qubits - GHZ circuit which meet the criteria. Write your code below, show the result with the function `check_ghz_fidelity_from_jobs` .\n",
        "\n",
        "Now we implement the same GHZ workflow as in the preceding material, but on a Heron device. This gives you some experience with the layout and features of the Heron processors. No new strategies are introduced.\n",
        "\n",
        "*Approximate QPU time to run this next experiment is 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": [
        "Congratulations! You have completed your introduction to utility-scale quantum computing! You are now poised to make meaningful contributions in the quest for quantum advantage! Thank you for making IBM Quantum® part of your personal quantum journey.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ddd145f",
      "metadata": {},
      "source": [
        "## Post-course survey\n",
        "\n",
        "Congratulations on completing this course! Please take a moment to help us improve our course by filling out the following [quick survey](https://your.feedback.ibm.com/jfe/form/SV_5vvmdT5yFFetkgK). Your feedback will be used to enhance our content offering and user experience. Thank you!\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
}