{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "alleged-prairie",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Utilitaire III\"\n",
        "description: \"Cette leçon porte sur la construction d'un circuit GHZ pour 20 qubits ou plus afin que, lors de la mesure, la fidélité de l'état GHZ sur l' 0.5 e.\"\n",
        "---\n",
        "\n",
        "<span id=\"utility-scale-experiment-iii\" />\n",
        "\n",
        "# Expérience à l'échelle industrielle III\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Toshinari Itoko, Tamiya Onodera, Kifumi Numata (19 juillet 2024)\n",
        "\n",
        "  [Télécharger le pdf](https://ibm.ent.box.com/public/static/fw1538dogvyv0qbfqg8tan1k2fs27mfv.zip) de la conférence originale. Notez que certains extraits de code peuvent devenir obsolètes car il s'agit d'images statiques.\n",
        "\n",
        "  *Le temps approximatif d'exécution de cette première expérience par la QPU est de 12 m 30 s. Il y a une expérience supplémentaire ci-dessous qui nécessite environ 4 m.*\n",
        "\n",
        "  (Note : ce carnet pourrait ne pas être évalué dans le temps imparti sur le plan ouvert. Veillez à utiliser les ressources informatiques quantiques à bon escient)\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. Introduction\n",
        "\n",
        "Passons brièvement en revue les états GHZ et le type de distribution que l'on peut attendre de l'application de `Sampler` à l'un d'entre eux. Ensuite, nous énoncerons explicitement l'objectif de cette leçon.\n",
        "\n",
        "<span id=\"11-ghz-state\" />\n",
        "\n",
        "### 1.1 État GHZ\n",
        "\n",
        "L'état GHZ (état de Greenberger-Horne-Zeilinger) pour les qubits $n$ est défini comme suit\n",
        "\n",
        "$$\n",
        "\\frac{1}{\\sqrt 2}(|0\\rangle ^ {\\otimes n}+ |1\\rangle^ {\\otimes n})\n",
        "$$\n",
        "\n",
        "Naturellement, il peut être créé pour 6 qubits avec le circuit quantique suivant.\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": [
        "La profondeur n'est pas trop importante, même si vous savez, grâce aux leçons précédentes, que vous pouvez faire mieux. Choisissons un backend et transposons ce 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": [
        "Là encore, la profondeur de deux qubits transposée n'est pas trop importante. Mais pour travailler avec un état GHZ sur un plus grand nombre de qubits, il est clair qu'il faut penser à optimiser le circuit. Exécutons cette opération à l'aide de `Sampler` et voyons ce que donne un véritable ordinateur quantique.\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": [
        "Voici le résultat du circuit GHZ à 6 qubits. Comme vous pouvez le constater, les états de tous les $|0\\rangle$ 's et de tous les $|1\\rangle$ 's dominent, mais les erreurs sont substantielles. Essayons de voir quelle taille de circuit GHZ il est possible de réaliser avec un dispositif Eagle, tout en obtenant des résultats où les états corrects ont au moins plus de 50 % de chances de se produire.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9942f911-bfbf-4a16-8349-05bc490885e6",
      "metadata": {},
      "source": [
        "<span id=\"12-your-goal\" />\n",
        "\n",
        "### 1.2 Votre objectif\n",
        "\n",
        "Construire un circuit GHZ pour 20 qubits ou plus de sorte que, lors de la mesure, **la fidélité de votre état GHZ > 0.5**\n",
        "Remarque :\n",
        "\n",
        "* Vous devez utiliser un appareil Eagle (`min_num_qubits=127`) et régler le nombre de tirs sur 40 000.\n",
        "* Vous devez exécuter le circuit GHZ à l'aide de la fonction `execute_ghz_fidelity` et calculer la fidélité à l'aide de la fonction `check_ghz_fidelity_from_jobs` .\n",
        "\n",
        "Il s'agit d'un exercice indépendant, dans lequel vous mettez à profit ce que vous avez appris jusqu'à présent dans ce cours.\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": [
        "Dans ce carnet, nous appliquerons trois stratégies pour créer de bons états GHZ en utilisant 16 qubits et 30 qubits. Ces approches s'appuient sur des stratégies que vous connaissez déjà grâce aux leçons précédentes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "371c0d79",
      "metadata": {},
      "source": [
        "<span id=\"2-strategy-1-noise-aware-qubit-selection\" />\n",
        "\n",
        "## 2. Stratégie 1. Sélection de qubits sensible au bruit\n",
        "\n",
        "Nous commençons par spécifier un backend. Comme nous allons beaucoup travailler avec les propriétés d'un backend spécifique, il est préférable de spécifier un backend, plutôt que d'utiliser l'option `least_busy` .\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": [
        "Nous allons construire un circuit impliquant de nombreuses portes à deux qubits. Il est logique que nous utilisions les qubits qui présentent les erreurs les plus faibles lors de la mise en œuvre de ces portes à deux qubits. Trouver la meilleure \"chaîne de qubits\" sur la base des erreurs signalées sur 2q-gate est un problème non trivial. Mais nous pouvons définir quelques fonctions pour nous aider à déterminer les meilleurs qubits à utiliser.\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": [
        "Nous utiliserons les fonctions ci-dessus pour trouver tous les chemins simples de N qubits entre toutes les paires de nœuds du graphe (Référence : [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",
        "Ensuite, en utilisant la fonction `path_fidelity` créée ci-dessus, nous trouverons la meilleure chaîne de qubits qui a la plus grande fidélité au chemin.\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": [
        "Représentons la meilleure chaîne de qubits, en rose, dans le diagramme de couplage.\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 Construisez un circuit GHZ sur la meilleure chaîne de qubits\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "273f469d-3273-40ee-922d-4ad43e1d53ad",
      "metadata": {},
      "source": [
        "Nous choisissons un qubit au milieu de la chaîne pour appliquer la porte H en premier. Cela devrait réduire la profondeur du circuit d'environ la moitié.\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": [
        "Veillez à exécuter la cellule suivante après que les statuts des travaux ci-dessus sont devenus \"DONE\", afin de montrer le résultat à l'aide de la fonction `check_ghz_fidelity_from_jobs` .\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": [
        "Ce résultat ne répond pas aux critères. Passons à l'idée suivante.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ce16e4cb",
      "metadata": {},
      "source": [
        "<span id=\"3-strategy-2-balanced-tree-of-qubits\" />\n",
        "\n",
        "## 3. Stratégie 2. Arbre équilibré de qubits\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "45d96784",
      "metadata": {},
      "source": [
        "L'idée suivante est de trouver un arbre équilibré de qubits. En utilisant l'arbre plutôt que la chaîne, la profondeur du circuit devrait diminuer. Avant cela, nous supprimons du graphe de couplage les nœuds présentant de \"mauvaises\" erreurs de lecture et les arêtes présentant de \"mauvaises\" erreurs de porte.\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": [
        "Dessinons le graphe de la carte de couplage sans les mauvaises arêtes et les mauvais 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": [
        "Nous essayons de créer un état GHZ à 16 qubits comme précédemment.\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": [
        "Nous appelons la fonction `betweenness_centrality` pour trouver un qubit pour le nœud racine. Le nœud ayant la valeur la plus élevée de la centralité d'interdépendance se trouve au centre du graphe. Référence : [https://www.rustworkx.org/tutorial/betweenness\\_centrality.html](https://www.rustworkx.org/tutorial/betweenness_centrality.html)\n",
        "\n",
        "Vous pouvez également le sélectionner manuellement.\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": [
        "En commençant par le nœud racine, générer un arbre par la méthode BFS (breadth first search). Référence : [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": [
        "Représentons les qubits sélectionnés, en rose, dans le diagramme de couplage.\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": [
        "Montrons la structure arborescente des 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": [
        "La profondeur du circuit est désormais bien inférieure à celle de la structure en chaîne.\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": [
        "Nous avons passé avec succès les critères avec la structure arborescente équilibrée!\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": [
        "Essayons maintenant de créer un état GHZ plus grand : un état GHZ de 30 qubits.\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",
        "Nous suivrons le cadre des [modèles Qiskit](/docs/guides/intro-to-patterns).\n",
        "\n",
        "* Étape 1 : Tracer le problème à l'aide de circuits et d'opérateurs quantiques\n",
        "* Étape 2 : Optimisation pour le matériel cible\n",
        "* Étape 3 : Exécution sur le matériel cible\n",
        "* Étape 4 : Post-traitement des résultats\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",
        "#### Étape 1 : Mettre en correspondance le problème avec les circuits et opérateurs quantiques Étape 2 : Optimiser pour le matériel cible\n",
        "\n",
        "Ici, nous sélectionnons manuellement le nœud racine.\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": [
        "La profondeur de cet arbre est de 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 Sélectionnez manuellement un autre nœud racine\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": [
        "La profondeur de cet arbre est de 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": [
        "De manière surprenante, alors que la profondeur de l'arbre est passée de 5 à 6, la profondeur des deux qubits a diminué de 9 à 8! Utilisons donc ce dernier circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7046ce9c-cce8-4fca-9875-f9ef78ccc488",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-on-target-hardware\" />\n",
        "\n",
        "#### Étape 3 : Exécuter sur le matériel cible\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",
        "#### Étape 4 : Post-traitement des résultats\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": [
        "Comme vous pouvez le constater, ce résultat ne répond pas aux critères.\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. Stratégie 3. Exécutez avec les options de suppression des erreurs\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2c40dc0f-93a0-4fae-980f-b7c5ab449a40",
      "metadata": {},
      "source": [
        "Vous pouvez définir les options de suppression des erreurs dans l' V2 de Sampler. Pour plus d'informations, consultez le guide [de gestion du bruit de l'échantillonneur](/docs/guides/sampler-noise-management) et la [`ExecutionOptionsV2`](/docs/api/qiskit-ibm-runtime/options-execution-options-v2) documentation de l'API.\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": [
        "Le résultat s'est amélioré mais ne répond toujours pas aux critères.\n",
        "\n",
        "Nous avons vu trois idées jusqu'à présent. Vous pouvez combiner et développer ces idées ou trouver vos propres idées pour créer un meilleur circuit GHZ. Reprenons maintenant l'objectif.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "70b460c8",
      "metadata": {},
      "source": [
        "<span id=\"5-your-goal-recap\" />\n",
        "\n",
        "## 5. Votre objectif (récapitulatif)\n",
        "\n",
        "Construisez un circuit GHZ pour 20 qubits ou plus afin que le résultat de la mesure réponde aux critères : La fidélité de votre état GHZ > 0.5.\n",
        "\n",
        "* Vous devez utiliser un appareil Eagle (tel que `ibm_brisbane`) et définir le nombre de tirs à 40 000.\n",
        "* Vous devez exécuter le circuit GHZ à l'aide de la fonction `execute_ghz_fidelity` et calculer la fidélité à l'aide de la fonction `check_ghz_fidelity_from_jobs` .\n",
        "  Vous devez trouver les plus gros qubits - circuit GHZ qui répondent aux critères. Ecrivez votre code ci-dessous, montrez le résultat avec la fonction `check_ghz_fidelity_from_jobs` .\n",
        "\n",
        "Nous allons maintenant mettre en œuvre le même flux de travail GHZ que dans l'article précédent, mais sur un appareil Heron. Cela vous permet d'acquérir une certaine expérience de la présentation et des fonctionnalités des processeurs Heron. Aucune nouvelle stratégie n'est introduite.\n",
        "\n",
        "*Le temps approximatif d'exécution de cette expérience par la QPU est de 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": [
        "Félicitations ! Vous avez terminé votre introduction à l'informatique quantique à grande échelle! Vous êtes maintenant prêt à apporter des contributions significatives dans la quête d'un avantage quantique! Merci d'avoir fait de IBM Quantum® une partie de votre voyage quantique personnel.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ddd145f",
      "metadata": {},
      "source": [
        "<span id=\"post-course-survey\" />\n",
        "\n",
        "## Enquête post-formation\n",
        "\n",
        "Félicitations pour avoir suivi ce cours! Veuillez prendre un moment pour nous aider à améliorer notre cours en répondant au [questionnaire rapide](https://your.feedback.ibm.com/jfe/form/SV_5vvmdT5yFFetkgK) suivant. Vos commentaires seront utilisés pour améliorer notre offre de contenu et l'expérience des utilisateurs. Merci !\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
}