{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "a10c2a41-ab48-4da5-bbe1-a16fd47906fe",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Transition de phase de Nishimori\"\n",
        "description: \"Ce tutoriel montre comment réaliser une transition de phase Nishimori sur un processeur quantique à effet tunnel ( IBM ).\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore Verresen Layden Tantivasadakarn Vishwanath Trebst */}\n",
        "\n",
        "<span id=\"nishimori-phase-transition\" />\n",
        "\n",
        "# Transition de phase de Nishimori\n",
        "\n",
        "*Estimation de la durée d'exécution : 3 minutes sur un processeur Heron r2 (REMARQUE : il s'agit uniquement d'une estimation. (La durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6113a1ff-5cc0-433a-afd7-5aadc97bb28f",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Acquis d'apprentissage\n",
        "\n",
        "À l'issue de ce tutoriel, les utilisateurs devraient obtenir les résultats suivants :\n",
        "\n",
        "* Comprendre la transition de phase de Nishimori et la manière dont elle se manifeste par l'apparition d'un enchevêtrement à longue portée dans le modèle d'Ising à liaisons aléatoires.\n",
        "* Mettre en œuvre le protocole *de* génération d'intrication par mesure (GEM) sur du matériel quantique à l'aide de mesures en cours de circuit et de circuits à profondeur constante.\n",
        "* Caractériser la transition en calculant la corrélation à deux points et la variance normalisée de la magnétisation à partir des données expérimentales.\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prérequis\n",
        "\n",
        "Nous vous recommandons de vous familiariser avec les sujets suivants avant de suivre ce tutoriel :\n",
        "\n",
        "* Le guide sur [les qubits de mesure](/docs/guides/measure-qubits), en particulier la section consacrée à la mesure en cours de circuit sur laquelle repose le protocole GEM.\n",
        "* [Simulation exacte et avec bruit à l'aide des primitives de Qiskit Aer](/docs/guides/simulate-with-qiskit-aer) : c'est ainsi que s'effectue la partie à petite échelle.\n",
        "* [L'intrication à longue portée avec des circuits dynamiques](/docs/tutorials/long-range-entanglement), un tutoriel complémentaire qui utilise le même paradigme d'intrication basé sur la mesure.\n",
        "* [Réseau hexagonal dense](https://www.ibm.com/quantum/blog/heavy-hex-lattice), la topologie matérielle d’ IBM®, sur laquelle repose le réseau de plaquettes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "838ac5c7-0819-4482-8875-c57db88ba82a",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Ce tutoriel explique comment réaliser une transition de phase de Nishimori sur un processeur quantique. Cette expérience a été décrite pour la première fois dans [*l'article intitulé « Realizing the Nishimori transition across the error threshold for constant-depth quantum circuits*](https://arxiv.org/abs/2309.02863) ».\n",
        "\n",
        "La transition de phase de Nishimori désigne la transition entre les phases ordonnées à courte et à longue portée dans le modèle d'Ising à liaisons aléatoires. Sur un ordinateur quantique, la phase ordonnée à longue portée se manifeste par un état dans lequel les qubits sont intriqués à l'échelle de l'ensemble du dispositif. Cet état fortement intriqué est préparé à l'aide du protocole *de génération d'intrication par mesure* (GEM). Grâce à des mesures effectuées à mi-parcours, le protocole GEM permet d'intricer des qubits sur l'ensemble du dispositif en utilisant uniquement des circuits de profondeur constante. Ce tutoriel utilise l'implémentation du protocole GEM fournie par la suite logicielle [GEM Suite](https://github.com/qiskit-community/gem-suite).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4091005d-ecd5-40c9-b90f-263e49a4cc16",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :\n",
        "\n",
        "* Qiskit SDK v1.0 ou plus tard, avec prise en charge de [la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.22 ou version ultérieure (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Aer v0.14 ou version ultérieure (`pip install qiskit-aer`)\n",
        "* GEM Suite (`pip install gem-suite`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "210b8730-ca3e-46d9-b8b0-64c36f13df8e",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "4ad70734-54f8-400a-8399-188ee8a0f3ac",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import warnings\n",
        "\n",
        "from collections import defaultdict\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_aer import AerSimulator\n",
        "\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "\n",
        "from gem_suite import PlaquetteLattice\n",
        "from gem_suite.experiments import GemExperiment"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "281ec87e-3f50-44b6-8e6f-a906b92fba74",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Exemple de simulateur à petite échelle\n",
        "\n",
        "`AerSimulator`Dans cette section, le déroulement complet du processus est présenté sur le format sans bruit. Le réseau de plaquettes est limité à une seule plaquette (12 qubits), ce qui permet de conserver une simulation de petite taille et rapide, tout en mettant en œuvre l'ensemble des étapes du protocole GEM : mesure en cours de circuit, balayage de l'angle d' $R_{ZZ}$, décodage et analyse de la variance normalisée. Ce même flux de travail est ensuite étendu à plusieurs plaquettes, puis à l'ensemble du réseau, sur du matériel réel.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "39f59f3a-eb2a-4160-9da2-2bb83c58771c",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Étape 1 : Mettre en correspondance les entrées classiques avec un problème quantique\n",
        "\n",
        "Le protocole GEM fonctionne sur un processeur quantique dont la connectivité des qubits est décrite par un réseau. Les processeurs « IBM Quantum® » actuels utilisent le [réseau hexagonal dense](https://www.ibm.com/quantum/blog/heavy-hex-lattice). Les qubits du processeur sont regroupés en *plaquettes* en fonction de la cellule élémentaire du réseau qu'ils occupent. Étant donné qu'un qubit peut se trouver dans plusieurs cellules unitaires, les plaquettes ne sont pas disjointes. Sur le réseau hexagonal dense, une plaquette contient 12 qubits. Les plaquettes elles-mêmes forment également des réseaux, dans lesquels deux plaquettes sont reliées si elles partagent des qubits. Sur le réseau hexagonal dense, les plaquettes voisines partagent trois qubits.\n",
        "\n",
        "Dans la suite logicielle GEM Suite, la classe fondamentale permettant de mettre en œuvre le protocole GEM est `PlaquetteLattice`, qui représente le réseau de plaquettes (à ne pas confondre avec le réseau « heavy-hex »). A `PlaquetteLattice` peut être initialisé à partir d'une carte de couplage de qubits. À l'heure actuelle, seules les cartes de couplage « heavy-hex » sont prises en charge.\n",
        "\n",
        "La cellule de code suivante initialise un réseau de plaquettes à partir de la carte de couplage d'une unité de traitement quantique (QPU). Le réseau de plaquettes ne recouvre pas toujours l'ensemble du matériel. Par exemple, `ibm_torino` dispose au total de 133 qubits, mais le plus grand réseau de plaquettes pouvant tenir sur le dispositif n’en utilise que 125, répartis sur 18 plaquettes; `ibm_pittsburgh` (156 qubits) intègre de la même manière 144 qubits dans 21 plaquettes. La même tendance s'observe pour d'autres QPU à structure hexagonale dense comportant un nombre différent de qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "553dbafe-1778-4971-83c3-0408605b701d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# QiskitRuntimeService.save_account(channel=\"ibm_quantum\", token=\"<YOUR_API_KEY>\", overwrite=True,\n",
        "# set_as_default=True)\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")\n",
        "aer_backend = AerSimulator.from_backend(backend)\n",
        "plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)\n",
        "\n",
        "print(f\"Number of qubits in backend: {backend.num_qubits}\")\n",
        "print(\n",
        "    f\"Number of qubits in plaquette lattice: {len(list(plaquette_lattice.qubits()))}\"\n",
        ")\n",
        "print(f\"Number of plaquettes: {len(list(plaquette_lattice.plaquettes()))}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0bf43539-d782-44a9-ba4e-a1b6c7510803",
      "metadata": {},
      "source": [
        "Vous pouvez visualiser le treillis de plaquettes en générant un diagramme de sa représentation graphique. Dans le diagramme, les plaquettes sont représentées par des hexagones étiquetés, et deux plaquettes sont reliées par une arête si elles partagent des qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "625882a4-faeb-4d96-b441-c989f43c4dea",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/625882a4-faeb-4d96-b441-c989f43c4dea-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plaquette_lattice.draw_plaquettes()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2f0a6b73-d1d2-4117-80cf-6fe3adbb82c5",
      "metadata": {},
      "source": [
        "Vous pouvez obtenir des informations sur des plaquettes individuelles, telles que les qubits qu'elles contiennent, à l'aide de la méthode `plaquettes` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "e412a612-c7d5-4689-840a-2383dd538f06",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PyPlaquette(index=0, qubits=[3, 4, 5, 6, 7, 16, 17, 23, 24, 25, 26, 27], neighbors=[4, 3, 1])"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get a list of the plaquettes\n",
        "plaquettes = list(plaquette_lattice.plaquettes())\n",
        "# Display information about plaquette 0\n",
        "plaquettes[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75edf1b4-f95f-4ff7-9e3c-2e810711636f",
      "metadata": {},
      "source": [
        "Vous pouvez également produire un diagramme des qubits sous-jacents qui forment le réseau de plaquettes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "a19d63ce-3572-4081-a008-c1332fbbe303",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/a19d63ce-3572-4081-a008-c1332fbbe303-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plaquette_lattice.draw_qubits()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e7b0f54b-5bc2-42bb-b4d4-1346f0a902e3",
      "metadata": {},
      "source": [
        "Outre les étiquettes des qubits et les arêtes indiquant les qubits connectés, le diagramme contient trois informations supplémentaires pertinentes pour le protocole GEM :\n",
        "\n",
        "* Chaque qubit est soit ombré (gris), soit non ombré. Les qubits ombrés sont des qubits \"sites\" qui représentent les sites du modèle d'Ising, et les qubits non ombrés sont des qubits \"liens\" utilisés pour médier les interactions entre les qubits sites.\n",
        "* Chaque qubit de site est étiqueté (A) ou (B), indiquant l'un des deux rôles qu'un qubit de site peut jouer dans le protocole GEM (les rôles sont expliqués plus loin).\n",
        "* Chaque arête est colorée selon l'une des six couleurs, ce qui permet de répartir les arêtes en six groupes. Ce découpage détermine la manière dont les portes à deux qubits peuvent être parallélisées, ainsi que les différents schémas de planification susceptibles d'entraîner des niveaux d'erreur variables sur un processeur quantique soumis à du bruit. Les arêtes d'un groupe étant disjointes, il est possible d'appliquer simultanément une couche de portes à deux qubits sur ces arêtes. En effet, il est possible de répartir les six couleurs en trois groupes de deux couleurs de telle sorte que l'union de chaque groupe de deux couleurs reste disjointe. Par conséquent, seules trois couches de portes à deux qubits sont nécessaires pour activer chaque bord. Il existe 12 façons de répartir les six couleurs de cette manière, et chacune de ces répartitions donne lieu à un programme de portes à trois couches différent.\n",
        "\n",
        "Maintenant que vous avez créé un réseau de plaquettes, l'étape suivante consiste à initialiser un `GemExperiment` objet en lui transmettant à la fois le réseau de plaquettes et le backend sur lequel vous comptez exécuter l'expérience. Cette `GemExperiment` classe gère la mise en œuvre concrète du protocole GEM, notamment la génération de circuits, l'envoi de tâches et l'analyse des données. Le bloc de code suivant initialise la classe d'expérience tout en limitant le réseau de plaquettes à une seule plaquette (12 qubits), ce qui permet de conserver une simulation de petite taille et rapide. Le réseau de plaquettes complet est utilisé par la suite lors du passage à l'échelle réelle sur du matériel physique.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "02357c6e-5c83-4ac0-811d-22602d9f33d5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/02357c6e-5c83-4ac0-811d-22602d9f33d5-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Filter the plaquette lattice down to a single plaquette (12 qubits)\n",
        "# so the AerSimulator run stays fast. The full lattice is used later\n",
        "# in the large-scale hardware example.\n",
        "gem_exp = GemExperiment(plaquette_lattice.filter([9]), backend=aer_backend)\n",
        "\n",
        "# visualize the plaquette lattice after filtering\n",
        "plaquette_lattice.filter([9]).draw_qubits()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3234e018-a1a8-47b7-b10c-57d6f7f77ff1",
      "metadata": {},
      "source": [
        "Un circuit de protocole GEM est construit en suivant les étapes suivantes :\n",
        "\n",
        "1. Préparer l'état tout $|+\\rangle$ en appliquant une porte de Hadamard à chaque qubit.\n",
        "2. Appliquez une porte « $R_{ZZ}$ » entre chaque paire de qubits connectés. Pour ce faire, vous pouvez utiliser trois niveaux de portes logiques. Chaque porte « $R_{ZZ}$ » agit sur un qubit de site et un qubit de liaison. Si le qubit de site est identifié par (B), l'angle est alors fixé à $\\frac{\\pi}{2}$. Si le qubit de site est identifié par (A), l'angle peut varier, ce qui permet d'obtenir différents circuits. Par défaut, la plage d'angles est définie sur 21 points équidistants compris entre $0$ et $\\frac{\\pi}{2}$, inclus.\n",
        "3. Mesurer chaque qubit de liaison dans la base d' $X$ s de Pauli. Comme les qubits sont mesurés dans la base d' $Z$ s de Pauli, vous pouvez y parvenir en appliquant une porte de Hadamard avant de mesurer le qubit.\n",
        "\n",
        "Notez que l'article cité dans l'introduction de ce tutoriel utilise une convention différente pour l'angle $R_{ZZ}$, qui diffère de la convention utilisée dans ce tutoriel d'un facteur 2.\n",
        "\n",
        "À la troisième étape, seuls les qubits de liaison sont mesurés. Pour comprendre dans quel état se trouvent les qubits de site, il est utile d'envisager le cas où l'angle d' $R_{ZZ}$ ion appliqué aux qubits de site (A) à la deuxième étape est égal à $\\frac{\\pi}{2}$. Dans ce cas, les qubits de site se retrouvent dans un état fortement intriqué, similaire à l'état GHZ,\n",
        "\n",
        "$$\n",
        "\\lvert \\text{GHZ} \\rangle = \\lvert 00 \\cdots 00 \\rangle + \\lvert 11 \\cdots 11 \\rangle.\n",
        "$$\n",
        "\n",
        "En raison du caractère aléatoire des résultats de mesure, l'état réel des qubits du site pourrait être un état différent présentant un ordre à longue portée, par exemple $\\lvert 00110 \\rangle + \\lvert 11001 \\rangle$. Cependant, l'état GHZ peut être reconstitué en appliquant une opération de décodage basée sur les résultats de mesure. Lorsque l'angle d' $R_{ZZ}$ est réduit à partir de $\\frac{\\pi}{2}$, l'ordre à longue portée peut encore être rétabli jusqu'à un angle critique qui, en l'absence de bruit, est d'environ $0.3 \\pi$. En dessous de cet angle, l'état résultant ne présente plus d'intrication à longue portée. Cette transition entre la présence et l'absence d'ordre à longue portée est la transition de phase de Nishimori.\n",
        "\n",
        "Dans la description ci-dessus, les qubits de site n'ont pas été mesurés, et l'opération de décodage peut être effectuée en appliquant des portes quantiques. Dans l'expérience telle qu'elle est mise en œuvre dans la suite GEM, les qubits de site sont en effet mesurés, et l'opération de décodage est appliquée lors d'une étape de post-traitement classique.\n",
        "\n",
        "Dans la description ci-dessus, l'opération de décodage peut être réalisée en appliquant des portes quantiques aux qubits du site afin de reconstituer l'état quantique. Toutefois, si l'objectif est de mesurer immédiatement l'état (par exemple, à des fins de caractérisation), il est alors possible de mesurer les qubits de site en même temps que les qubits de liaison, puis d'appliquer l'opération de décodage lors d'une étape de post-traitement classique.\n",
        "\n",
        "Outre l'angle d' $R_{ZZ}$, défini à l'étape 2 et qui, par défaut, balaye 21 valeurs, le circuit du protocole GEM dépend également du schéma de planification utilisé pour mettre en œuvre les trois couches de portes d' $R_{ZZ}$. Comme nous l'avons vu précédemment, il existe 12 modèles de planification de ce type. Par conséquent, le nombre total de circuits dans l'expérience est de $21 \\times 12 = 252$ s.\n",
        "\n",
        "Les circuits de l'expérience peuvent être générés à l'aide de la `circuits` méthode de la `GemExperiment` classe.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "8e2ade62-9a57-42c3-9a85-3fe2dec3c426",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Total number of circuits: 252\n"
          ]
        }
      ],
      "source": [
        "circuits = gem_exp.circuits()\n",
        "print(f\"Total number of circuits: {len(circuits)}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "56c3c656-dcfa-4ca0-9df9-2d97c187c685",
      "metadata": {},
      "source": [
        "Pour les besoins de ce tutoriel, il suffit de considérer un seul modèle d'ordonnancement. La cellule de code suivante limite l'expérience au premier modèle d'ordonnancement. Par conséquent, l'expérience ne comporte que 21 circuits, un pour chaque angle $R_{ZZ}$ balayé.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "4f8a2c73-752d-47b9-95d5-83439933fc08",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Total number of circuits: 21\n",
            "RZZ angles:\n",
            "[0.         0.07853982 0.15707963 0.23561945 0.31415927 0.39269908\n",
            " 0.4712389  0.54977871 0.62831853 0.70685835 0.78539816 0.86393798\n",
            " 0.9424778  1.02101761 1.09955743 1.17809725 1.25663706 1.33517688\n",
            " 1.41371669 1.49225651 1.57079633]\n"
          ]
        }
      ],
      "source": [
        "# Restrict experiment to the first scheduling pattern\n",
        "gem_exp.set_experiment_options(schedule_idx=0)\n",
        "\n",
        "# There are less circuits now\n",
        "circuits = gem_exp.circuits()\n",
        "print(f\"Total number of circuits: {len(circuits)}\")\n",
        "\n",
        "# Print the RZZ angles swept over\n",
        "print(f\"RZZ angles:\\n{gem_exp.parameters()}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7011dc79-8561-42ef-909b-00fd8be9ef34",
      "metadata": {},
      "source": [
        "La cellule de code suivante dessine un schéma du circuit à l'index 5. Pour réduire la taille du diagramme, les portes de mesure à la fin du circuit sont supprimées.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "fd57d483-c70b-4ad5-b309-15750ad38bac",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/fd57d483-c70b-4ad5-b309-15750ad38bac-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get the circuit at index 5\n",
        "circuit = circuits[5]\n",
        "# Remove the final measurements to ease visualization\n",
        "circuit.remove_final_measurements()\n",
        "# Draw the circuit\n",
        "circuit.draw(\"mpl\", fold=-1, scale=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3aa063b-44cf-49f3-9e12-23b2f6a1c85b",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique\n",
        "\n",
        "La compilation des circuits quantiques en vue de leur exécution sur du matériel comporte généralement [plusieurs étapes](/docs/guides/transpiler-stages). En règle générale, les étapes qui génèrent la plus grande charge de calcul sont le choix de la disposition des qubits, l'acheminement des portes à deux qubits afin de s'adapter à la connectivité des qubits du matériel, et l'optimisation du circuit pour réduire au minimum le nombre de portes et sa profondeur. Dans le protocole GEM, les étapes de disposition et de routage ne sont pas nécessaires, car la connectivité matérielle est déjà intégrée à la conception du protocole. Les circuits comportent déjà une configuration de qubits, et les portes à deux qubits sont déjà mappées sur des connexions natives. De plus, afin de préserver la structure du circuit lorsque l'angle d' $R_{ZZ}$ est modifié, il convient de ne procéder qu'à une optimisation très basique du circuit.\n",
        "\n",
        "La classe `GemExperiment` transpose de manière transparente les circuits lors de l'exécution de l'expérience. Les étapes de mise en page et de routage sont déjà surchargées par défaut pour ne rien faire, et l'optimisation du circuit est effectuée à un niveau qui ne permet d'optimiser que les portes à un seul qubit. Cependant, vous pouvez remplacer ou passer des options supplémentaires en utilisant la méthode `set_transpile_options` . À des fins de visualisation, la cellule de code suivante transpose manuellement le circuit affiché précédemment et dessine le circuit transposé.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "e9b99d48-8d33-46b5-bff5-480ab1c1c1f2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/e9b99d48-8d33-46b5-bff5-480ab1c1c1f2-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Demonstrate setting transpile options\n",
        "gem_exp.set_transpile_options(\n",
        "    optimization_level=1  # This is the default optimization level\n",
        ")\n",
        "pass_manager = generate_preset_pass_manager(\n",
        "    backend=aer_backend,\n",
        "    initial_layout=list(gem_exp.physical_qubits),\n",
        "    **dict(gem_exp.transpile_options),\n",
        ")\n",
        "transpiled = pass_manager.run(circuit)\n",
        "transpiled.draw(\"mpl\", idle_wires=False, fold=-1, scale=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d0dcd59-54ef-4af8-9213-0784ef94b838",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "Pour exécuter les circuits du protocole GEM sur le matériel, appelez la méthode `run` de l'objet `GemExperiment` . Vous pouvez spécifier le nombre de prises de vue que vous souhaitez échantillonner dans chaque circuit. La méthode `run` renvoie un objet [ExperimentData](https://qiskit-community.github.io/qiskit-experiments/stubs/qiskit_experiments.framework.ExperimentData.html) que vous devez enregistrer dans une variable. Notez que la méthode `run` ne fait que soumettre des travaux sans attendre qu'ils se terminent, il s'agit donc d'un appel non bloquant.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "6171a383-dd58-4e3f-88aa-bbec7b5870df",
      "metadata": {},
      "outputs": [],
      "source": [
        "exp_data = gem_exp.run(shots=10_000)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "71e81552-0d33-4950-8d45-e6c0a8a056c9",
      "metadata": {},
      "source": [
        "Pour attendre les résultats, appelez la méthode `block_for_results` de l'objet `ExperimentData` . Cet appel entraîne la suspension de l'interprète jusqu'à ce que les travaux soient terminés.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "ed14a067-35ba-4ffc-8534-4ae5ec6bc4c9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "ExperimentData(GemExperiment, 90bf2a90-f729-4c4e-a6da-664aecb11039, job_ids=['04a7c405-47fd-46ca-aa4b-aaf7e339cfbe'], metadata=<5 items>, figure_names=['two_point_correlation.svg', 'normalized_variance.svg', 'plaquette_ops.svg', 'bond_ops.svg'])"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# The noiseless AerSimulator produces zero-variance UFloat objects in the\n",
        "# analysis, which triggers a harmless warning from the `uncertainties`\n",
        "# library. Suppress it so the output stays clean.\n",
        "with warnings.catch_warnings():\n",
        "    warnings.filterwarnings(\n",
        "        \"ignore\", message=\"Using UFloat objects with std_dev==0\"\n",
        "    )\n",
        "    exp_data.block_for_results()\n",
        "exp_data"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "36e0570b-f091-45f2-bb83-143edbc3b433",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Étape 4 : Post-traitement et restitution du résultat dans le format classique souhaité\n",
        "\n",
        "À un angle de $R_{ZZ}$ de $\\frac{\\pi}{2}$, l'état décodé serait l'état GHZ en l'absence de bruit. L'ordre à longue portée de l'état GHZ peut être visualisé en traçant l'aimantation des chaînes de bits mesurées. L'aimantation $M$ est définie comme la somme des opérateurs de Pauli $Z$ à qubit unique,\n",
        "\n",
        "$$\n",
        "M = \\sum_{j=1}^N Z_j,\n",
        "$$\n",
        "\n",
        "où $N$ correspond au nombre de qubits du site. Sa valeur, pour une chaîne de bits, est égale à la différence entre le nombre de zéros et le nombre de uns. La mesure de l'état GHZ donne, avec une probabilité égale, soit l'état « tout zéros », soit l'état « tout uns »; ainsi, la magnétisation serait de $+N$ la moitié du temps et de $-N$ l'autre moitié du temps. En présence d'erreurs dues au bruit, d'autres valeurs pourraient également apparaître, mais si le bruit n'est pas trop important, la distribution atteindrait tout de même son maximum aux valeurs $+N$ et $-N$.\n",
        "\n",
        "Pour les chaînes de bits brutes avant décodage, la distribution de l'aimantation serait équivalente à celle de chaînes de bits uniformément aléatoires, en l'absence de bruit.\n",
        "\n",
        "La cellule de code suivante représente la magnétisation des chaînes de bits brutes et des chaînes de bits décodées à l'angle $R_{ZZ}$ de $\\frac{\\pi}{2}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "8ead3582-16df-4616-836c-bdce867ad6b8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0.5, 1.0, 'Magnetization distribution with and without decoding')"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/8ead3582-16df-4616-836c-bdce867ad6b8-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "def magnetization_distribution(\n",
        "    counts_dict: dict[str, int],\n",
        ") -> dict[str, float]:\n",
        "    \"\"\"Compute magnetization distribution from counts dictionary.\"\"\"\n",
        "    # Construct dictionary from magnetization to count\n",
        "    mag_dist = defaultdict(float)\n",
        "    for bitstring, count in counts_dict.items():\n",
        "        mag = bitstring.count(\"0\") - bitstring.count(\"1\")\n",
        "        mag_dist[mag] += count\n",
        "    # Normalize\n",
        "    shots = sum(counts_dict.values())\n",
        "    for mag in mag_dist:\n",
        "        mag_dist[mag] /= shots\n",
        "    return mag_dist\n",
        "\n",
        "\n",
        "# Get counts dictionaries with and without decoding\n",
        "data = exp_data.data()\n",
        "# Get the last data point, which is at the angle for the GHZ state\n",
        "raw_counts = data[-1][\"counts\"]\n",
        "# Without decoding\n",
        "site_indices = [\n",
        "    i for i, q in enumerate(gem_exp.plaquettes.qubits()) if q.role == \"Site\"\n",
        "]\n",
        "site_raw_counts = defaultdict(int)\n",
        "for key, val in raw_counts.items():\n",
        "    site_str = \"\".join(key[-1 - i] for i in site_indices)\n",
        "    site_raw_counts[site_str] += val\n",
        "# With decoding\n",
        "_, site_decoded_counts = gem_exp.plaquettes.decode_outcomes(\n",
        "    raw_counts, return_counts=True\n",
        ")\n",
        "\n",
        "# Compute magnetization distribution\n",
        "raw_magnetization = magnetization_distribution(site_raw_counts)\n",
        "decoded_magnetization = magnetization_distribution(site_decoded_counts)\n",
        "\n",
        "# Plot\n",
        "plt.bar(*zip(*raw_magnetization.items()), label=\"raw\")\n",
        "plt.bar(*zip(*decoded_magnetization.items()), label=\"decoded\", width=0.3)\n",
        "plt.legend()\n",
        "plt.xlabel(\"Magnetization\")\n",
        "plt.ylabel(\"Frequency\")\n",
        "plt.title(\"Magnetization distribution with and without decoding\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90a7ae7a-5175-421f-bda9-bc6b986bdf5f",
      "metadata": {},
      "source": [
        "Pour caractériser plus rigoureusement l'ordre à longue portée, vous pouvez examiner la corrélation moyenne à deux points $f$, définie comme suit\n",
        "\n",
        "$$\n",
        "f = \\frac{1}{N^2} \\left(\\langle M^2 \\rangle - \\langle M \\rangle ^2\\right).\n",
        "$$\n",
        "\n",
        "Une valeur plus élevée indique un degré d'enchevêtrement plus important. La classe `GemExperiment` calcule automatiquement cette valeur pour les chaînes de bits décodées dans le cadre du traitement des données expérimentales. Il stocke une figure accessible via la méthode `figure` de la classe de données d'expérience. Dans ce cas, le nom de la figure est `two_point_correlation`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "4ecb25c8-e572-49af-a879-9943039db131",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/4ecb25c8-e572-49af-a879-9943039db131-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "exp_data.figure(\"two_point_correlation\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f2f3e7e2-2a8b-4790-8ba7-b190c4ed1049",
      "metadata": {},
      "source": [
        "Pour déterminer le point critique de la transition de phase de Nishimori, vous pouvez examiner la variance normalisée de $M^2 / N$, définie comme suit\n",
        "\n",
        "$$\n",
        "g = \\frac{1}{N^3} \\left(\\langle M^4 \\rangle - \\langle M^2 \\rangle^2\\right),\n",
        "$$\n",
        "\n",
        "qui quantifie l'importance de la fluctuation de l'aimantation au carré. Cette valeur est maximale au point critique de la transition de phase de Nishimori. En l'absence de bruit, le point critique se situe approximativement à $0.3 \\pi$. En présence de bruit, le point critique est décalé vers le haut, mais la transition de phase est toujours observée tant que le point critique se situe en dessous de $0.5 \\pi$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "2b351d68-3924-445a-94ef-047b16214e8a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/2b351d68-3924-445a-94ef-047b16214e8a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 15,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "exp_data.figure(\"normalized_variance\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a94e0cbe-8429-487c-b203-50a8b2eacee3",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Exemple de matériel à grande échelle\n",
        "\n",
        "Après avoir validé le protocole sur un simulateur, vous pouvez désormais passer à une échelle supérieure et exécuter l'expérience sur le matériel quantique réel sélectionné dans la section «[ Configuration](#setup) ». Cet exemple utilise deux problèmes de plus grande envergure :\n",
        "\n",
        "* **Six plaquettes (environ 49 qubits)** : une série de taille moyenne qui montre déjà le décalage vers la droite du point critique sous l'effet du bruit matériel.\n",
        "* **Le réseau complet de plaquettes** : toutes les plaquettes prises en charge par la topologie « heavy-hex » du dispositif (par exemple, 18 plaquettes / 125 qubits sur `ibm_torino` ou 21 plaquettes / 144 qubits sur `ibm_pittsburgh`), permettant d’intricer les qubits sur l’ensemble du dispositif à l’aide de circuits à profondeur constante.\n",
        "\n",
        "La cellule de code ci-dessous est autonome : elle construit le réseau de plaquettes à partir de la carte de couplage du backend et exécute les deux expériences; cette section peut donc être exécutée après les cellules [de configuration,](#setup) sans qu'il soit nécessaire d'exécuter au préalable la section à petite échelle.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "08581c09-a6a5-4a56-9fc4-abf22b063c6a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Total number of circuits (full lattice): 21\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/08581c09-a6a5-4a56-9fc4-abf22b063c6a-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/08581c09-a6a5-4a56-9fc4-abf22b063c6a-2.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/nishimori-phase-transition/extracted-outputs/08581c09-a6a5-4a56-9fc4-abf22b063c6a-3.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "# Initialize the runtime service, pick a real quantum hardware backend,\n",
        "# and build the plaquette lattice from its coupling map. This is repeated\n",
        "# from the small-scale example so this cell can run standalone after the\n",
        "# Setup section. The full plaquette lattice is the \"large-scale\" target;\n",
        "# a six-plaquette subset (range(3, 9)) is also used to show an intermediate\n",
        "# scaling step.\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")\n",
        "plaquette_lattice = PlaquetteLattice.from_coupling_map(backend.coupling_map)\n",
        "\n",
        "# Build a GemExperiment for the full plaquette lattice and one for the\n",
        "# six-plaquette subset, each restricted to a single scheduling pattern so\n",
        "# the experiment has one circuit per RZZ angle (21 circuits total).\n",
        "gem_exp_full = GemExperiment(plaquette_lattice, backend=backend)\n",
        "gem_exp_full.set_experiment_options(schedule_idx=0)\n",
        "gem_exp_6 = GemExperiment(\n",
        "    plaquette_lattice.filter(range(3, 9)), backend=backend\n",
        ")\n",
        "gem_exp_6.set_experiment_options(schedule_idx=0)\n",
        "\n",
        "circuits = gem_exp_full.circuits()\n",
        "print(f\"Total number of circuits (full lattice): {len(circuits)}\")\n",
        "\n",
        "# -------------------------Step 2-------------------------\n",
        "# GemExperiment transpiles internally for the target backend: the layout\n",
        "# and routing stages are overridden because the plaquette lattice already\n",
        "# matches the hardware connectivity, and optimization is restricted so the\n",
        "# RZZ angle structure is preserved. The code below manually transpiles one\n",
        "# circuit from the six-plaquette experiment with the same settings this\n",
        "# experiment will use, and draws it for inspection. (The full-lattice\n",
        "# transpiled circuit has too many qubits to visualize cleanly, so the\n",
        "# six-plaquette circuit is used here as a representative example.)\n",
        "gem_exp_6.set_transpile_options(optimization_level=1)\n",
        "circuits_6 = gem_exp_6.circuits()\n",
        "pass_manager = generate_preset_pass_manager(\n",
        "    backend=backend,\n",
        "    initial_layout=list(gem_exp_6.physical_qubits),\n",
        "    **dict(gem_exp_6.transpile_options),\n",
        ")\n",
        "transpiled = pass_manager.run(circuits_6[5])\n",
        "display(transpiled.draw(\"mpl\", idle_wires=False, fold=-1, scale=0.5))\n",
        "\n",
        "# -------------------------Step 3-------------------------\n",
        "# Run both problem sizes on real hardware:\n",
        "#   1. Six plaquettes (~49 qubits) — an intermediate scale-up.\n",
        "#   2. The full plaquette lattice — every plaquette the device supports.\n",
        "exp_data_6 = gem_exp_6.run(shots=10_000, job_tags=[\"TUT_NPT\"])\n",
        "exp_data_full = gem_exp_full.run(shots=10_000, job_tags=[\"TUT_NPT\"])\n",
        "exp_data_6.block_for_results()\n",
        "exp_data_full.block_for_results()\n",
        "\n",
        "# -------------------------Step 4-------------------------\n",
        "# Plot the normalized variance at each scale. The peak marks the critical\n",
        "# point of the Nishimori transition; as the system grows, hardware noise\n",
        "# shifts the peak rightward.\n",
        "display(exp_data_6.figure(\"normalized_variance\"))\n",
        "exp_data_full.figure(\"normalized_variance\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da03c8eb-5ec5-4c9c-8eb7-d44c5948b43c",
      "metadata": {},
      "source": [
        "Il convient de noter que, selon le niveau de bruit du backend utilisé, les courbes de variance normalisée pour les grandes tailles peuvent ne pas présenter de pic net dans la plage d'angles balayée. Dans les simulations ci-dessus, le pic a été repoussé jusqu’à $0.5 \\pi$, soit le bord droit de la plage d’analyse (les rapports `critical_angle = 0.5000` d’analyse concernent à la fois les simulations à six plaquettes et celles sur le réseau complet). Cela signifie que le bruit matériel a décalé le point critique vers (ou juste au-delà de) la limite de la plage d'angles physiquement significative du protocole; la transition se situe donc à la limite de ce que ce balayage est capable de résoudre.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6abd9701-58e4-43a8-a1d7-279506570de4",
      "metadata": {},
      "source": [
        "<span id=\"conclusion\" />\n",
        "\n",
        "## Conclusion\n",
        "\n",
        "Dans ce tutoriel, vous avez réalisé une transition de phase de Nishimori sur un processeur quantique à l'aide du protocole GEM. Les paramètres que vous avez analysés lors du post-traitement — notamment la corrélation en deux points et la variance normalisée — servent de repères pour évaluer la capacité du dispositif à générer des états intriqués à longue portée. Ces tests de performance élargissent le champ d'application du protocole GEM au-delà de l'étude de phénomènes physiques intéressants. Dans le cadre de ce protocole, vous avez mis en corrélation des qubits sur l'ensemble du dispositif à l'aide de circuits de profondeur constante uniquement. Cet exploit n'est possible que grâce à l'utilisation, par le protocole, de mesures en cours de transmission. Dans cette expérience, l'état intriqué a été mesuré immédiatement, mais on pourrait envisager d'approfondir la recherche en utilisant cet état dans d'autres traitements quantiques.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "717f22c6-4945-4719-b9ba-ae44cde58da8",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  Si ce travail vous a paru intéressant, les documents suivants pourraient vous intéresser :\n",
        "\n",
        "  * [Intrication à longue portée avec des circuits dynamiques](/docs/tutorials/long-range-entanglement)\n",
        "  * [Codes de répétition](/docs/tutorials/repetition-codes)\n",
        "  * [Flux de commande et flux de contrôle classiques](/docs/guides/classical-feedforward-and-control-flow)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bf0dc8db-004f-4567-8162-f09be453b434",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Références\n",
        "\n",
        "\\[1] E. H. Chen, « G.-Y ». Zhu, R. Verresen, A. Seif, E. Bäumer, D. Layden, N. Tantivasadakarn, G. Zhu, S. Sheldon, A. Vishwanath, S. Trebst, A. Kandala. *Réalisation de la transition de Nishimori au-delà du seuil d'erreur pour des circuits quantiques à profondeur constante.* [arXiv:2309.02863](https://arxiv.org/abs/2309.02863) (2023).\n",
        "\n",
        "\\[2.] [La suite](https://github.com/qiskit-community/gem-suite) logicielle GEM.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1,
    "qpuSeconds": 180
  },
  "nbformat": 4,
  "nbformat_minor": 4
}