{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "aff344db",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simulez le modèle Ising à champ incliné d' 2D s à l'aide de la fonction QESEM\"\n",
        "description: \"Ce tutoriel présente une simulation d'un modèle d'Ising à champ transversal d' 2d s avec atténuation des erreurs QESEM combinée au module de rétropropagation d'opérateurs Qiskit.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore bppeps */}\n",
        "\n",
        "<span id=\"simulate-2d-tilted-field-ising-with-the-qesem-function\" />\n",
        "\n",
        "# Simulez le modèle Ising à champ incliné d' 2D s à l'aide de la fonction QESEM\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "edb68a2a9b5ee911",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\">\n",
        "  Qiskit Functions sont une fonctionnalité expérimentale disponible uniquement pour les utilisateurs des plans IBM Quantum® Premium Plan, Flex et On-Prem (via IBM Quantum Platform API). Elles sont en cours de publication et peuvent être modifiées.\n",
        "</Admonition>\n",
        "\n",
        "*Estimation d'utilisation : 20 minutes sur un processeur Heron r2. (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "88c617fe",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Ce tutoriel montre comment utiliser [QESEM](/docs/guides/qedma-qesem), la fonction Qiskit de Qedma, pour simuler la dynamique d'un modèle de spin quantique canonique, le modèle d'Ising à champ incliné (TFI) 2D avec des angles non-Clifford :\n",
        "\n",
        "$$\n",
        "H = J \\sum_{\\langle i,j \\rangle} Z_i Z_j + g_x \\sum_i X_i + g_z \\sum_i Z_i ,\n",
        "$$\n",
        "\n",
        "où $\\langle i,j \\rangle$ désigne les voisins les plus proches sur un treillis. La simulation de l'évolution temporelle de systèmes quantiques à plusieurs corps est une tâche difficile pour les ordinateurs classiques. Les ordinateurs quantiques, en revanche, sont naturellement conçus pour accomplir cette tâche efficacement. Le modèle TFI, en particulier, est devenu une référence populaire sur le matériel quantique en raison de son comportement physique riche et de sa mise en œuvre facile pour le matériel.\n",
        "\n",
        "Au lieu de simuler une dynamique à temps continu, nous adoptons le modèle d'Ising, étroitement lié. La dynamique peut être exprimée exactement comme un circuit quantique périodique, où chaque étape d'évolution consiste en trois couches de portes fractionnaires à deux qubits $R_{ZZ} (\\alpha_{ZZ})$, entrelacées avec des couches de portes à un qubit $R_X (\\alpha_X)$ et $R_Z (\\alpha_Z)$.\n",
        "\n",
        "Nous utiliserons des angles génériques qui posent des problèmes à la fois pour la simulation classique et pour l'atténuation des erreurs. Plus précisément, nous avons choisi $\\alpha_{ZZ} = 1.0$, $\\alpha_X = 0.53$, et $\\alpha_Z = 0.1$, plaçant le modèle loin de tout point intégrable.\n",
        "\n",
        "Dans ce tutoriel, nous ferons ce qui suit :\n",
        "\n",
        "* Estimer la durée d'exécution prévue de la QPU pour une réduction complète des erreurs en utilisant les fonctions analytiques et empiriques d'estimation du temps de QESEM.\n",
        "* Construire et simuler le circuit du modèle d'Ising à champ basculé 2D en utilisant des dispositions de qubits et des couches de portes inspirées du matériel.\n",
        "* Visualisez la connectivité des qubits du dispositif et les sous-graphes sélectionnés pour votre expérience.\n",
        "* Démontrer l'utilisation de [la rétropropagation de l'opérateur (OBP)](https://qiskit.github.io/qiskit-addon-obp/) pour réduire la profondeur du circuit. Cette technique réduit les opérations à partir de l'extrémité du circuit, au prix d'un plus grand nombre de mesures effectuées par l'opérateur.\n",
        "* Réaliser une atténuation d'erreur non biaisée (EM) pour plusieurs observables simultanément en utilisant QESEM, en comparant les résultats idéaux, bruités et atténués.\n",
        "* Analyser et tracer l'impact de l'atténuation des erreurs sur la magnétisation à différentes profondeurs de circuit.\n",
        "\n",
        "Remarque : OBP renvoie généralement un ensemble d'observables éventuellement non commutatifs. QESEM optimise automatiquement les bases de mesure lorsque les observables cibles contiennent des termes non commutatifs. Il génère des ensembles de bases de mesure candidats à l'aide de plusieurs algorithmes heuristiques et sélectionne l'ensemble qui minimise le nombre de bases distinctes. Cela signifie que QESEM regroupe des observables compatibles dans des bases communes afin de réduire le nombre total de configurations de mesure requises, ce qui améliore l'efficacité.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bb2bd156c9c6db20",
      "metadata": {},
      "source": [
        "<span id=\"about-qesem\" />\n",
        "\n",
        "## À propos de QESEM\n",
        "\n",
        "QESEM est un logiciel fiable, de haute précision, basé sur la caractérisation, qui met en œuvre une réduction efficace et non biaisée des erreurs quasi-probabilistes.\n",
        "Il est conçu pour atténuer les erreurs dans les circuits quantiques génériques et est indépendant des applications. Il a été validé sur diverses plates-formes matérielles, y compris des expériences à l'échelle de l'entreprise sur les appareils Eagle et Heron de IBM®. Les étapes du processus QESEM sont les suivantes :\n",
        "\n",
        "1. Caractérisation des dispositifs - cartographie des fidélités des portes et identification des erreurs cohérentes, fournissant des données d'étalonnage en temps réel. Cette étape permet de s'assurer que l'atténuation s'appuie sur les opérations de la plus haute fidélité disponibles.\n",
        "2. Transpilation sensible au bruit - génère et évalue des mappages de qubits, des ensembles d'opérations et des bases de mesure alternatifs, en sélectionnant la variante qui minimise le temps d'exécution estimé de la QPU, avec une parallélisation optionnelle pour accélérer la collecte des données.\n",
        "3. Suppression des erreurs - redéfinit les portes natives, applique le tournoiement de Pauli et optimise le contrôle du niveau d'impulsion (sur les plates-formes prises en charge) afin d'améliorer la fidélité.\n",
        "4. Caractérisation des circuits - construction d'un modèle d'erreur locale sur mesure et adaptation de ce modèle aux mesures de QPU pour quantifier le bruit résiduel.\n",
        "5. Atténuation des erreurs - construit des décompositions quasi-probabilistes multi-types et les échantillonne selon un processus adaptatif qui minimise le temps d'atténuation de la QPU et la sensibilité aux fluctuations matérielles, ce qui permet d'obtenir des précisions élevées pour de grands volumes de circuits.\n",
        "\n",
        "Pour plus d'informations sur QESEM et sur une expérience à grande échelle de ce modèle réalisée sur un sous-graphe à haute connectivité de 103 qubits issu de la géométrie native « heavy-hex » de `ibm_marrakesh`, consultez l'article « [Reliable high-accuracy error mitigation for utility-scale quantum circuits](https://arxiv.org/abs/2508.10997) ».\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d1ad1f56516888f2",
      "metadata": {},
      "source": [
        "![Flux de travail QESEM.](https://quantum.cloud.ibm.com/docs/images/tutorials/qedma-2d-ising-with-qesem/QESEM_workflow.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4a2ede52",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Installez les paquets Python suivants avant d'exécuter l'ordinateur portable :\n",
        "\n",
        "* Qiskit SDK v2.0.0 ou plus tard (`pip install qiskit`)\n",
        "* Qiskit Runtime v0.40.0 ou plus tard (`pip install qiskit-ibm-runtime`)\n",
        "* Qiskit Functions Catalog v0.8.0 ou plus tard ( `pip install qiskit-ibm-catalog` )\n",
        "* Operator Backpropagation Qiskit addon v0.3.0 ou plus récent ( `pip install qiskit-addon-obp` )\n",
        "* Qiskit Utils addon v0.1.1 ou plus récent ( `pip install qiskit-addon-utils` )\n",
        "* Qiskit Aer simulator v0.17.1 ou plus récent ( `pip install qiskit-aer` )\n",
        "* Matplotlib v3.10.3 ou plus tard ( `pip install matplotlib` )\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a675b3b1",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n",
        "Tout d'abord, il faut importer les bibliothèques concernées :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "acea2e46",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:50:58.471653Z",
          "start_time": "2025-11-05T12:50:58.467381Z"
        }
      },
      "outputs": [],
      "source": [
        "%matplotlib inline\n",
        "\n",
        "from typing import Sequence\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "import qiskit\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "from qiskit_ibm_catalog import QiskitFunctionsCatalog\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n",
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "from qiskit.visualization import (\n",
        "    plot_gate_map,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "467f1569",
      "metadata": {},
      "source": [
        "Ensuite, authentifiez-vous à l'aide de votre clé API disponible dans le tableau de bord d' [IBM Quantum Platform](http://quantum.cloud.ibm.com/). Ensuite, sélectionnez la fonction Qiskit comme suit. (Notez que, pour des raisons de sécurité, il est préférable [d](/docs/guides/functions-get-started#install-qiskit-functions-catalog-client) 'enregistrer les identifiants de votre compte sur votre environnement local, si vous utilisez un ordinateur de confiance, afin de ne pas avoir à saisir votre clé API à chaque authentification.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "41a53d27",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Paste here your instance and token strings\n",
        "\n",
        "instance = \"YOUR_INSTANCE\"\n",
        "token = \"YOUR_TOKEN\"\n",
        "channel = \"ibm_quantum_platform\"\n",
        "\n",
        "catalog = QiskitFunctionsCatalog(\n",
        "    channel=channel, token=token, instance=instance\n",
        ")\n",
        "qesem_function = catalog.load(\"qedma/qesem\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "69eafdcc",
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac9dcff0",
      "metadata": {},
      "source": [
        "Nous commençons par définir une fonction qui crée le circuit de Trotter :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3842021c",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:12.446678Z",
          "start_time": "2025-11-05T12:51:12.442978Z"
        }
      },
      "outputs": [],
      "source": [
        "def trotter_circuit_from_layers(\n",
        "    steps: int,\n",
        "    theta_x: float,\n",
        "    theta_z: float,\n",
        "    theta_zz: float,\n",
        "    layers: Sequence[Sequence[tuple[int, int]]],\n",
        "    init_state: str | None = None,\n",
        ") -> qiskit.QuantumCircuit:\n",
        "    \"\"\"\n",
        "    Generates an ising trotter circuit\n",
        "    :param steps: trotter steps\n",
        "    :param theta_x: RX angle\n",
        "    :param theta_z: RZ angle\n",
        "    :param theta_zz: RZZ angle\n",
        "    :param layers: list of layers (can be list of layers in device)\n",
        "    :param init_state: Initial state to prepare.\n",
        "     If None, will not prepare any state. If \"+\", will\n",
        "     add Hadamard gates to all qubits.\n",
        "    :return: QuantumCircuit\n",
        "    \"\"\"\n",
        "    qubits = sorted({i for layer in layers for edge in layer for i in edge})\n",
        "    circ = qiskit.QuantumCircuit(max(qubits) + 1)\n",
        "\n",
        "    if init_state == \"+\":\n",
        "        print(\"init_state = +\")\n",
        "        for q in qubits:\n",
        "            circ.h(q)\n",
        "\n",
        "    for _ in range(steps):\n",
        "        for q in qubits:\n",
        "            circ.rx(theta_x, q)\n",
        "            circ.rz(theta_z, q)\n",
        "\n",
        "        for layer in layers:\n",
        "            for edge in layer:\n",
        "                circ.rzz(theta_zz, *edge)\n",
        "        circ.barrier(qubits)\n",
        "\n",
        "    return circ"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8b4b76968dc50bb1",
      "metadata": {},
      "source": [
        "Nous créons ensuite une fonction permettant de calculer les valeurs espérées idéales à l'aide de `AerSimulator`.\n",
        "\n",
        "Veuillez noter que pour les grands circuits (30 qubits ou plus), nous recommandons d'utiliser les valeurs précalculées issues des simulations PEPS par propagation de croyances (BP). Ce code comprend, à titre d'exemple, des valeurs précalculées pour 35 qubits, basées sur l'approche BP pour l'évolution d'un réseau tensoriel PEPS présentée [dans cet article](https://www.science.org/doi/10.1126/sciadv.adk4321) (que nous appelons PEPS-BP), en utilisant le package [quimb](https://joss.theoj.org/papers/10.21105/joss.00819) d' Python s de réseaux tensoriels.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "12f4f2e334e4268b",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T20:20:32.759885Z",
          "start_time": "2025-11-06T20:20:32.752872Z"
        }
      },
      "outputs": [],
      "source": [
        "def calculate_ideal_evs(circ, obs, num_qubits, step):\n",
        "    # Predefined results for large circuits - calculated using\n",
        "    # bppeps for 3, 5, 7, 9 trotter steps\n",
        "    predefined_35 = [\n",
        "        0.79537,\n",
        "        0.78653,\n",
        "        0.79699,\n",
        "    ]\n",
        "\n",
        "    if num_qubits == 35:\n",
        "        print(\n",
        "            \"Using precalculated ideal values for large circuits calculated \"\n",
        "            \"with belief propagation PEPS. Currently only for 35 qubits.\"\n",
        "        )\n",
        "        return predefined_35[step]\n",
        "\n",
        "    else:\n",
        "        simulator = AerSimulator()\n",
        "\n",
        "        # Use Estimator primitive to get expectation value\n",
        "        estimator = Estimator(simulator)\n",
        "        sim_result = estimator.run([(circ, [obs])], precision=0.0001).result()\n",
        "\n",
        "        # Extracting the result\n",
        "        ideal_values = sim_result[0].data.evs[0]\n",
        "        return ideal_values"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7d342cd7",
      "metadata": {},
      "source": [
        "Nous utilisons une cartographie matérielle des couches $R_{ZZ}$ provenant du dispositif Heron, à partir de laquelle nous découpons les couches en fonction du nombre de qubits que nous voulons simuler. Nous définissons des sous-graphes pour 10, 21, 28 et 35 qubits qui conservent une structure 2D (n'hésitez pas à changer pour votre sous-graphe préféré) :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "27402210",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:22.025741Z",
          "start_time": "2025-11-05T12:51:22.018325Z"
        }
      },
      "outputs": [],
      "source": [
        "LAYERS_HERON_R2 = [  # the full set of hardware layers for Heron r2\n",
        "    [\n",
        "        (2, 3),\n",
        "        (6, 7),\n",
        "        (10, 11),\n",
        "        (14, 15),\n",
        "        (20, 21),\n",
        "        (16, 23),\n",
        "        (24, 25),\n",
        "        (17, 27),\n",
        "        (28, 29),\n",
        "        (18, 31),\n",
        "        (32, 33),\n",
        "        (19, 35),\n",
        "        (36, 41),\n",
        "        (42, 43),\n",
        "        (37, 45),\n",
        "        (46, 47),\n",
        "        (38, 49),\n",
        "        (50, 51),\n",
        "        (39, 53),\n",
        "        (60, 61),\n",
        "        (56, 63),\n",
        "        (64, 65),\n",
        "        (57, 67),\n",
        "        (68, 69),\n",
        "        (58, 71),\n",
        "        (72, 73),\n",
        "        (59, 75),\n",
        "        (76, 81),\n",
        "        (82, 83),\n",
        "        (77, 85),\n",
        "        (86, 87),\n",
        "        (78, 89),\n",
        "        (90, 91),\n",
        "        (79, 93),\n",
        "        (94, 95),\n",
        "        (100, 101),\n",
        "        (96, 103),\n",
        "        (104, 105),\n",
        "        (97, 107),\n",
        "        (108, 109),\n",
        "        (98, 111),\n",
        "        (112, 113),\n",
        "        (99, 115),\n",
        "        (116, 121),\n",
        "        (122, 123),\n",
        "        (117, 125),\n",
        "        (126, 127),\n",
        "        (118, 129),\n",
        "        (130, 131),\n",
        "        (119, 133),\n",
        "        (134, 135),\n",
        "        (140, 141),\n",
        "        (136, 143),\n",
        "        (144, 145),\n",
        "        (137, 147),\n",
        "        (148, 149),\n",
        "        (138, 151),\n",
        "        (152, 153),\n",
        "        (139, 155),\n",
        "    ],\n",
        "    [\n",
        "        (1, 2),\n",
        "        (3, 4),\n",
        "        (5, 6),\n",
        "        (7, 8),\n",
        "        (9, 10),\n",
        "        (11, 12),\n",
        "        (13, 14),\n",
        "        (21, 22),\n",
        "        (23, 24),\n",
        "        (25, 26),\n",
        "        (27, 28),\n",
        "        (29, 30),\n",
        "        (31, 32),\n",
        "        (33, 34),\n",
        "        (40, 41),\n",
        "        (43, 44),\n",
        "        (45, 46),\n",
        "        (47, 48),\n",
        "        (49, 50),\n",
        "        (51, 52),\n",
        "        (53, 54),\n",
        "        (55, 59),\n",
        "        (61, 62),\n",
        "        (63, 64),\n",
        "        (65, 66),\n",
        "        (67, 68),\n",
        "        (69, 70),\n",
        "        (71, 72),\n",
        "        (73, 74),\n",
        "        (80, 81),\n",
        "        (83, 84),\n",
        "        (85, 86),\n",
        "        (87, 88),\n",
        "        (89, 90),\n",
        "        (91, 92),\n",
        "        (93, 94),\n",
        "        (95, 99),\n",
        "        (101, 102),\n",
        "        (103, 104),\n",
        "        (105, 106),\n",
        "        (107, 108),\n",
        "        (109, 110),\n",
        "        (111, 112),\n",
        "        (113, 114),\n",
        "        (120, 121),\n",
        "        (123, 124),\n",
        "        (125, 126),\n",
        "        (127, 128),\n",
        "        (129, 130),\n",
        "        (131, 132),\n",
        "        (133, 134),\n",
        "        (135, 139),\n",
        "        (141, 142),\n",
        "        (143, 144),\n",
        "        (145, 146),\n",
        "        (147, 148),\n",
        "        (149, 150),\n",
        "        (151, 152),\n",
        "        (153, 154),\n",
        "    ],\n",
        "    [\n",
        "        (3, 16),\n",
        "        (7, 17),\n",
        "        (11, 18),\n",
        "        (22, 23),\n",
        "        (26, 27),\n",
        "        (30, 31),\n",
        "        (34, 35),\n",
        "        (21, 36),\n",
        "        (25, 37),\n",
        "        (29, 38),\n",
        "        (33, 39),\n",
        "        (41, 42),\n",
        "        (44, 45),\n",
        "        (48, 49),\n",
        "        (52, 53),\n",
        "        (43, 56),\n",
        "        (47, 57),\n",
        "        (51, 58),\n",
        "        (62, 63),\n",
        "        (66, 67),\n",
        "        (70, 71),\n",
        "        (74, 75),\n",
        "        (61, 76),\n",
        "        (65, 77),\n",
        "        (69, 78),\n",
        "        (73, 79),\n",
        "        (81, 82),\n",
        "        (84, 85),\n",
        "        (88, 89),\n",
        "        (92, 93),\n",
        "        (83, 96),\n",
        "        (87, 97),\n",
        "        (91, 98),\n",
        "        (102, 103),\n",
        "        (106, 107),\n",
        "        (110, 111),\n",
        "        (114, 115),\n",
        "        (101, 116),\n",
        "        (105, 117),\n",
        "        (109, 118),\n",
        "        (113, 119),\n",
        "        (121, 122),\n",
        "        (124, 125),\n",
        "        (128, 129),\n",
        "        (132, 133),\n",
        "        (123, 136),\n",
        "        (127, 137),\n",
        "        (131, 138),\n",
        "        (142, 143),\n",
        "        (146, 147),\n",
        "        (150, 151),\n",
        "        (154, 155),\n",
        "        (0, 1),\n",
        "        (4, 5),\n",
        "        (8, 9),\n",
        "        (12, 13),\n",
        "        (54, 55),\n",
        "        (15, 19),\n",
        "    ],\n",
        "]\n",
        "\n",
        "subgraphs = {  # the subgraphs for the different qubit counts such that it's 2D\n",
        "    10: list(range(22, 29)) + [16, 17, 37],\n",
        "    21: list(range(3, 12)) + list(range(23, 32)) + [16, 17, 18],\n",
        "    28: list(range(3, 12))\n",
        "    + list(range(23, 32))\n",
        "    + list(range(45, 50))\n",
        "    + [16, 17, 18, 37, 38],\n",
        "    35: list(range(3, 12))\n",
        "    + list(range(21, 32))\n",
        "    + list(range(41, 50))\n",
        "    + [16, 17, 18, 36, 37, 38],\n",
        "    42: list(range(3, 12))\n",
        "    + list(range(21, 32))\n",
        "    + list(range(41, 50))\n",
        "    + list(range(63, 68))\n",
        "    + [16, 17, 18, 36, 37, 38, 56, 57],\n",
        "}\n",
        "\n",
        "n_qubits = 35  # 21, 28, 35, 42"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "c03b7ca9951aba38",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:25.383040Z",
          "start_time": "2025-11-05T12:51:25.380298Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[[(6, 7), (10, 11), (16, 23), (24, 25), (17, 27), (28, 29), (18, 31), (36, 41), (42, 43), (37, 45), (46, 47), (38, 49)], [(3, 4), (5, 6), (7, 8), (9, 10), (21, 22), (23, 24), (25, 26), (27, 28), (29, 30), (43, 44), (45, 46), (47, 48)], [(3, 16), (7, 17), (11, 18), (22, 23), (26, 27), (30, 31), (21, 36), (25, 37), (29, 38), (41, 42), (44, 45), (48, 49), (4, 5), (8, 9)]]\n"
          ]
        }
      ],
      "source": [
        "layers = [\n",
        "    [\n",
        "        edge\n",
        "        for edge in layer\n",
        "        if edge[0] in subgraphs[n_qubits] and edge[1] in subgraphs[n_qubits]\n",
        "    ]\n",
        "    for layer in LAYERS_HERON_R2\n",
        "]\n",
        "\n",
        "print(layers)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46f93e695a2ae23e",
      "metadata": {},
      "source": [
        "Nous visualisons maintenant la disposition des qubits sur le dispositif Heron pour le sous-graphe sélectionné :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4f02995559bb437f",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:51:33.346900Z",
          "start_time": "2025-11-05T12:51:29.975427Z"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/4f02995559bb437f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "catalog = QiskitFunctionsCatalog(\n",
        "    channel=channel,\n",
        "    token=token,\n",
        "    instance=instance,\n",
        ")\n",
        "backend = catalog.backend(\"ibm_fez\")  # or any available device\n",
        "\n",
        "selected_qubits = subgraphs[n_qubits]\n",
        "num_qubits = backend.configuration().num_qubits\n",
        "qubit_color = [\n",
        "    \"#ff7f0e\" if i in selected_qubits else \"#d3d3d3\"\n",
        "    for i in range(num_qubits)\n",
        "]\n",
        "\n",
        "plot_gate_map(\n",
        "    backend=backend,\n",
        "    figsize=(15, 10),\n",
        "    qubit_color=qubit_color,\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7b6ecaa9",
      "metadata": {},
      "source": [
        "Notez que la connectivité de la disposition de qubits choisie n'est pas nécessairement linéaire et peut couvrir de grandes régions du dispositif Heron en fonction du nombre de qubits sélectionné.\n",
        "\n",
        "Nous générons maintenant le circuit de Trotter et l'observable de magnétisation moyenne pour le nombre de qubits et les paramètres choisis :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1d34dd0d",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T12:56:50.447530Z",
          "start_time": "2025-11-05T12:56:47.922845Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Circuit 2q layers: 27\n",
            "\n",
            "Circuit structure:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/1d34dd0d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,\n",
            " 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j])\n"
          ]
        }
      ],
      "source": [
        "# Chosen parameters:\n",
        "theta_x = 0.53\n",
        "theta_z = 0.1\n",
        "theta_zz = 1.0\n",
        "steps = 9\n",
        "\n",
        "circ = trotter_circuit_from_layers(steps, theta_x, theta_z, theta_zz, layers)\n",
        "print(\n",
        "    f\"Circuit 2q layers: \"\n",
        "    f\"{circ.depth(filter_function=lambda instr: len(instr.qubits) == 2)}\"\n",
        ")\n",
        "print(\"\\nCircuit structure:\")\n",
        "\n",
        "circ.draw(\"mpl\", scale=0.8, fold=-1, idle_wires=False)\n",
        "plt.show()\n",
        "\n",
        "observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [q], 1 / n_qubits) for q in subgraphs[n_qubits]],\n",
        "    np.max(subgraphs[n_qubits]) + 1,\n",
        ")  # Average magnetization observable\n",
        "\n",
        "print(observable)\n",
        "obs_list = [observable]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "76923674",
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7402ea001f0b9f1a",
      "metadata": {},
      "source": [
        "<span id=\"qpu-time-estimation-with-and-without-obp\" />\n",
        "\n",
        "### Estimation du temps QPU avec et sans OBP\n",
        "\n",
        "Les utilisateurs veulent généralement savoir combien de temps de QPU est nécessaire pour leur expérience.\n",
        "Toutefois, ce problème est considéré comme difficile pour les ordinateurs classiques.\n",
        "\n",
        "QESEM propose deux modes d'estimation du temps pour informer les utilisateurs de la faisabilité de leurs expériences :\n",
        "\n",
        "1. Estimation analytique du temps - donne une estimation très approximative et ne nécessite pas de temps de QPU. Ceci peut être utilisé pour tester si une passe de transpilation pourrait potentiellement réduire le temps de QPU.\n",
        "2. Estimation empirique du temps (démontrée ici) - donne une assez bonne estimation et utilise quelques minutes de temps QPU.\n",
        "\n",
        "Dans les deux cas, QESEM fournit une estimation du temps nécessaire pour atteindre la précision requise pour **toutes les** observables.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "478e18ff",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T14:39:16.112626Z",
          "start_time": "2025-11-05T14:39:12.416396Z"
        }
      },
      "outputs": [],
      "source": [
        "run_on_real_hardware = True\n",
        "\n",
        "precision = 0.05\n",
        "if run_on_real_hardware:\n",
        "    backend_name = \"ibm_fez\"\n",
        "else:\n",
        "    backend_name = \"fake_fez\"\n",
        "\n",
        "# Start a job for empirical time estimation\n",
        "estimation_job_wo_obp = qesem_function.run(\n",
        "    pubs=[(circ, obs_list)],\n",
        "    instance=instance,\n",
        "    backend_name=backend_name,  # E.g. \"ibm_brisbane\"\n",
        "    options={\n",
        "        # \"empirical\" - gets actual time estimates without running full mitigation\n",
        "        \"estimate_time_only\": \"empirical\",\n",
        "        \"max_execution_time\": 120,  # Limits the QPU time, specified in seconds.\n",
        "        \"default_precision\": precision,\n",
        "    },\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "bd0ff03fe6324ce5",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:04:27.695966Z",
          "start_time": "2025-11-05T15:04:25.885170Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "17d3828e-9fdb-482e-8e9b-392f3eefe313\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "print(estimation_job_wo_obp.job_id)\n",
        "print(estimation_job_wo_obp.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6357b2b5",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:09:13.194534Z",
          "start_time": "2025-11-05T15:09:05.286214Z"
        }
      },
      "outputs": [],
      "source": [
        "# Get the result object (blocking method).\n",
        "# Use job.status() in a loop for non-blocking.\n",
        "# This takes 1-3 minutes\n",
        "result = estimation_job_wo_obp.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "1e3eab49",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:04:47.335176Z",
          "start_time": "2025-11-05T15:04:47.332618Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Empirical time estimation (sec): 1200\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f\"Empirical time estimation (sec): {result[0].metadata['time_estimation_sec']}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75dbab74",
      "metadata": {},
      "source": [
        "Nous allons maintenant utiliser la rétropropagation par opérateur (OBP). (Consultez la documentation [OBP](https://qiskit.github.io/qiskit-addon-obp/) pour plus de détails sur l'extension OBP Qiskit.) Nous allons créer une fonction qui génère les tranches de circuit pour la rétropropagation :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2ac55a76daddddcf",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:08:38.887654Z",
          "start_time": "2025-11-05T15:08:38.878679Z"
        }
      },
      "outputs": [],
      "source": [
        "def run_backpropagation(circ_vec, observable, steps_vec, max_qwc_groups=8):\n",
        "    \"\"\"\n",
        "    Runs backpropagation for a list of circuits and observables.\n",
        "    Returns lists of backpropagated circuits and observables.\n",
        "    \"\"\"\n",
        "    op_budget = OperatorBudget(max_qwc_groups=max_qwc_groups)\n",
        "    bp_circuit_vec = []\n",
        "    bp_observable_vec = []\n",
        "\n",
        "    for i, circ in enumerate(circ_vec):\n",
        "        slices = slice_by_gate_types(circ)\n",
        "        bp_observable, remaining_slices, metadata = backpropagate(\n",
        "            observable,\n",
        "            slices,\n",
        "            operator_budget=op_budget,\n",
        "        )\n",
        "        bp_circuit = combine_slices(remaining_slices, include_barriers=True)\n",
        "        bp_circuit_vec.append(bp_circuit)\n",
        "        bp_observable_vec.append(bp_observable)\n",
        "        print(f\"n.o. steps: {steps_vec[i]}\")\n",
        "        print(f\"Backpropagated {metadata.num_backpropagated_slices} slices.\")\n",
        "        print(\n",
        "            f\"New observable has {len(bp_observable.paulis)} terms, \"\n",
        "            f\"which can be combined into \"\n",
        "            f\"{len(bp_observable.group_commuting(qubit_wise=True))} groups.\\n\"\n",
        "            f\"After truncation, the error in our observable is bounded by \"\n",
        "            f\"{metadata.accumulated_error(0):.3e}\"\n",
        "        )\n",
        "        print(\"-----------------\")\n",
        "    return bp_circuit_vec, bp_observable_vec"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "84135dc1fb687d2d",
      "metadata": {},
      "source": [
        "Nous appelons la fonction :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "1bc2f43bd93b3a92",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:15:00.391655Z",
          "start_time": "2025-11-05T15:14:59.993228Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "n.o. steps: 9\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n"
          ]
        }
      ],
      "source": [
        "bp_circ_vec, bp_obs_vec = run_backpropagation([circ], observable, [steps])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "cedb7fa1",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:15:53.031008Z",
          "start_time": "2025-11-05T15:15:49.863350Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The remaining circuit after backpropagation looks as follows:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/cedb7fa1-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "print(\"The remaining circuit after backpropagation looks as follows:\")\n",
        "bp_circ_vec[-1].draw(\"mpl\", scale=0.8, fold=-1, idle_wires=False)\n",
        "None"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fc1e532a",
      "metadata": {},
      "source": [
        "Nous pouvons voir que la rétropropagation a réduit deux couches du circuit.\n",
        "Maintenant que nous avons notre circuit réduit et nos observables étendus, procédons à l'estimation du temps pour le circuit rétropropagé :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "88160fbc",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T15:27:07.496690Z",
          "start_time": "2025-11-05T15:27:01.891318Z"
        }
      },
      "outputs": [],
      "source": [
        "# Start a job for empirical time estimation\n",
        "estimation_job_obp = qesem_function.run(\n",
        "    pubs=[(bp_circ_vec[-1], [bp_obs_vec[-1]])],\n",
        "    instance=instance,\n",
        "    backend_name=backend_name,\n",
        "    options={\n",
        "        \"estimate_time_only\": \"empirical\",\n",
        "        \"max_execution_time\": 120,\n",
        "        \"default_precision\": precision,\n",
        "    },\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "c9ec8a5dcf0bfb21",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:15:10.776085Z",
          "start_time": "2025-11-05T17:15:08.740353Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "8bae699d-a16b-4d39-bbd9-d123fbcce55d\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "print(estimation_job_obp.job_id)\n",
        "print(estimation_job_obp.status())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "19cd4cc2",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:15:19.426157Z",
          "start_time": "2025-11-05T17:15:13.048448Z"
        }
      },
      "outputs": [],
      "source": [
        "result_obp = estimation_job_obp.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "feca3059",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:15:21.205216Z",
          "start_time": "2025-11-05T17:15:21.203160Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Empirical time estimation (sec): 900\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    f\"Empirical time estimation (sec): {result_obp[0].metadata['time_estimation_sec']}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "504669f5",
      "metadata": {},
      "source": [
        "Nous constatons que l'OBP réduit le coût en temps de l'atténuation du circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4d1d5092",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3da535e9",
      "metadata": {},
      "source": [
        "<span id=\"run-with-real-backend\" />\n",
        "\n",
        "### Exécutez avec un backend réel\n",
        "\n",
        "Nous allons maintenant réaliser l'expérience complète sur quelques marches de Trotter. Le nombre de qubits, la précision requise et la durée maximale de la QPU peuvent être modifiés en fonction des ressources disponibles de la QPU. Notez que le fait de limiter la durée maximale de la QPU affectera la précision finale, comme vous le verrez dans le graphique final ci-dessous.\n",
        "\n",
        "Nous analysons quatre circuits avec 5, 7 et 9 pas de Trotter à une précision de 0.05, en comparant leurs valeurs d'espérance idéales, bruitées et atténuées par les erreurs :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 84,
      "id": "7cfb4dbc",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T13:47:04.428822Z",
          "start_time": "2025-11-06T13:47:04.415793Z"
        }
      },
      "outputs": [],
      "source": [
        "steps_vec = [5, 7, 9]\n",
        "\n",
        "circ_vec = []\n",
        "for steps in steps_vec:\n",
        "    circ = trotter_circuit_from_layers(\n",
        "        steps, theta_x, theta_z, theta_zz, layers\n",
        "    )\n",
        "    circ_vec.append(circ)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d63c1de0",
      "metadata": {},
      "source": [
        "Là encore, nous effectuons l'OBP sur chaque circuit afin de réduire le temps d'exécution :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 85,
      "id": "267e030f",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T13:47:10.089306Z",
          "start_time": "2025-11-06T13:47:09.003172Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "n.o. steps: 5\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n",
            "n.o. steps: 7\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n",
            "n.o. steps: 9\n",
            "Backpropagated 11 slices.\n",
            "New observable has 363 terms, which can be combined into 4 groups.\n",
            "After truncation, the error in our observable is bounded by 0.000e+00\n",
            "-----------------\n"
          ]
        }
      ],
      "source": [
        "bp_circ_vec_35, bp_obs_vec_35 = run_backpropagation(\n",
        "    circ_vec, observable, steps_vec\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68bb0915",
      "metadata": {},
      "source": [
        "Nous allons maintenant exécuter un lot de tâches QESEM complètes. Nous limitons la durée d'exécution maximale de la QPU pour chacun des points afin de mieux contrôler le budget de la QPU.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 106,
      "id": "c039197fca88d16a",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T16:41:01.087031Z",
          "start_time": "2025-11-06T16:41:01.073625Z"
        }
      },
      "outputs": [],
      "source": [
        "run_on_real_hardware = True\n",
        "\n",
        "precision = 0.05\n",
        "if run_on_real_hardware:\n",
        "    backend_name = \"ibm_marrakesh\"\n",
        "else:\n",
        "    backend_name = \"fake_fez\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "id": "e60a2fc8",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:27:41.525432Z",
          "start_time": "2025-11-05T17:27:41.523337Z"
        }
      },
      "outputs": [],
      "source": [
        "# Running full jobs for:\n",
        "pubs_list = [\n",
        "    [(bp_circ_vec_35[i], bp_obs_vec_35[i])] for i in range(len(bp_obs_vec_35))\n",
        "]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "534ad1d013e4fb2",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T17:29:17.142120Z",
          "start_time": "2025-11-05T17:29:04.155649Z"
        }
      },
      "outputs": [],
      "source": [
        "# Initiating multiple jobs for different lengths\n",
        "job_list = []\n",
        "for pubs in pubs_list:\n",
        "    job_obp = qesem_function.run(\n",
        "        pubs=pubs,\n",
        "        instance=instance,\n",
        "        backend_name=backend_name,  # E.g. \"ibm_brisbane\"\n",
        "        options={\n",
        "            \"max_execution_time\": 300,  # Limits the QPU time, specified in seconds.\n",
        "            \"default_precision\": 0.05,\n",
        "        },\n",
        "    )\n",
        "    job_list.append(job_obp)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "05c75ada",
      "metadata": {},
      "source": [
        "Nous vérifions ici l'état de chaque travail :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 61,
      "id": "b869fd4f",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-05T19:44:14.257551Z",
          "start_time": "2025-11-05T19:44:08.130764Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "DONE\n",
            "DONE\n",
            "DONE\n",
            "DONE\n"
          ]
        }
      ],
      "source": [
        "for job in job_list:\n",
        "    print(job.status())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68cabcc4",
      "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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "72e511b12012254d",
      "metadata": {},
      "source": [
        "Lorsque tous les travaux sont terminés, nous pouvons comparer leur valeur d'attente bruyante et atténuée.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 130,
      "id": "9bc7cce51e0d4b4c",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T20:49:40.252799Z",
          "start_time": "2025-11-06T20:49:14.517682Z"
        }
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.\n",
            "---------------------------------\n",
            "Ideal: 0.79537\n",
            "Noisy: 0.7039237951821501\n",
            "QESEM: 0.7828018244130982 ± 0.013257266977728376\n",
            "Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.\n",
            "---------------------------------\n",
            "Ideal: 0.78653\n",
            "Noisy: 0.6478583812958806\n",
            "QESEM: 0.7875259197423828 ± 0.02703045139248604\n",
            "Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.\n",
            "---------------------------------\n",
            "Ideal: 0.79699\n",
            "Noisy: 0.6171787879868142\n",
            "QESEM: 0.6918791909168913 ± 0.0740873782039517\n"
          ]
        }
      ],
      "source": [
        "ideal_values = []\n",
        "noisy_values = []\n",
        "error_mitigated_values = []\n",
        "error_mitigated_stds = []\n",
        "\n",
        "for i in range(len(job_list)):\n",
        "    job = job_list[i]\n",
        "    result = job.result()  # Blocking - takes 3-5 minutes\n",
        "    noisy_results = result[0].metadata[\"noisy_results\"]\n",
        "\n",
        "    ideal_val = calculate_ideal_evs(circ_vec[i], observable, n_qubits, i)\n",
        "    print(\"---------------------------------\")\n",
        "    print(f\"Ideal: {ideal_val}\")\n",
        "    print(f\"Noisy: {noisy_results.evs}\")\n",
        "    print(f\"QESEM: {result[0].data.evs} \\u00b1 {result[0].data.stds}\")\n",
        "\n",
        "    ideal_values.append(ideal_val)\n",
        "    noisy_values.append(noisy_results.evs)\n",
        "    error_mitigated_values.append(result[0].data.evs)\n",
        "    error_mitigated_stds.append(result[0].data.stds)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "52d3b66d2e42b464",
      "metadata": {},
      "source": [
        "Enfin, nous pouvons tracer l'aimantation en fonction du nombre de pas. Cela résume les avantages de l'utilisation de la fonction Qiskit de QESEM pour l'atténuation des erreurs sans biais sur les dispositifs quantiques bruyants.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 131,
      "id": "0f1a44d0",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-11-06T20:49:49.067165Z",
          "start_time": "2025-11-06T20:49:48.990714Z"
        }
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0, 0.5, 'Magnetization')"
            ]
          },
          "execution_count": 131,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/qedma-2d-ising-with-qesem/extracted-outputs/0f1a44d0-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(steps_vec, ideal_values, \"--\", label=\"ideal\")\n",
        "plt.scatter(steps_vec, noisy_values, label=\"noisy\")\n",
        "plt.errorbar(\n",
        "    steps_vec,\n",
        "    error_mitigated_values,\n",
        "    yerr=error_mitigated_stds,\n",
        "    fmt=\"o\",\n",
        "    capsize=5,\n",
        "    label=\"QESEM mitigation\",\n",
        ")\n",
        "plt.legend()\n",
        "plt.xlabel(\"n.o. steps\")\n",
        "plt.ylabel(\"Magnetization\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e14cb01633a8451a",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\">\n",
        "  La neuvième étape présente une barre d'erreur statistique importante car nous avons limité la durée de la QPU à 5 minutes. Si vous exécutez cette étape pendant 15 minutes (comme le suggère l'estimation empirique du temps), vous obtiendrez une barre d'erreur plus petite. La valeur atténuée se rapprochera donc de la valeur idéale.\n",
        "</Admonition>\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": 1200
  },
  "nbformat": 4,
  "nbformat_minor": 5
}