{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3445b0b5-7f26-4610-8e1b-5112df1fccb0",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Combiner les options d'atténuation des erreurs avec la primitive Estimator\"\n",
        "description: \"Combinez les options d'atténuation des erreurs pour les expériences à grande échelle à l'aide des QPU « 100Q+ » IBM Quantum et de la primitive « Estimator » de l' IBM Quantum.\"\n",
        "---\n",
        "\n",
        "<span id=\"combine-error-mitigation-options-with-the-estimator-primitive\" />\n",
        "\n",
        "# Combiner les options d'atténuation des erreurs avec la primitive Estimator\n",
        "\n",
        "*Estimation du temps d'exécution : 7 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": "39ba4396-9111-4421-9371-542f067bc816",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Résultats d'apprentissage\n",
        "\n",
        "Nous recommandons aux utilisateurs de se familiariser avec les sujets suivants avant de suivre ce tutoriel :\n",
        "\n",
        "* Les principes fondamentaux du découplage dynamique, de l'atténuation des erreurs de mesure, du « gate twirling » et de l'extrapolation sans bruit, tels que décrits dans ce [guide](/docs/guides/error-mitigation-and-suppression-techniques).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b27cb482-b77a-4953-a0a4-39df33896d12",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prérequis\n",
        "\n",
        "À l'issue de ce tutoriel, les utilisateurs devraient avoir compris :\n",
        "\n",
        "* Comment les techniques d'atténuation des erreurs susmentionnées sont mises en œuvre de manière sélective au niveau matériel.\n",
        "* Comment se comparent-ils en termes de capacité à atténuer le bruit du matériel?\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "018313d1-d959-4011-81df-59bf7c157470",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Ce tutoriel présente les options de suppression et d'atténuation des erreurs disponibles avec la primitive « Estimator » de l' IBM Quantum. Ce tutoriel explique comment implémenter chacune des méthodes suivantes séparément :\n",
        "\n",
        "* Découplage dynamique\n",
        "* Atténuation des erreurs de mesure\n",
        "* Tournoiement de la porte\n",
        "* Extrapolation à bruit nul (ZNE)\n",
        "\n",
        "Il convient de noter qu'au lieu de mettre en œuvre ces techniques individuellement, il est possible de les mettre en œuvre à l'aide d'un [niveau de résilience](/docs/guides/estimator-noise-management), qui `resilience_level` peut prendre les valeurs 0, 1 ou 2 :\n",
        "\n",
        "* 0 : Aucune mesure d'atténuation n'est mise en œuvre.\n",
        "* 1 : Une mesure visant à réduire les erreurs de mesure a été mise en place.\n",
        "* 2 : La rotation des portes, l'atténuation des erreurs de mesure et le ZNE sont mis en œuvre.\n",
        "\n",
        "Dans ce tutoriel, vous allez monter un circuit et un observable, puis soumettre des tâches à l'aide de la primitive Estimator en utilisant différentes combinaisons de paramètres d'atténuation des erreurs. Vous représenterez ensuite les résultats graphiquement afin d'observer les effets des différents paramètres. La majeure partie du tutoriel utilise un circuit de 10 qubits pour faciliter la visualisation, et à la fin, vous étendrez le processus à 50 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0f3d3378-8dac-489e-bf6d-292e693ec145",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Avant de commencer ce guide, assurez-vous que les éléments suivants sont installés :\n",
        "\n",
        "* Qiskit SDK v2.1 ou plus tard, avec prise en charge de [la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 ou plus tard (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "069658fa-fa45-48b8-ab69-2610228bf6e4",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "95a72452-e067-4ee5-bcd9-e3bfcf0de208",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "from qiskit.circuit.library import efficient_su2, unitary_overlap\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Batch, EstimatorV2 as Estimator"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d15c5fde-5046-4d86-86d8-72d1ed748c4e",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Exemple de simulateur à petite échelle\n",
        "\n",
        "Nous allons ignorer cette étape, car la gestion des erreurs d'exécution n'est pas prise en charge sur les simulateurs.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b2be0539-4f60-46e8-be1f-8869dae30888",
      "metadata": {},
      "source": [
        "<span id=\"hardware-example\" />\n",
        "\n",
        "## Exemple de matériel\n",
        "\n",
        "<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",
        "Cette procédure suppose que le problème classique a déjà été transposé au niveau quantique. Commencez par construire un circuit et un observateur pour effectuer les mesures. Bien que les techniques utilisées ici s'appliquent à de nombreux types de circuits différents, pour plus de simplicité, ce guide utilise le [`efficient_su2`](/docs/api/qiskit/qiskit.circuit.library.efficient_su2) circuit inclus dans la bibliothèque de circuits Qiskit.\n",
        "\n",
        "`efficient_su2` Il s'agit d'un circuit quantique paramétré conçu pour être exécuté efficacement sur du matériel quantique doté d'une connectivité limitée entre les qubits, tout en restant suffisamment puissant pour résoudre des problèmes dans des domaines d'application tels que l'optimisation et la chimie. Il est construit en alternant des couches de portes à un qubit paramétrées avec une couche contenant un motif fixe de portes à deux qubits, pour un nombre de répétitions donné. La configuration des portes à deux qubits peut être définie par l'utilisateur. Ici, vous pouvez utiliser le schéma `pairwise` intégré, car il permet de réduire au minimum la profondeur du circuit en regroupant les portes à deux qubits de la manière la plus compacte possible. Ce schéma peut être mis en œuvre en utilisant uniquement une connectivité linéaire entre les qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "24abd7ba-bbb8-443b-9e81-866795d39a6c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/24abd7ba-bbb8-443b-9e81-866795d39a6c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "n_qubits = 10\n",
        "reps = 1\n",
        "\n",
        "circuit = efficient_su2(n_qubits, entanglement=\"pairwise\", reps=reps)\n",
        "\n",
        "circuit.decompose().draw(\"mpl\", scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "34fec53b-a8b2-461b-a20d-e8ad54b992fa",
      "metadata": {},
      "source": [
        "Comme observable, prenons l'opérateur d' $Z$ de Pauli agissant sur le dernier qubit, $Z I \\cdots I$.\n",
        "Notons que le fait que le dernier qubit corresponde au premier élément de cette chaîne est dû à l'utilisation par Qiskit de la notation « little-endian ».\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "9500ba7b-4542-465f-a4e1-60165d6b6d1e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Z on the last qubit (index -1) with coefficient 1.0\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [-1], 1.0)], num_qubits=n_qubits\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46eff743-de9f-4f55-9c47-02c6d6115b80",
      "metadata": {},
      "source": [
        "À ce stade, vous pouvez lancer votre circuit et mesurer la grandeur observable. Cependant, vous souhaitez également comparer le résultat obtenu par le dispositif quantique avec la réponse correcte, c'est-à-dire la valeur théorique de l'observable, si le circuit avait été exécuté sans erreur. Pour les petits circuits quantiques, il est possible de calculer cette valeur en simulant le circuit sur un ordinateur classique, mais cela n'est pas possible pour les circuits plus grands, destinés à une utilisation à grande échelle. Vous pouvez contourner ce problème grâce à la technique du « circuit miroir » (également appelée « compute-uncompute »), qui s'avère utile pour évaluer les performances des dispositifs quantiques.\n",
        "\n",
        "<span id=\"mirror-circuit\" />\n",
        "\n",
        "#### Circuit miroir\n",
        "\n",
        "Dans la technique du circuit miroir, vous concaténé le circuit avec son circuit inverse, qui est formé en inversant chaque porte du circuit dans l'ordre inverse. Le circuit résultant met en œuvre l'opérateur d'identité, qui peut trivialement être simulé. Comme la structure du circuit original est préservée dans le circuit miroir, l'exécution du circuit miroir donne toujours une idée de la manière dont le dispositif quantique fonctionnerait sur le circuit original.\n",
        "\n",
        "La cellule de code suivante attribue des paramètres aléatoires à votre circuit, puis construit le circuit miroir à l'aide de la [`unitary_overlap`](/docs/api/qiskit/qiskit.circuit.library.unitary_overlap) classe. Avant de refléter le circuit, ajoutez-y une instruction [de barrière](/docs/api/qiskit/circuit#qiskit.circuit.Barrier) afin d'empêcher le transpilateur de fusionner les deux parties du circuit situées de part et d'autre de la barrière, ce qui donnerait un circuit transpilé dépourvu de portes logiques.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "4dbde811-1ba9-47a8-85a0-dcaff054ed60",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/4dbde811-1ba9-47a8-85a0-dcaff054ed60-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Generate random parameters\n",
        "rng = np.random.default_rng(1234)\n",
        "params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)\n",
        "\n",
        "# Assign the parameters to the circuit\n",
        "assigned_circuit = circuit.assign_parameters(params)\n",
        "\n",
        "# Add a barrier to prevent circuit optimization of mirrored operators\n",
        "assigned_circuit.barrier()\n",
        "\n",
        "# Construct mirror circuit\n",
        "mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)\n",
        "\n",
        "mirror_circuit.decompose().draw(\"mpl\", scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a37c97de-c942-4419-9409-5bcaef9c6876",
      "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",
        "Vous devez optimiser votre circuit avant de l'exécuter sur du matériel. Ce processus comporte plusieurs étapes :\n",
        "\n",
        "* Choisissez une disposition de qubits qui fait correspondre les qubits virtuels de votre circuit aux qubits physiques du matériel.\n",
        "* Insérez des portes de permutation si nécessaire pour acheminer les interactions entre les qubits qui ne sont pas connectés.\n",
        "* Traduire les portes de votre circuit en instructions [ISA (Instruction Set Architecture)](/docs/guides/transpile#instruction-set-architecture) qui peuvent être directement exécutées sur le matériel.\n",
        "* Effectuer des optimisations de circuit pour minimiser la profondeur du circuit et le nombre de portes.\n",
        "\n",
        "Le transcompilateur intégré à Qiskit peut effectuer toutes ces étapes à votre place. Comme cet exemple utilise un circuit efficace sur le plan matériel, le transcompilateur devrait être capable de choisir une disposition des qubits qui ne nécessite pas l'insertion de portes d'échange pour acheminer les interactions.\n",
        "\n",
        "Vous devez choisir le périphérique matériel à utiliser avant d'optimiser votre circuit. La cellule de code suivante recherche le dispositif le moins sollicité disposant d'au moins 127 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2c3ea768-0596-4db1-9b51-3ff78ec65818",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "aeaf076c-1c0a-4ec4-af66-49eff621589d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "print(backend)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46280e59-8c9f-4dd9-a365-6a230ceccf42",
      "metadata": {},
      "source": [
        "Vous pouvez transcompiler votre circuit vers le backend de votre choix en créant un gestionnaire de passes, puis en l'exécutant sur le circuit. Une façon simple de créer un gestionnaire de mots de passe consiste à utiliser la [`generate_preset_pass_manager`](/docs/api/qiskit/qiskit.transpiler.generate_preset_pass_manager) fonction. Pour une explication plus détaillée de la transcompilation à l'aide des gestionnaires de passes, consultez [la section « Transpile with pass managers](/docs/guides/transpile-with-pass-managers) ».\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "027e829a-44d3-455e-b2bf-8ce0d7e26b9b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/027e829a-44d3-455e-b2bf-8ce0d7e26b9b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pass_manager = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=1234\n",
        ")\n",
        "isa_circuit = pass_manager.run(mirror_circuit)\n",
        "\n",
        "isa_circuit.draw(\"mpl\", idle_wires=False, scale=0.7, fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1865671e-2c7a-44dd-8938-fe4637bb5993",
      "metadata": {},
      "source": [
        "Le circuit transpilé ne contient désormais plus que des instructions ISA. Toutes les portes ont été décomposées en portes de type « $\\sqrt{X}$ », en rotations de type « $R_z$ » et en [portes CZ](/docs/api/qiskit/qiskit.circuit.library.CZGate).\n",
        "\n",
        "Le processus de transpilation a mis en correspondance les qubits virtuels du circuit avec les qubits physiques du matériel. Les informations relatives à la disposition du qubit sont stockées dans l'attribut `layout` du circuit transposé. L'observable a également été défini en termes de qubits virtuels, vous devez donc appliquer cette disposition à l'observable, ce que vous pouvez faire avec la méthode [`apply_layout`](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp#apply_layout) de `SparsePauliOp`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "12338588-a2d0-4b9c-9e49-e25ec8617b93",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original observable:\n",
            "SparsePauliOp(['ZIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n",
            "\n",
            "Observable with layout applied:\n",
            "SparsePauliOp(['IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n"
          ]
        }
      ],
      "source": [
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "print(\"Original observable:\")\n",
        "print(observable)\n",
        "print()\n",
        "print(\"Observable with layout applied:\")\n",
        "print(isa_observable)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "439d4edf-d1ad-49f3-b206-320958be4f82",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "Vous êtes maintenant prêt à faire fonctionner votre circuit en utilisant la primitive Estimator.\n",
        "\n",
        "Vous allez ici soumettre cinq tâches distinctes, en commençant sans aucune option de suppression ou d'atténuation des erreurs, puis en activant successivement les différentes options de suppression et d'atténuation des erreurs disponibles dans le service de calcul d' IBM Quantum. Pour plus d'informations sur les options, consultez les pages suivantes :\n",
        "\n",
        "* [Aperçu de toutes les options](/docs/api/qiskit-ibm-runtime/options)\n",
        "* [Découplage dynamique](/docs/api/qiskit-ibm-runtime/options-dynamical-decoupling-options)\n",
        "* [Résilience, y compris l'atténuation des erreurs de mesure et l'extrapolation à bruit nul (ZNE)](/docs/api/qiskit-ibm-runtime/options-resilience-options-v2)\n",
        "* [Tourbillon](/docs/api/qiskit-ibm-runtime/options-twirling-options)\n",
        "\n",
        "Ces tâches pouvant s'exécuter indépendamment les unes des autres, vous pouvez utiliser [le mode batch](/docs/guides/run-jobs-batch) pour permettre à Quantum Compute d'optimiser le calendrier de leur exécution.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1114d15b-1408-4f4c-a20f-60f0da722325",
      "metadata": {},
      "outputs": [],
      "source": [
        "pub = (isa_circuit, isa_observable)\n",
        "\n",
        "jobs = []\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    estimator = Estimator(mode=batch)\n",
        "    estimator.options.environment.job_tags = [\n",
        "        \"TUT_CEM_SS\"\n",
        "    ]  # add tag for this small scale job\n",
        "    # Set number of shots\n",
        "    estimator.options.default_shots = 100_000\n",
        "    # Disable runtime compilation and error mitigation\n",
        "    estimator.options.resilience_level = 0\n",
        "\n",
        "    # Run job with no error mitigation\n",
        "    job0 = estimator.run([pub])\n",
        "    jobs.append(job0)\n",
        "\n",
        "    # Add dynamical decoupling (DD)\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "    job1 = estimator.run([pub])\n",
        "    jobs.append(job1)\n",
        "\n",
        "    # Add readout error mitigation (DD + TREX)\n",
        "    estimator.options.resilience.measure_mitigation = True\n",
        "    job2 = estimator.run([pub])\n",
        "    jobs.append(job2)\n",
        "\n",
        "    # Add gate twirling (DD + TREX + Gate Twirling)\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "    job3 = estimator.run([pub])\n",
        "    jobs.append(job3)\n",
        "\n",
        "    # Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)\n",
        "    estimator.options.resilience.zne_mitigation = True\n",
        "    estimator.options.resilience.zne.noise_factors = (1, 3, 5)\n",
        "    estimator.options.resilience.zne.extrapolator = (\"exponential\", \"linear\")\n",
        "    job4 = estimator.run([pub])\n",
        "    jobs.append(job4)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "919517c8-6f42-48d9-a976-53c715c00a4b",
      "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",
        "Enfin, vous pouvez analyser les données. Ici, vous récupérez les résultats du travail, vous en extrayez les valeurs attendues mesurées et vous tracez les valeurs, y compris les barres d'erreur d'un écart type.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "eef38976-0ca2-429a-b2dc-41aac69605f7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/eef38976-0ca2-429a-b2dc-41aac69605f7-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Retrieve the job results\n",
        "results = [job.result() for job in jobs]\n",
        "\n",
        "# Unpack the PUB results (there's only one PUB result in each job result)\n",
        "pub_results = [result[0] for result in results]\n",
        "\n",
        "# Unpack the expectation values and standard errors\n",
        "expectation_vals = np.array(\n",
        "    [float(pub_result.data.evs) for pub_result in pub_results]\n",
        ")\n",
        "standard_errors = np.array(\n",
        "    [float(pub_result.data.stds) for pub_result in pub_results]\n",
        ")\n",
        "\n",
        "# Plot the expectation values\n",
        "fig, ax = plt.subplots()\n",
        "labels = [\"No mitigation\", \"+ DD\", \"+ TREX\", \"+ Twirling\", \"+ ZNE\"]\n",
        "ax.bar(\n",
        "    range(len(labels)),\n",
        "    expectation_vals,\n",
        "    yerr=standard_errors,\n",
        "    label=\"experiment\",\n",
        ")\n",
        "ax.axhline(y=1.0, color=\"gray\", linestyle=\"--\", label=\"ideal\")\n",
        "ax.set_xticks(range(len(labels)))\n",
        "ax.set_xticklabels(labels)\n",
        "ax.set_ylabel(\"Expectation value\")\n",
        "ax.legend(loc=\"upper left\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b400eae8-f38c-4566-82fe-0ccc5ee3f05d",
      "metadata": {},
      "source": [
        "À cette petite échelle, il est difficile de voir l'effet de la plupart des techniques d'atténuation des erreurs, mais l'extrapolation à bruit nul apporte une amélioration notable. Il convient toutefois de noter que cette amélioration n'est pas gratuite, car le résultat ZNE présente également une barre d'erreur plus importante.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "958a5d23-ea09-4750-827c-e3bf6c3fa3f4",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Exemple de matériel à grande échelle\n",
        "\n",
        "Lors du développement d'une expérience, il est utile de commencer par un petit circuit afin de faciliter les visualisations et les simulations. Maintenant que vous avez développé et testé notre flux de travail sur un circuit à 10 qubits, vous pouvez le faire passer à 50 qubits. La cellule de code suivante répète toutes les étapes de cette procédure pas à pas, mais les applique désormais à un circuit de 50 qubits.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "d7d8408b-faf1-4eda-ab9c-bdeaab01ff53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/d7d8408b-faf1-4eda-ab9c-bdeaab01ff53-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n_qubits = 50\n",
        "reps = 1\n",
        "\n",
        "# Construct circuit and observable\n",
        "circuit = efficient_su2(n_qubits, entanglement=\"pairwise\", reps=reps)\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [-1], 1.0)], num_qubits=n_qubits\n",
        ")\n",
        "\n",
        "# Assign parameters to circuit\n",
        "params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)\n",
        "assigned_circuit = circuit.assign_parameters(params)\n",
        "assigned_circuit.barrier()\n",
        "\n",
        "# Construct mirror circuit\n",
        "mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)\n",
        "\n",
        "# Transpile circuit and observable\n",
        "isa_circuit = pass_manager.run(mirror_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "# Run jobs\n",
        "pub = (isa_circuit, isa_observable)\n",
        "\n",
        "jobs = []\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    estimator = Estimator(mode=batch)\n",
        "    estimator.options.environment.job_tags = [\n",
        "        \"TUT_CEM_LS\"\n",
        "    ]  # add tag for this large scale job\n",
        "    # Set number of shots\n",
        "    estimator.options.default_shots = 100_000\n",
        "    # Disable runtime compilation and error mitigation\n",
        "    estimator.options.resilience_level = 0\n",
        "\n",
        "    # Run job with no error mitigation\n",
        "    job0 = estimator.run([pub])\n",
        "    jobs.append(job0)\n",
        "\n",
        "    # Add dynamical decoupling (DD)\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "    job1 = estimator.run([pub])\n",
        "    jobs.append(job1)\n",
        "\n",
        "    # Add readout error mitigation (DD + TREX)\n",
        "    estimator.options.resilience.measure_mitigation = True\n",
        "    job2 = estimator.run([pub])\n",
        "    jobs.append(job2)\n",
        "\n",
        "    # Add gate twirling (DD + TREX + Gate Twirling)\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "    job3 = estimator.run([pub])\n",
        "    jobs.append(job3)\n",
        "\n",
        "    # Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)\n",
        "    estimator.options.resilience.zne_mitigation = True\n",
        "    estimator.options.resilience.zne.noise_factors = (1, 3, 5)\n",
        "    estimator.options.resilience.zne.extrapolator = (\"exponential\", \"linear\")\n",
        "    job4 = estimator.run([pub])\n",
        "    jobs.append(job4)\n",
        "\n",
        "# Retrieve the job results\n",
        "results = [job.result() for job in jobs]\n",
        "\n",
        "# Unpack the PUB results (there's only one PUB result in each job result)\n",
        "pub_results = [result[0] for result in results]\n",
        "\n",
        "# Unpack the expectation values and standard errors\n",
        "expectation_vals = np.array(\n",
        "    [float(pub_result.data.evs) for pub_result in pub_results]\n",
        ")\n",
        "standard_errors = np.array(\n",
        "    [float(pub_result.data.stds) for pub_result in pub_results]\n",
        ")\n",
        "\n",
        "# Plot the expectation values\n",
        "fig, ax = plt.subplots()\n",
        "labels = [\"No mitigation\", \"+ DD\", \"+ TREX\", \"+ Twirling\", \"+ ZNE\"]\n",
        "ax.bar(\n",
        "    range(len(labels)),\n",
        "    expectation_vals,\n",
        "    yerr=standard_errors,\n",
        "    label=\"experiment\",\n",
        ")\n",
        "ax.axhline(y=1.0, color=\"gray\", linestyle=\"--\", label=\"ideal\")\n",
        "ax.set_xticks(range(len(labels)))\n",
        "ax.set_xticklabels(labels)\n",
        "ax.set_ylabel(\"Expectation value\")\n",
        "ax.legend(loc=\"upper left\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32d9b036-ea0a-4484-b6ee-036ff732026b",
      "metadata": {},
      "source": [
        "Lorsque vous comparez les résultats de 50 qubits avec les résultats de 10 qubits obtenus précédemment, vous pouvez noter les éléments suivants (vos résultats peuvent différer d'une série à l'autre) :\n",
        "\n",
        "* Toutes les expériences donnent des résultats plus proches de la valeur idéale et toutes les barres d'erreur sont plus petites.\n",
        "* L'ajout du découplage dynamique pourrait avoir nui aux performances par rapport au scénario sans mesure d'atténuation. Cela n'a rien d'étonnant, car le circuit est très dense. Le découplage dynamique est particulièrement utile lorsqu'il existe de longs intervalles dans le circuit, pendant lesquels les qubits restent inactifs sans qu'aucune porte ne leur soit appliquée. Lorsque ces intervalles font défaut, le découplage dynamique n'est pas efficace et peut même nuire aux performances en raison d'erreurs dans les impulsions de découplage dynamique elles-mêmes. Il se peut que le circuit de 10 qubits ait été trop petit pour nous permettre d'observer cet effet.\n",
        "* Avec une extrapolation sans bruit, le résultat est très proche de la valeur idéale. Cela démontre toute la puissance de ZNE.\n",
        "\n",
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  Si ce travail vous a paru intéressant, vous pourriez être intéressé par les ressources suivantes, qui traitent d'autres techniques d'atténuation et de suppression des erreurs non abordées dans ce tutoriel :\n",
        "\n",
        "  * [Annulation probabiliste des erreurs (PEC)](/docs/guides/error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec)\n",
        "  * [Réduction](https://qiskit.github.io/qiskit-addon-mthree/) [des erreurs](/docs/tutorials/readout-error-mitigation-sampler/) de mesure sans matrice ( M3 ) (voir également le tutoriel « Réduction des erreurs de lecture pour la primitive Sampler à l'aide de M3 »)\n",
        "  * [Cônes de lumière ombrés](https://qiskit.github.io/qiskit-addon-slc/)\n",
        "  * [Absorption acoustique propagée (PNA)](https://qiskit.github.io/qiskit-addon-pna/)\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": 420
  },
  "nbformat": 4,
  "nbformat_minor": 4
}