{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "39a1fd9b-a17f-486e-aa7a-38d46b867984",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Utilité I\"\n",
        "description: \"Dans cette leçon, nous utilisons un circuit à l'échelle industrielle qui apparaît dans ce que nous appelons familièrement « l'article sur les services publics », publié dans Nature Vol 618, le 15 juin 2023.\"\n",
        "---\n",
        "\n",
        "<span id=\"utility-scale-experiment-i\" />\n",
        "\n",
        "# Expérience à l'échelle industrielle I\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Tamiya Onodera (5 juillet 2024)\n",
        "\n",
        "  [Télécharger le pdf](https://ibm.ent.box.com/public/static/k38xhk5yaqy9b3gjdmu0ynhnd9i4c3e1.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",
        "  *La durée approximative de cette expérience est de 45 secondes.*\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "f0d73c71-11ba-4dcb-b230-ecea500e8732",
      "metadata": {},
      "source": [
        "<span id=\"1-introduction-to-the-utility-paper\" />\n",
        "\n",
        "## 1. Introduction au document utilitaire\n",
        "\n",
        "Dans cette leçon, nous faisons fonctionner un circuit à l'échelle de l'utilité qui apparaît dans ce que nous appelons officieusement [\"l'article sur l'utilité\"](https://www.nature.com/articles/s41586-023-06096-3) publié dans Nature Vol 618, 15 juin 2023. L'article traite de l'évolution temporelle du modèle d'Ising à champ transverse 2D. En particulier, ils considèrent la dynamique temporelle de l'hamiltonien,\n",
        "\n",
        "$$\n",
        "H = H_{ZZ} + H_X = - J \\sum_{(i,j)} Z_i Z_j + h \\sum_{i} X_i\n",
        "$$\n",
        "\n",
        "où $J > 0$ est le couplage des spins les plus proches avec $i < j$ et $h$ est le champ transversal global.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "c92cf6bb-df3e-4020-b980-edf17188e096",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "Ils simulent la dynamique du spin à partir d'un état initial au moyen d'une décomposition de Trotter du premier ordre de l'opérateur d'évolution temporelle,\n",
        "\n",
        "$$\n",
        "\\begin{aligned}\n",
        " \\exp(-i H_{ZZ} \\delta t) &= \\prod_{(i,j)} \\exp (i J \\delta t Z_i Z_j)  = \\prod_{(i,j)} \\mathrm{R}_{Z_i Z_j} ( - 2 J \\delta t) \\\\\n",
        " \\exp(-i H_X \\delta t) &= \\prod_{i} \\exp (-i h \\delta t X_i )  = \\prod_{i} \\mathrm{R}_{X_i} ( 2 h \\delta t)\n",
        "\\end{aligned}\n",
        "$$\n",
        "\n",
        "dans lequel le temps d'évolution $T$ est discrétisé en $T / \\delta t$ étapes de Trotter et $\\mathrm{R}_{Z_i Z_j}(\\theta_J)$ et $\\mathrm{R}_{X_i}(\\theta_h)$ sont respectivement les portes de rotation $ZZ$ et $X$.\n",
        "\n",
        "Ils ont réalisé des expériences sur un processeur Quantum® Eagle ( IBM ), qui est un dispositif de 127 qubits avec une connectivité heavy-hex, en appliquant les interactions $X$ à tous les qubits et les interactions $ZZ$ à tous les bords de la carte de couplage.  Notez que toutes les interactions du site $ZZ$ ne peuvent pas être appliquées simultanément en raison de la \"dépendance des données\".  C'est pourquoi ils **colorent la carte de couplage** pour les regrouper en couches. Ceux qui se trouvent dans une même couche se voient attribuer la même couleur et peuvent être appliqués en parallèle.\n",
        "\n",
        "En outre, pour des raisons de simplicité expérimentale, ils se sont concentrés sur le cas $\\theta_J=-\\pi /2$.\n",
        "\n",
        "La nouveauté de l'article réside dans le fait qu'ils ont construit des circuits quantiques à une échelle dépassant la simulation de vecteurs d'état, qu'ils les ont exécutés sur des ordinateurs quantiques bruyants et qu'ils sont parvenus à extraire des résultats fiables. En d'autres termes, ils ont démontré l'utilité des ordinateurs quantiques bruyants. Pour ce faire, ils ont appliqué l'extrapolation à bruit nul (ZNE) avec l'amplification probabiliste des erreurs (PEA) afin d'atténuer les erreurs provenant des dispositifs bruyants.\n",
        "\n",
        "À partir de ce moment-là, nous avons appelé ces expériences et circuits \"utility-scale\".\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fcb359b4-469a-4e37-b260-8bf5044d1f60",
      "metadata": {},
      "source": [
        "<span id=\"11-your-goal\" />\n",
        "\n",
        "### 1.1 Votre objectif\n",
        "\n",
        "L'objectif de cette leçon est de construire un circuit utilitaire et de le faire fonctionner sur un processeur Eagle.  L'extraction de résultats fiables dépasse le cadre de ce carnet, d'une part parce que la PEA est une fonctionnalité expérimentale de Qiskit au moment où nous écrivons ces lignes, et d'autre part parce que l'application des ZNE avec la PEA prendra beaucoup de temps.\n",
        "\n",
        "Concrètement, il vous est demandé de construire et de faire fonctionner le circuit correspondant à la figure 4b du document, et de tracer les points \"non atténués\" de votre choix.  Comme vous le voyez, il s'agit d'un circuit de 127 qubits $\\times$ à 60 couches (20 étapes de Trotter) avec le site $\\langle Z_{62} \\rangle$ comme observable.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a4aecf28-fc89-42db-ad8f-67e8afacff87",
      "metadata": {},
      "source": [
        "![image.png](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/utility-i/utility1.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b8c5a5ae-fc75-4fa9-9c70-2de36652f49a",
      "metadata": {},
      "source": [
        "Cela vous semble important? Ne vous inquiétez pas. Les trois dernières leçons de ce cours constituent des tremplins. Pour commencer, nous ferons la démonstration d'une expérience à petite échelle qui consiste à construire et à faire fonctionner sur un faux appareil un circuit de 27 qubits $\\times$ à 6 couches (2 étapes de Trotter) avec le site $\\langle Z_{13} \\rangle$ comme observable.\n",
        "\n",
        "C'est tout pour l'introduction. Partons à l'aventure dans le domaine des services d'utilité publique!\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "ad57179f-240e-441d-b2e4-e531cd192b45",
      "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": "35fff60f-7c73-4492-ace9-ecdc8fd33b0c",
      "metadata": {},
      "outputs": [],
      "source": [
        "#!pip install qiskit_ibm_runtime\n",
        "#!pip install qiskit_aer"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "2f7464cc-4190-400f-b947-4f38100b43d2",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "\n",
        "from qiskit import QuantumCircuit, transpile\n",
        "from qiskit.circuit import Parameter\n",
        "from qiskit.circuit.library import YGate\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    fake_provider,\n",
        "    EstimatorV2 as Estimator,\n",
        ")\n",
        "from qiskit_aer import AerSimulator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "acd628f1-e010-44b7-ba77-cceb3b4198c6",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "06556d62-08bc-4c43-a7d0-382b73ec239a",
      "metadata": {},
      "source": [
        "<span id=\"2-preparation\" />\n",
        "\n",
        "## 2. Préparation\n",
        "\n",
        "<span id=\"21-construct-rzz-$pi$-/-2\" />\n",
        "\n",
        "### 2.1 Construire RZZ(- $\\pi$ /2)\n",
        "\n",
        "Tout d'abord, il convient d'observer que la porte RZZ nécessite en général deux portes $CX$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "d0b96520-3a4b-43c4-ade8-85cec68e3e09",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/d0b96520-3a4b-43c4-ade8-85cec68e3e09-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.circuit.library import RZZGate\n",
        "\n",
        "θ_h = Parameter(\"$\\\\theta_h$\")\n",
        "qc1 = QuantumCircuit(2)\n",
        "qc1.append(RZZGate(θ_h), [0, 1])\n",
        "qc1.decompose(reps=1).draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2770e3d2-d9d1-4bd5-8f00-8d8401f10438",
      "metadata": {},
      "source": [
        "Comme mentionné ci-dessus, nous nous concentrons sur la porte RZZ avec un angle spécifique, - $\\pi$ / 2, pour cette expérience.  Comme le montre l'article, il peut être réalisé avec une seule porte $CX$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "14b0ecb2-a8da-484a-bf49-606450bb074a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/14b0ecb2-a8da-484a-bf49-606450bb074a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc2 = QuantumCircuit(2)\n",
        "\n",
        "qc2.sdg([0, 1])\n",
        "qc2.append(YGate().power(1 / 2), [1])\n",
        "qc2.cx(0, 1)\n",
        "qc2.append(YGate().power(1 / 2).adjoint(), [1])\n",
        "\n",
        "qc2.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "269a5094-b4e6-4249-b9d5-ea25ded1767e",
      "metadata": {},
      "source": [
        "Pour la suite, nous définirons une porte en fonction de ce circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "2b5fbb1a",
      "metadata": {},
      "outputs": [],
      "source": [
        "rzz = qc2.to_gate(label=\"RZZ\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d89cccac-13a8-49b5-9dab-c1e98f9735c8",
      "metadata": {},
      "source": [
        "Faisons un usage aléatoire de la nouvelle définition `rzz`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6753b9a4-90b1-4981-82b2-8177ac258af2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/6753b9a4-90b1-4981-82b2-8177ac258af2-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "qc3 = QuantumCircuit(3)\n",
        "qc3.append(rzz, [0, 1])\n",
        "qc3.append(rzz, [0, 2])\n",
        "display(qc3.draw(\"mpl\"))\n",
        "# display(qc.decompose(reps=1).draw(\"mpl\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed9c4589-c996-4199-8dd7-19e8d144a384",
      "metadata": {},
      "source": [
        "Avant d'aller plus loin, vérifions l'équivalence logique de `qc1` (la porte RZZ) pour -pi/2 et notre porte `rzz` ou `qc2` nouvellement définie :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "b84c6f00-d50a-4874-9fa4-0c81742654e8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.quantum_info import Operator\n",
        "\n",
        "op1 = Operator(qc1.assign_parameters([-np.pi / 2]))\n",
        "op2 = Operator(qc2)\n",
        "\n",
        "op1.equiv(op2)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ace1a98d-2ef7-4fe8-b67d-7799e7566f5a",
      "metadata": {},
      "source": [
        "<span id=\"22-color-the-coupling-map\" />\n",
        "\n",
        "### 2.2 Colorer la carte de couplage\n",
        "\n",
        "Voyons comment nous colorons la carte de couplage d'un backend.  Ceci est nécessaire pour regrouper les interactions de $ZZ$ en couches.\n",
        "\n",
        "Pour commencer, visualisons la carte de couplage d'un backend.  Notez que les cartes de couplage sont fortement hexagonales pour tous les dispositifs Quantum actuels ( IBM ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "84e11b8e-bc74-4f3a-8538-d4d34d069e0f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/84e11b8e-bc74-4f3a-8538-d4d34d069e0f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "backend.coupling_map.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5fbfce4-c8cc-49fe-831d-31ad59f84c08",
      "metadata": {},
      "source": [
        "Pour colorer une carte de couplage, nous utilisons [rustworkx](https://www.rustworkx.org/), qui est un paquetage Python pour travailler avec des graphes et des réseaux complexes.  Il fournit plusieurs algorithmes de coloration, qui sont tous heuristiques et ne garantissent donc pas de trouver une coloration minimale.\n",
        "\n",
        "Cela dit, comme les graphes lourds-hex sont bipartis, nous prenons [graph\\_bipartite\\_edge\\_color](https://www.rustworkx.org/apiref/rustworkx.graph_bipartite_edge_color.html), qui devrait trouver une coloration minimale pour ces graphes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "79d3af17-efa0-4e48-89c5-ad9a962cf83b",
      "metadata": {
        "scrolled": true
      },
      "outputs": [],
      "source": [
        "def color_coupling_map(backend):\n",
        "    graph = backend.coupling_map.graph\n",
        "    undirected_graph = graph.to_undirected(multigraph=False)\n",
        "    edge_color_map = rx.graph_bipartite_edge_color(undirected_graph)\n",
        "    if edge_color_map is None:\n",
        "        edge_color_map = rx.graph_greedy_edge_color(undirected_graph)\n",
        "    # build a map from color to a list of edges\n",
        "    edge_index_map = undirected_graph.edge_index_map()\n",
        "    color_edges_map = {color: [] for color in edge_color_map.values()}\n",
        "    for edge_index, color in edge_color_map.items():\n",
        "        color_edges_map[color].append(\n",
        "            (edge_index_map[edge_index][0], edge_index_map[edge_index][1])\n",
        "        )\n",
        "    return edge_color_map, color_edges_map"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1a409faf-cb52-4c1c-95bd-beeca2cfd379",
      "metadata": {},
      "source": [
        "Les graphes fortement hexagonaux doivent être peints en trois couleurs.  Vérifions cela pour la carte de couplage ci-dessus.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "5cf7f215-2155-498e-a8e8-acc51ccba363",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "ibm_strasbourg, 127-qubit device, 3 colors assigned.\n"
          ]
        }
      ],
      "source": [
        "edge_color_map, color_edges_map = color_coupling_map(backend)\n",
        "print(\n",
        "    f\"{backend.name}, {backend.num_qubits}-qubit device, {len(color_edges_map.keys())} colors assigned.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "574bb71f-436e-499c-bb23-1adbab9445db",
      "metadata": {},
      "source": [
        "Oui, c'est le cas!\n",
        "\n",
        "Pour le plaisir, peignons la carte de couplage sur la coloration obtenue, en utilisant la fonctionnalité de visualisation de rustworks.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "fb9b6915-5ace-4104-b81d-91666e689f0d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/fb9b6915-5ace-4104-b81d-91666e689f0d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "color_str_map = {0: \"green\", 1: \"red\", 2: \"blue\"}\n",
        "\n",
        "undirected_graph = backend.coupling_map.graph.to_undirected(multigraph=False)\n",
        "for i in undirected_graph.edge_indices():\n",
        "    undirected_graph.get_edge_data_by_index(i)[\"color\"] = color_str_map[\n",
        "        edge_color_map[i]\n",
        "    ]\n",
        "\n",
        "rx.visualization.graphviz_draw(\n",
        "    undirected_graph, method=\"neato\", edge_attr_fn=lambda edge: {\"color\": edge[\"color\"]}\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4ae34f8d-6a46-4d81-bd0d-66a0d6fca588",
      "metadata": {},
      "source": [
        "<span id=\"3-solve-the-trotterized-time-evolution-of-a-2d-ising-model\" />\n",
        "\n",
        "## 3. Résoudre l'évolution temporelle trottisée d'un modèle d'Ising à 2D.\n",
        "\n",
        "Définissons une routine pour construire un circuit du papier utilitaire pour l'évolution temporelle d'un modèle d'Ising 2D.  La routine prend trois paramètres, un backend, un entier indiquant le nombre de pas de Trotter, et un booléen contrôlant l'insertion de la barrière.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "b9d5dfd9-997f-4d8c-af1f-7927df31bffb",
      "metadata": {},
      "outputs": [],
      "source": [
        "def get_utility_circuit(backend, num_steps: int, barrier: bool = False):\n",
        "    num_qubits = backend.num_qubits\n",
        "    _, color_edges_map = color_coupling_map(backend)\n",
        "    θ_h = Parameter(\"$\\\\theta_h$\")\n",
        "    qc = QuantumCircuit(num_qubits)\n",
        "\n",
        "    for i in range(num_steps):\n",
        "        qc.rx(θ_h, range(num_qubits))\n",
        "\n",
        "        for _, edge_list in color_edges_map.items():\n",
        "            for edge in edge_list:\n",
        "                qc.append(rzz, edge)\n",
        "\n",
        "        if barrier:\n",
        "            qc.barrier()\n",
        "    return qc"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e6689c89-8321-44e8-8983-82d6b0735751",
      "metadata": {},
      "source": [
        "Veuillez noter que nous avons déjà effectué manuellement le mappage et le routage des qubits pour le circuit construit.  Ainsi, lorsque nous transposons le circuit ultérieurement, nous ne demandons pas (**ne devrions pas** demander) au transpileur d'effectuer le mappage et le routage des qubits. Comme vous le verrez bientôt, nous l'invoquons avec un niveau d'optimisation de 1 et une méthode de mise en page \"triviale\".\n",
        "\n",
        "Ensuite, nous définissons une routine simple qui permet d'obtenir les informations sur le circuit construit pour une vérification rapide.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5c002120",
      "metadata": {},
      "outputs": [],
      "source": [
        "def get_circuit_info(qc: QuantumCircuit, reps: int = 0):\n",
        "    qc0 = qc.decompose(reps=reps)\n",
        "    return (\n",
        "        f\"{qc0.num_qubits} qubits × {qc0.depth(lambda x: x.operation.num_qubits == 2)} layers ({qc0.depth()}-depth)\"\n",
        "        + \",  \"\n",
        "        + f\"\"\"Gate breakdown: {\", \".join([f\"{k.upper()} {v}\" for k, v in qc0.count_ops().items()])}\"\"\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30b69c52-09f0-4d5a-855c-e8f90acd0984",
      "metadata": {},
      "source": [
        "Exerçons ces routines.  Vous devriez voir un circuit de 27 qubits $\\times$ 15 couches (5 étapes de Trotter). Puisque le faux dispositif a 28 arêtes, il devrait y avoir 28\\*5 gages d'enchevêtrement.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "af7bdb29-3bdb-4f6e-a53c-76ce935dff03",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/af7bdb29-3bdb-4f6e-a53c-76ce935dff03-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "27 qubits × 15 layers (20-depth),  Gate breakdown: CIRCUIT-165 140, RX 135, BARRIER 5\n",
            "27 qubits × 15 layers (60-depth),  Gate breakdown: SDG 280, UNITARY 280, CX 140, R 135, BARRIER 5\n"
          ]
        }
      ],
      "source": [
        "backend = fake_provider.FakeTorontoV2()\n",
        "num_steps = 5\n",
        "qc = get_utility_circuit(backend, num_steps, True)\n",
        "\n",
        "display(qc.draw(output=\"mpl\", fold=-1))\n",
        "print(get_circuit_info(qc, reps=0))\n",
        "print(get_circuit_info(qc, reps=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bf038a4d-4482-4368-8171-8cb4cd828c51",
      "metadata": {},
      "source": [
        "<span id=\"4-solve-the-27-qubit-version-of-the-problem\" />\n",
        "\n",
        "## 4. Résolvez la version à 27 qubits du problème.\n",
        "\n",
        "Nous démontrons maintenant une version à plus petite échelle de l'expérience d'utilité. Nous construisons un circuit de 27 qubits $\\times$ à 6 couches (2 étapes de Trotter) avec $\\langle Z_{13} \\rangle$ comme observable, et nous l'exécutons à la fois sur AerSimulator et sur un faux dispositif.\n",
        "\n",
        "Bien entendu, nous suivons notre flux de travail en quatre étapes, le \"modèle Qiskit\", qui consiste en un mappage, une optimisation, une exécution et un post-traitement. Plus concrètement,\n",
        "\n",
        "* Faire correspondre des entrées classiques à un calcul quantique.\n",
        "* Optimiser les circuits pour l'informatique quantique.\n",
        "* Exécuter des circuits à l'aide de primitives.\n",
        "* Post-traitement et retour des résultats dans un format classique.\n",
        "\n",
        "Dans ce qui suit, nous présentons l'étape de Map pour créer un circuit pour une expérience à plus petite échelle. Nous disposons alors d'un ensemble d'optimisations et d'exécutions pour AerSimulator et d'un autre pour un faux appareil.  Enfin, l'étape Post-Process permet de tracer les résultats.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad5f3abd-0f25-47ec-9541-16fe83b7e710",
      "metadata": {},
      "source": [
        "<span id=\"41-step-1-map\" />\n",
        "\n",
        "### 4.1 Étape 1 : Cartographier\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "cd9c6774",
      "metadata": {},
      "outputs": [],
      "source": [
        "backend = fake_provider.FakeTorontoV2()  # a 27 qubit fake device.\n",
        "num_steps = 2\n",
        "qc = get_utility_circuit(backend, num_steps)\n",
        "obs = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [13], 1)], num_qubits=backend.num_qubits\n",
        ")  # Falcon\n",
        "angles = [\n",
        "    0,\n",
        "    0.1,\n",
        "    0.2,\n",
        "    0.3,\n",
        "    0.4,\n",
        "    0.5,\n",
        "    0.6,\n",
        "    0.7,\n",
        "    0.8,\n",
        "    1.0,\n",
        "    np.pi / 2,\n",
        "]  # We try 11 angles for theta_h."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "81b39024-a7f4-4a7a-a543-59185def648b",
      "metadata": {},
      "source": [
        "<span id=\"42-steps-2-and-3-optimize-and-execute-simulator\" />\n",
        "\n",
        "### 4.2 Étapes 2 et 3 : Optimisation et exécution (simulateur)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "fa307165-fc74-4b6c-88c5-a2ac36341a3c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54\n",
            "27 qubits × 6 layers (16-depth),  Gate breakdown: U3 80, CX 56, R 54, U1 32, U 28\n"
          ]
        }
      ],
      "source": [
        "backend_sim = AerSimulator()\n",
        "transpiled_qc_sim = transpile(\n",
        "    qc, backend_sim, optimization_level=1, layout_method=\"trivial\"\n",
        ")\n",
        "transpiled_obs_sim = obs.apply_layout(layout=transpiled_qc_sim.layout)\n",
        "\n",
        "print(get_circuit_info(qc, reps=1))\n",
        "print(get_circuit_info(transpiled_qc_sim, reps=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a101b40-5555-41d9-96b2-a8c986ea9903",
      "metadata": {},
      "source": [
        "Un utilisateur a exécuté la cellule suivante en utilisant un MacBook Pro avec 2.3 GHz quad-core Intel Core i7 Processor équipé de 32GB 3LPDDR4X RAM, qui fonctionne sous macOS 14.5.  Il a pris 161ms en temps masqué. Chaque ordinateur portable est légèrement différent.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "178dd8c4-439b-4324-ba1f-687acc57a678",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "CPU times: user 231 ms, sys: 186 ms, total: 417 ms\n",
            "Wall time: 111 ms\n"
          ]
        }
      ],
      "source": [
        "%%time\n",
        "params = [[p] for p in angles]\n",
        "estimator = Estimator(mode=backend_sim)\n",
        "pub = (transpiled_qc_sim, transpiled_obs_sim, params)\n",
        "result_sim = estimator.run([pub]).result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "48b684f9-061a-49fd-acb1-3ed7b4bab1f2",
      "metadata": {},
      "source": [
        "<span id=\"43-steps-2-and-3-optimize-and-execute-fake-device\" />\n",
        "\n",
        "### 4.3 Étapes 2 et 3 : Optimisation et exécution (appareil factice)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "f944eeae-8dc5-4ad6-97ad-92f95159956b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "27 qubits × 6 layers (23-depth),  Gate breakdown: SDG 112, UNITARY 112, CX 56, R 54\n",
            "27 qubits × 6 layers (49-depth),  Gate breakdown: SDG 324, U1 274, H 162, CX 56, U3 14\n"
          ]
        }
      ],
      "source": [
        "backend_fake = fake_provider.FakeTorontoV2()\n",
        "transpiled_qc_fake = transpile(\n",
        "    qc, backend_fake, optimization_level=1, layout_method=\"trivial\"\n",
        ")\n",
        "transpiled_obs_fake = obs.apply_layout(layout=transpiled_qc_fake.layout)\n",
        "\n",
        "print(get_circuit_info(qc, reps=1))\n",
        "print(get_circuit_info(transpiled_qc_fake, reps=1))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59adfdfb-0954-42f7-9dd2-c819d04264b6",
      "metadata": {},
      "source": [
        "Lorsque le même utilisateur a exécuté la cellule suivante avec le même environnement que ci-dessus, il a fallu 2min 19s en Wall Time.  L'exécution d'un circuit sur un faux dispositif invoque une simulation bruyante qui prend beaucoup plus de temps qu'une simulation exacte.   Nous vous recommandons de ne pas exécuter un circuit plus important (tel qu'un circuit de 27 qubits $\\times$ 9 couches avec 3 étapes de Trotter) sur un faux appareil.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "62d0e0bb-9acb-474a-8d19-b132013131a0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "CPU times: user 4min 42s, sys: 9.35 s, total: 4min 51s\n",
            "Wall time: 38.3 s\n"
          ]
        }
      ],
      "source": [
        "%%time\n",
        "params = [[p] for p in angles]\n",
        "estimator = Estimator(mode=backend_fake)\n",
        "pub = (transpiled_qc_fake, transpiled_obs_fake, params)\n",
        "result_fake = estimator.run([pub]).result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "181e5f61-2830-485c-bd22-a1d70b443ed9",
      "metadata": {},
      "source": [
        "<span id=\"44-step-4-post-process\" />\n",
        "\n",
        "### 4.4 Étape 4 : Post-traitement\n",
        "\n",
        "Nous présentons les résultats des simulations exactes et bruyantes. Vous voyez les effets graves du bruit sur FakeToronto.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "4cf3cad0-e720-469c-a9ed-e397dae0f2f6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/4cf3cad0-e720-469c-a9ed-e397dae0f2f6-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(angles, result_fake[0].data.evs, \"o\", label=\"Fake Device\")\n",
        "plt.plot(angles, result_sim[0].data.evs, \"o\", label=\"AerSimulator\")\n",
        "plt.xlabel(\"$\\\\mathrm{R_x}$ angle $\\\\theta_h$\")\n",
        "plt.title(\"$\\\\langle Z_{13} \\\\rangle$\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1b05966c-efe2-4eb4-b63c-43c07cf81f41",
      "metadata": {},
      "source": [
        "<span id=\"5-solve-the-127-qubit-version-of-the-problem\" />\n",
        "\n",
        "## 5. Résolvez la version à 127 qubits du problème\n",
        "\n",
        "Votre objectif est de réaliser l'expérience à l'échelle de l'utilité publique mentionnée au début. Vous allez créer et exécuter un circuit de 127 qubits et 60 couches (20 étapes de Trotter) avec $\\langle Z_{62} \\rangle$ comme observable. Nous vous recommandons d'essayer de le faire vous-même, en utilisant le code de la version à 27 qubits le cas échéant. Mais la solution est fournie ici.\n",
        "\n",
        "**La solution :**\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a18a42dd-5891-4b5f-8c3b-1bc7e1ae4a5e",
      "metadata": {},
      "source": [
        "<span id=\"51-step-1-map\" />\n",
        "\n",
        "### 5.1 Étape 1 : Cartographier\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "8b900d9a-0351-426a-b25b-19e631fd257e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# backend_map = service.backend(\"ibm_brisbane\")\n",
        "backend_map = service.least_busy(operational=True, simulator=False)\n",
        "\n",
        "num_steps = 20\n",
        "qc = get_utility_circuit(backend_map, num_steps)\n",
        "obs = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [62], 1)], num_qubits=backend_map.num_qubits\n",
        ")  # Eagle\n",
        "angles = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 1.0, np.pi / 2]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "51ea0288-d8e9-4ee1-bbe8-dc24826a5026",
      "metadata": {},
      "source": [
        "<span id=\"52-steps-2-and-3-optimize-and-execute\" />\n",
        "\n",
        "### 5.2 Étapes 2 et 3 : Optimiser et exécuter\n",
        "\n",
        "Nous constatons que la carte de couplage du processeur Eagle comporte 144 arêtes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f9fadf76-48e3-43a3-b773-8fa3b5fd5f3d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "156 qubits × 60 layers (221-depth),  Gate breakdown: SDG 7040, UNITARY 7040, CX 3520, R 3120\n",
            "156 qubits × 60 layers (201-depth),  Gate breakdown: RZ 11933, SX 6240, CZ 3520\n"
          ]
        }
      ],
      "source": [
        "# backend = service.backend(\"ibm_brisbane\")\n",
        "backend = backend_map\n",
        "\n",
        "transpiled_qc = transpile(qc, backend, optimization_level=1, layout_method=\"trivial\")\n",
        "transpiled_obs = obs.apply_layout(layout=transpiled_qc.layout)\n",
        "\n",
        "print(get_circuit_info(qc, reps=1))\n",
        "print(get_circuit_info(transpiled_qc))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "2ddff1e1-c868-4300-bdbb-cdd3b6932302",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "job id=d1479n6qf56g0081sxa0\n"
          ]
        }
      ],
      "source": [
        "params = [[p] for p in angles]\n",
        "estimator = Estimator(mode=backend)\n",
        "pub = (transpiled_qc, transpiled_obs, params)\n",
        "job = estimator.run([pub])\n",
        "\n",
        "job_id = job.job_id()\n",
        "print(f\"job id={job_id}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c497fc9d-aa58-4eb2-851e-369c7209da97",
      "metadata": {},
      "source": [
        "<span id=\"53-post-process\" />\n",
        "\n",
        "### 5.3 Post-traitement\n",
        "\n",
        "Nous fournissons les valeurs des points \"atténués\" dans la figure 4b du document sur les services publics.   Tracez-les avec vos résultats.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "1c4ede0c-e865-41fe-9b38-8ecf39afa64f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/utility-i/extracted-outputs/1c4ede0c-e865-41fe-9b38-8ecf39afa64f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "result_paper = [\n",
        "    1.0171,\n",
        "    1.0044,\n",
        "    0.9563,\n",
        "    0.9602,\n",
        "    0.8394,\n",
        "    0.8120,\n",
        "    0.5466,\n",
        "    0.4556,\n",
        "    0.1953,\n",
        "    0.0141,\n",
        "    0.0117,\n",
        "]\n",
        "\n",
        "# REPLACE WITH YOUR OWN JOB ID\n",
        "job = service.job(job_id)\n",
        "\n",
        "plt.plot(angles, job.result()[0].data.evs, \"o\", label=f\"{job.backend().name}\")\n",
        "plt.plot(angles, result_paper, \"o\", label=\"Utility Paper\")\n",
        "plt.xlabel(\"$\\\\mathrm{R_x}$ angle $\\\\theta_h$\")\n",
        "plt.title(\"$\\\\langle Z_{62} \\\\rangle$\")\n",
        "plt.legend()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7fb3e103-af1d-4a9f-8016-1a270c5706be",
      "metadata": {},
      "source": [
        "Vos résultats sont-ils similaires aux résultats \"non atténués\" de la figure 4b? Elles peuvent être très différentes, en fonction de l'appareil et de son état au moment de l'expérience.  Ne vous préoccupez pas des résultats eux-mêmes. Ce que nous vérifierons, c'est si vous avez effectué le codage correctement.  Si c'est le cas, félicitations, vous avez atteint la ligne de départ de l'ère des services publics.\n",
        "\n",
        "Comme dans l'article sur les services publics, les scientifiques du monde entier ont fait preuve d'une grande ingéniosité pour extraire des résultats significatifs même en présence de bruit. L'objectif final de cet effort collectif est l'avantage quantique : un état dans lequel les ordinateurs quantiques peuvent résoudre certains problèmes utiles à l'industrie plus rapidement, avec une plus grande fidélité ou à moindre coût que les ordinateurs classiques. Il ne s'agira probablement pas d'un événement unique, mais plutôt d'une ère au cours de laquelle la reproduction classique des résultats quantiques prend de plus en plus de temps, jusqu'à ce qu'à un moment donné, le délai quantique devienne d'une importance cruciale. Une chose est claire en ce qui concerne l'avantage quantique : nous n'y parviendrons qu'au moyen d'expériences à grande échelle.  Si ce cours vous incite à participer à la quête, qui est pleine de défis et d'amusement, nous en serions très heureux.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "a346d104-299e-4152-a8ea-5e297d05b6a1",
      "metadata": {},
      "source": [
        "<span id=\"reference\" />\n",
        "\n",
        "## Compte de référence\n",
        "\n",
        "* Kim, Y., Eddins, A., Anand, S. et al. Preuve de l'utilité de l'informatique quantique pour la tolérance aux pannes. Nature 618, 500-505 (2023). [https://doi.org/10.1038/s41586-023-06096-3](https://doi.org/10.1038/s41586-023-06096-3)\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
}