{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "f69d5853-e815-4754-894d-833017217572",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Benchmarking en temps réel pour la sélection des qubits\"\n",
        "description: \"Effectuez des expériences de caractérisation en temps réel et mettez à jour les propriétés du backend afin d'améliorer la sélection des qubits lors du mappage d'un circuit vers les qubits physiques sur un QPU.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore sharex */}\n",
        "\n",
        "<span id=\"real-time-benchmarking-for-qubit-selection\" />\n",
        "\n",
        "# Benchmarking en temps réel pour la sélection des qubits\n",
        "\n",
        "*Estimation d'utilisation : 4 minutes sur un processeur Eagle r2 (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "500dc8c9-a5d8-4ef1-932f-30e400d6bdde",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Ce tutoriel montre comment réaliser des expériences de caractérisation en temps réel et mettre à jour les propriétés du backend afin d'améliorer la sélection des qubits lors de la mise en correspondance d'un circuit avec les qubits physiques d'une QPU. Vous apprendrez les expériences de caractérisation de base qui sont utilisées pour déterminer les propriétés de la QPU, comment les réaliser dans Qiskit, et comment mettre à jour les propriétés enregistrées dans l'objet backend représentant la QPU sur la base de ces expériences.\n",
        "\n",
        "Les propriétés rapportées par la QPU sont mises à jour une fois par jour, mais le système peut dériver plus rapidement que le temps entre les mises à jour. Cela peut affecter la fiabilité des routines de sélection des qubits dans l'étape `Layout` du gestionnaire de passes, car elles utiliseraient des propriétés rapportées qui ne représentent pas l'état actuel de la QPU. C'est pourquoi il peut être intéressant de consacrer du temps de QPU à des expériences de caractérisation, qui peuvent ensuite être utilisées pour mettre à jour les propriétés de QPU utilisées par la routine `Layout` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0babd413-d91f-4fd7-a0f5-bb46ae0bbf5b",
      "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 v2.0 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",
        "* Qiskit Experiments v0.12 ou plus récent ( `pip install qiskit-experiments` )\n",
        "* Bibliothèque de graphes Rustworkx (`pip install rustworkx`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3df52d5f-806a-4846-849e-633706a96d0b",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "4766c18a-ba45-456b-8b78-6b6f1d214586",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.quantum_info import hellinger_fidelity\n",
        "from qiskit.transpiler import InstructionProperties\n",
        "\n",
        "\n",
        "from qiskit_experiments.library import (\n",
        "    T1,\n",
        "    T2Hahn,\n",
        "    LocalReadoutError,\n",
        "    StandardRB,\n",
        ")\n",
        "from qiskit_experiments.framework import BatchExperiment, ParallelExperiment\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Session\n",
        "\n",
        "from datetime import datetime\n",
        "from collections import defaultdict\n",
        "import numpy as np\n",
        "import rustworkx\n",
        "import matplotlib.pyplot as plt\n",
        "import copy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65d49ed2-0581-486e-9031-a08fa9bace92",
      "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",
        "Pour évaluer la différence de performance, nous considérons un circuit qui prépare un état de Bell à travers une chaîne linéaire de longueur variable. La fidélité de l'état de Bell aux extrémités de la chaîne est mesurée.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "64c25da9-a728-4ae4-a377-3078a1dc618d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "ideal_dist = {\"00\": 0.5, \"11\": 0.5}\n",
        "\n",
        "num_qubits_list = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 127]\n",
        "circuits = []\n",
        "for num_qubits in num_qubits_list:\n",
        "    circuit = QuantumCircuit(num_qubits, 2)\n",
        "    circuit.h(0)\n",
        "    for i in range(num_qubits - 1):\n",
        "        circuit.cx(i, i + 1)\n",
        "    circuit.barrier()\n",
        "    circuit.measure(0, 0)\n",
        "    circuit.measure(num_qubits - 1, 1)\n",
        "    circuits.append(circuit)\n",
        "\n",
        "circuits[-1].draw(output=\"mpl\", style=\"clifford\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "16948f21-a39b-4444-bf02-5f81331825c4",
      "metadata": {},
      "source": [
        "<span id=\"set-up-backend-and-coupling-map\" />\n",
        "\n",
        "### Configurer le backend et la carte de couplage\n",
        "\n",
        "Tout d'abord, sélectionnez un backend\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "f968acca-9131-4f5d-aa74-70befcdda4f5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# To run on hardware, select the backend with the fewest number of jobs in the queue\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")\n",
        "\n",
        "qubits = list(range(backend.num_qubits))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90b0ddab-0d35-4784-8575-fe0505699197",
      "metadata": {},
      "source": [
        "On obtient alors sa carte de couplage\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "86d84baa-bd5e-4faf-9582-4568ab4c4a4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)\n",
        "\n",
        "# Get unidirectional coupling map\n",
        "one_dir_coupling_map = coupling_graph.edge_list()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d48b5ba2-74ec-4484-9ba6-b0681ae32919",
      "metadata": {},
      "source": [
        "Afin d'évaluer simultanément le plus grand nombre possible de portes à deux qubits, nous séparons la carte de couplage en `layered_coupling_map`. Cet objet contient une liste de couches où chaque couche est une liste d'arêtes sur lesquelles des portes à deux qubits peuvent être exécutées en même temps. On parle également de coloration des bords de la carte de couplage.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "62b36ded-ab4e-414e-b146-ff522786a871",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Get layered coupling map\n",
        "edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)\n",
        "layered_coupling_map = defaultdict(list)\n",
        "for edge_idx, color in edge_coloring.items():\n",
        "    layered_coupling_map[color].append(\n",
        "        coupling_graph.get_edge_endpoints_by_index(edge_idx)\n",
        "    )\n",
        "layered_coupling_map = [\n",
        "    sorted(layered_coupling_map[i])\n",
        "    for i in sorted(layered_coupling_map.keys())\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "875117af-8a2c-4aea-92d9-ffeee7ff37d5",
      "metadata": {},
      "source": [
        "<span id=\"characterization-experiments\" />\n",
        "\n",
        "### Expériences de caractérisation\n",
        "\n",
        "Une série d'expériences est utilisée pour caractériser les principales propriétés des qubits dans une QPU. Il s'agit de $T_1$, $T_2$, de l'erreur de lecture et de l'erreur de porte à un qubit et à deux qubits. Nous résumerons brièvement ces propriétés et ferons référence aux expériences menées dans le paquet [`qiskit-experiments`](https://qiskit-community.github.io/qiskit-experiments/index.html) qui sont utilisées pour les caractériser.\n",
        "\n",
        "<span id=\"t1\" />\n",
        "\n",
        "#### T1\n",
        "\n",
        "$T_1$ est le temps caractéristique nécessaire pour qu'un qubit excité retombe à l'état fondamental en raison des processus de décohérence à amortissement d'amplitude. Dans une [expérience $T_1$](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t1.html), nous mesurons un qubit excité après un délai. Plus le temps de retard est important, plus le qubit est susceptible de tomber à l'état fondamental plus le qubit est susceptible de tomber à l'état fondamental. Le but de l'expérience est de caractériser le taux de décroissance du qubit vers l'état fondamental l'état fondamental.\n",
        "\n",
        "<span id=\"t2\" />\n",
        "\n",
        "#### T2\n",
        "\n",
        "$T_2$ représente le temps nécessaire pour que la projection du vecteur de Bloch d'un seul qubit d'un qubit sur le plan XY tombe à environ 37 % ( $\\frac{1}{e}$ ) de son amplitude initiale en raison des processus de décohérence par déphasage de son amplitude initiale en raison des processus de décohérence par déphasage. Dans une [expérience d'écho de Hahn ( $T_2$ )](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t2hahn.html), nous pouvons estimer le taux de cette désintégration.\n",
        "\n",
        "<span id=\"state-preparation-and-measurement-spam-error-characterization\" />\n",
        "\n",
        "#### Caractérisation des erreurs de préparation et de mesure d'état (SPAM)\n",
        "\n",
        "Dans une [expérience de caractérisation des erreurs SPAM](https://qiskit-community.github.io/qiskit-experiments/manuals/measurement/readout_mitigation.html), les qubits sont préparés dans un certain état ( $\\vert 0 \\rangle$ ou $\\vert 1 \\rangle$ ) et mesurés. La probabilité de mesurer un état différent de celui préparé donne alors la probabilité de l'erreur.\n",
        "\n",
        "<span id=\"single-qubit-and-two-qubit-randomized-benchmarking\" />\n",
        "\n",
        "#### Benchmarking aléatoire à un et deux qubits\n",
        "\n",
        "Le [benchmarking aléatoire (RB)](https://qiskit-community.github.io/qiskit-experiments/manuals/verification/randomized_benchmarking.html) est un protocole populaire pour caractériser le taux d'erreur des processeurs quantiques processeurs quantiques. Une expérience RB consiste à générer des circuits aléatoires de Clifford sur les qubits donnés de telle sorte que l'unité calculée par les circuits soit l'identité identité. Après l'exécution des circuits, le nombre de tirs aboutissant à une erreur (c'est-à-dire à une sortie différente de l'état fondamental) est compté et, à partir de ces données, on peut déduire des estimations d'erreur pour le dispositif quantique, en calculant l'erreur par Clifford.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "9d57c42d-7a91-4e79-bc6c-6e579da929f8",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create T1 experiments on all qubit in parallel\n",
        "t1_exp = ParallelExperiment(\n",
        "    [\n",
        "        T1(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create T2-Hahn experiments on all qubit in parallel\n",
        "t2_exp = ParallelExperiment(\n",
        "    [\n",
        "        T2Hahn(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create readout experiments on all qubit in parallel\n",
        "readout_exp = LocalReadoutError(qubits)\n",
        "\n",
        "# Create single-qubit RB experiments on all qubit in parallel\n",
        "singleq_rb_exp = ParallelExperiment(\n",
        "    [\n",
        "        StandardRB(\n",
        "            physical_qubits=[qubit], lengths=[10, 100, 500], num_samples=10\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create two-qubit RB experiments on the three layers of disjoint edges of the heavy-hex\n",
        "twoq_rb_exp_batched = BatchExperiment(\n",
        "    [\n",
        "        ParallelExperiment(\n",
        "            [\n",
        "                StandardRB(\n",
        "                    physical_qubits=pair,\n",
        "                    lengths=[10, 50, 100],\n",
        "                    num_samples=10,\n",
        "                )\n",
        "                for pair in layer\n",
        "            ],\n",
        "            backend,\n",
        "            analysis=None,\n",
        "        )\n",
        "        for layer in layered_coupling_map\n",
        "    ],\n",
        "    backend,\n",
        "    flatten_results=True,\n",
        "    analysis=None,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cad4f8d3-c2d5-4bb5-92be-432a4573e14a",
      "metadata": {},
      "source": [
        "<span id=\"qpu-properties-over-time\" />\n",
        "\n",
        "### Propriétés du QPU au fil du temps\n",
        "\n",
        "En examinant les propriétés QPU rapportées au fil du temps (nous considérerons une seule semaine ci-dessous), nous constatons qu'elles peuvent fluctuer à l'échelle d'une seule journée. De petites fluctuations peuvent se produire même en l'espace d'une journée. Dans ce cas, les propriétés signalées (mises à jour une fois par jour) ne reflètent pas avec précision l'état actuel de la QPU. En outre, si un travail est transposé localement (en utilisant les propriétés actuelles rapportées) et soumis mais exécuté seulement à un moment ultérieur (quelques minutes ou quelques jours), il peut courir le risque d'avoir utilisé des propriétés obsolètes pour la sélection des qubits lors de l'étape de transposition. Cela souligne l'importance de disposer d'informations actualisées sur la QPU au moment de l'exécution. Tout d'abord, récupérons les propriétés sur une certaine période de temps.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "af1b6722-e77b-436a-bbcd-f272b95bb28c",
      "metadata": {},
      "outputs": [],
      "source": [
        "instruction_2q_name = \"cz\"  # set the name of the default 2q of the device\n",
        "errors_list = []\n",
        "for day_idx in range(10, 17):\n",
        "    calibrations_time = datetime(\n",
        "        year=2025, month=8, day=day_idx, hour=0, minute=0, second=0\n",
        "    )\n",
        "    targer_hist = backend.target_history(datetime=calibrations_time)\n",
        "\n",
        "    t1_dict, t2_dict = {}, {}\n",
        "    for qubit in range(targer_hist.num_qubits):\n",
        "        t1_dict[qubit] = targer_hist.qubit_properties[qubit].t1\n",
        "        t2_dict[qubit] = targer_hist.qubit_properties[qubit].t2\n",
        "\n",
        "    errors_dict = {\n",
        "        \"1q\": targer_hist[\"sx\"],\n",
        "        \"2q\": targer_hist[f\"{instruction_2q_name}\"],\n",
        "        \"spam\": targer_hist[\"measure\"],\n",
        "        \"t1\": t1_dict,\n",
        "        \"t2\": t2_dict,\n",
        "    }\n",
        "\n",
        "    errors_list.append(errors_dict)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac75df1b-f689-475c-94a2-d70d85b1f8ca",
      "metadata": {},
      "source": [
        "Traçons ensuite les valeurs\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e0ba509d-e0e0-438b-aedf-5e01919c7d4f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/e0ba509d-e0e0-438b-aedf-5e01919c7d4f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axs = plt.subplots(5, 1, figsize=(10, 20), sharex=False)\n",
        "\n",
        "\n",
        "# Plot for T1 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t1s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t1_dict = errors_dict[\"t1\"]\n",
        "        try:\n",
        "            t1s.append(t1_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t1 data for qubit {qubit}\")\n",
        "\n",
        "    axs[0].plot(t1s)\n",
        "\n",
        "axs[0].set_title(\"T1\")\n",
        "axs[0].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[0].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot for T2 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t2s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t2_dict = errors_dict[\"t2\"]\n",
        "        try:\n",
        "            t2s.append(t2_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t2 data for qubit {qubit}\")\n",
        "\n",
        "    axs[1].plot(t2s)\n",
        "\n",
        "axs[1].set_title(\"T2\")\n",
        "axs[1].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[1].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot SPAM values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    spams = []\n",
        "    for errors_dict in errors_list:\n",
        "        spam_dict = errors_dict[\"spam\"]\n",
        "        spams.append(spam_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[2].plot(spams)\n",
        "\n",
        "axs[2].set_title(\"SPAM Errors\")\n",
        "axs[2].set_ylabel(\"Error Rate\")\n",
        "axs[2].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 1Q Gate Errors\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    oneq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        oneq_gate_dict = errors_dict[\"1q\"]\n",
        "        oneq_gates.append(oneq_gate_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[3].plot(oneq_gates)\n",
        "\n",
        "axs[3].set_title(\"1Q Gate Errors\")\n",
        "axs[3].set_ylabel(\"Error Rate\")\n",
        "axs[3].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 2Q Gate Errors\n",
        "for pair in one_dir_coupling_map:\n",
        "    twoq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        twoq_gate_dict = errors_dict[\"2q\"]\n",
        "        twoq_gates.append(twoq_gate_dict[pair].error)\n",
        "\n",
        "    axs[4].plot(twoq_gates)\n",
        "\n",
        "axs[4].set_title(\"2Q Gate Errors\")\n",
        "axs[4].set_ylabel(\"Error Rate\")\n",
        "axs[4].set_xlabel(\"Days\")\n",
        "\n",
        "plt.subplots_adjust(hspace=0.5)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3343934-9273-457c-900b-2004b3aa9d0c",
      "metadata": {},
      "source": [
        "Vous pouvez constater que sur plusieurs jours, certaines des propriétés du qubit peuvent changer considérablement. Cela souligne l'importance de disposer d'informations récentes sur l'état de la QPU, afin de pouvoir sélectionner les qubits les plus performants pour une expérience.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e53f0a5-7713-4915-ae47-c1aa0a4f17cd",
      "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",
        "Aucune optimisation des circuits ou des opérateurs n'est effectuée dans ce tutoriel.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "862789c2-d17e-43ed-8e1b-2f37d7628ed2",
      "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": "d88f925f-3ffd-43de-ac1f-2c5ba3b8cd96",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-default-qubit-selection\" />\n",
        "\n",
        "### Exécuter un circuit quantique avec sélection de qubits par défaut\n",
        "\n",
        "Comme résultat de référence de la performance, nous exécuterons un circuit quantique sur une QPU en utilisant les qubits par défaut, qui sont les qubits sélectionnés avec les propriétés de backend demandées. Nous utiliserons `optimization_level = 3`.  Ce paramètre inclut l'optimisation de transpilation la plus avancée et utilise les propriétés de la cible (comme les erreurs d'opération) pour sélectionner les qubits les plus performants pour l'exécution.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5c0d09ad-2e6a-4067-8034-8df92a475ff1",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = generate_preset_pass_manager(target=backend.target, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits)\n",
        "initial_qubits = [\n",
        "    [\n",
        "        idx\n",
        "        for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "        if qb._register.name != \"ancilla\"\n",
        "    ]\n",
        "    for circuit in isa_circuits\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6d4fb04c-a5cf-48e8-a759-dde64fd751ac",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-real-time-qubit-selection\" />\n",
        "\n",
        "### Exécuter un circuit quantique avec sélection de qubits en temps réel\n",
        "\n",
        "Dans cette section, nous étudierons l'importance de disposer d'informations actualisées sur les propriétés des qubits de la QPU pour obtenir des résultats optimaux. Tout d'abord, nous réaliserons une série complète d'expériences de caractérisation de QPU ( $T_1$, $T_2$, SPAM, RB à un qubit et RB à deux qubits), que nous pourrons ensuite utiliser pour mettre à jour les propriétés de l'arrière-plan. Cela permet au gestionnaire de passes de sélectionner des qubits pour l'exécution sur la base d'informations récentes concernant la QPU, ce qui peut améliorer les performances d'exécution. Ensuite, nous exécutons le circuit de la paire de Bell et nous comparons la fidélité obtenue après avoir sélectionné les qubits avec les propriétés actualisées de la QPU à la fidélité obtenue auparavant lorsque nous utilisons les propriétés rapportées par défaut pour la sélection des qubits.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  Notez que certaines expériences de caractérisation peuvent échouer lorsque la routine d'ajustement ne peut pas ajuster une courbe aux données mesurées. Si vous voyez des avertissements provenant de ces expériences, inspectez-les pour comprendre quelle caractérisation a échoué sur quels qubits, et essayez d'ajuster les paramètres de l'expérience (comme les temps pour $T_1$, $T_2$, ou le nombre de longueurs des expériences RB).\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "a8266467-9d60-411f-89dd-8cad6558f588",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Prepare characterization experiments\n",
        "batches = [t1_exp, t2_exp, readout_exp, singleq_rb_exp, twoq_rb_exp_batched]\n",
        "batches_exp = BatchExperiment(batches, backend)  # , analysis=None)\n",
        "run_options = {\"shots\": 1e3, \"dynamic\": False}\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    sampler = SamplerV2(mode=session)\n",
        "\n",
        "    # Run characterization experiments\n",
        "    batches_exp_data = batches_exp.run(\n",
        "        sampler=sampler, **run_options\n",
        "    ).block_for_results()\n",
        "\n",
        "    EPG_sx_result_list = batches_exp_data.analysis_results(\"EPG_sx\")\n",
        "    EPG_sx_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_sx_result_list\n",
        "    ]\n",
        "    EPG_x_result_list = batches_exp_data.analysis_results(\"EPG_x\")\n",
        "    EPG_x_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_x_result_list\n",
        "    ]\n",
        "    T1_result_list = batches_exp_data.analysis_results(\"T1\")\n",
        "    T1_result_q_indices = [\n",
        "        result.device_components.index for result in T1_result_list\n",
        "    ]\n",
        "\n",
        "    T2_result_list = batches_exp_data.analysis_results(\"T2\")\n",
        "    T2_result_q_indices = [\n",
        "        result.device_components.index for result in T2_result_list\n",
        "    ]\n",
        "\n",
        "    Readout_result_list = batches_exp_data.analysis_results(\n",
        "        \"Local Readout Mitigator\"\n",
        "    )\n",
        "\n",
        "    EPG_2q_result_list = batches_exp_data.analysis_results(\n",
        "        f\"EPG_{instruction_2q_name}\"\n",
        "    )\n",
        "\n",
        "    # Update target properties\n",
        "    target = copy.deepcopy(backend.target)\n",
        "    for i in range(target.num_qubits - 1):\n",
        "        qarg = (i,)\n",
        "\n",
        "        if qarg in EPG_sx_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"sx\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_sx_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        if qarg in EPG_x_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"x\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_x_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "        err_mat = Readout_result_list.value.assignment_matrix(i)\n",
        "        readout_assignment_error = (\n",
        "            err_mat[0, 1] + err_mat[1, 0]\n",
        "        ) / 2  # average readout error\n",
        "        target.update_instruction_properties(\n",
        "            instruction=\"measure\",\n",
        "            qargs=qarg,\n",
        "            properties=InstructionProperties(error=readout_assignment_error),\n",
        "        )\n",
        "\n",
        "        if qarg in T1_result_q_indices:\n",
        "            target.qubit_properties[i].t1 = T1_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "        if qarg in T2_result_q_indices:\n",
        "            target.qubit_properties[i].t2 = T2_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "\n",
        "    for pair_idx, pair in enumerate(one_dir_coupling_map):\n",
        "        qarg = tuple(pair)\n",
        "        try:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        except:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg[::-1],\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "    # transpile circuits to updated target\n",
        "    pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "    isa_circuit_updated = pm.run(circuits)\n",
        "    updated_qubits = [\n",
        "        [\n",
        "            idx\n",
        "            for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "            if qb._register.name != \"ancilla\"\n",
        "        ]\n",
        "        for circuit in isa_circuit_updated\n",
        "    ]\n",
        "\n",
        "    n_trials = 3  # run multiple trials to see variations\n",
        "\n",
        "    # interleave circuits\n",
        "    interleaved_circuits = []\n",
        "    for original_circuit, updated_circuit in zip(\n",
        "        isa_circuits, isa_circuit_updated\n",
        "    ):\n",
        "        interleaved_circuits.append(original_circuit)\n",
        "        interleaved_circuits.append(updated_circuit)\n",
        "\n",
        "    # Run circuits\n",
        "    # Set simple error suppression/mitigation options\n",
        "    sampler.options.dynamical_decoupling.enable = True\n",
        "    sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "\n",
        "    job_interleaved = sampler.run(interleaved_circuits * n_trials)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d72e021a-3ea6-4ed2-829f-e59ce8017ef4",
      "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, comparons la fidélité de l'état de Bell obtenu dans les deux contextes différents :\n",
        "\n",
        "* `original`c'est-à-dire avec les qubits par défaut choisis par le transpileur sur la base des propriétés déclarées du backend.\n",
        "* `updated`c'est-à-dire avec les qubits choisis sur la base des propriétés mises à jour du backend après les expériences de caractérisation.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "5a19a5d1-5daf-4d65-9785-8f8724853821",
      "metadata": {},
      "outputs": [],
      "source": [
        "results = job_interleaved.result()\n",
        "all_fidelity_list, all_fidelity_updated_list = [], []\n",
        "for exp_idx in range(n_trials):\n",
        "    fidelity_list, fidelity_updated_list = [], []\n",
        "\n",
        "    for idx, num_qubits in enumerate(num_qubits_list):\n",
        "        pub_result_original = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx\n",
        "        ]\n",
        "        pub_result_updated = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx + 1\n",
        "        ]\n",
        "\n",
        "        fid = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_original.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_list.append(fid)\n",
        "\n",
        "        fid_up = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_updated.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_updated_list.append(fid_up)\n",
        "    all_fidelity_list.append(fidelity_list)\n",
        "    all_fidelity_updated_list.append(fidelity_updated_list)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "656ec97a-3fd9-4635-9a98-1c5589761689",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/656ec97a-3fd9-4635-9a98-1c5589761689-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(8, 6))\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"original\",\n",
        "    color=\"b\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_list, '-.')\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_updated_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_updated_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"updated\",\n",
        "    color=\"r\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_updated_list, '-.')\n",
        "plt.xlabel(\"Chain length\")\n",
        "plt.xticks(num_qubits_list)\n",
        "plt.ylabel(\"Fidelity\")\n",
        "plt.title(\"Bell pair fidelity at the edge of N-qubits chain\")\n",
        "plt.legend()\n",
        "plt.grid(\n",
        "    alpha=0.2,\n",
        "    linestyle=\"-.\",\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3705d85c-e11d-4a8f-bfae-20bd501124d8",
      "metadata": {},
      "source": [
        "La caractérisation en temps réel n'améliorera pas les performances de tous les essais. En outre, avec l'augmentation de la longueur de la chaîne, et donc la réduction de la liberté de choix des qubits physiques, l'importance de la mise à jour des informations sur le dispositif devient moins importante. Cependant, il est bon de collecter des données récentes sur les propriétés de l'appareil afin de comprendre ses performances. Occasionnellement, des systèmes transitoires à deux niveaux peuvent affecter les performances de certains qubits. Les données en temps réel peuvent nous informer lorsque de tels événements se produisent et nous aider à éviter les échecs expérimentaux dans de tels cas.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cd81b208-b13b-4988-854e-1741408f36f3",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"Passez à l'action :\">\n",
        "  Essayez d'appliquer cette méthode à vos exécutions et déterminez le bénéfice que vous en tirez! Vous pouvez également essayer de voir quelles améliorations vous obtenez avec différents backends.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b94755c1-1c19-434a-96b9-b83922b5d63c",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Enquête tutorielle\n",
        "\n",
        "Veuillez répondre à cette courte enquête pour nous faire part de vos commentaires sur ce didacticiel. Vos commentaires nous aideront à améliorer nos offres de contenu et l'expérience des utilisateurs.\n",
        "\n",
        "[Lien vers l'enquête](https://your.feedback.ibm.com/jfe/form/SV_0w6FZ9QrWkKfTQq)\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"
    },
    "vscode": {
      "interpreter": {
        "hash": "72460b19b35c51a2148375679c8e282bc3cd3c5550c209f0a38b7d09cded82d9"
      }
    },
    "hours": 2.5,
    "qpuSeconds": 240
  },
  "nbformat": 4,
  "nbformat_minor": 4
}