{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "70810e0b-08f9-48ae-beb5-8c863819000b",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Atténuation des erreurs de lecture pour la primitive Sampler à l'aide d' M3\"\n",
        "description: \"Utilisez l'add-on d'atténuation de lecture « M3 » avec la primitive Sampler\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore braket, Zgrm, newcommand, probs, quasis, topten */}\n",
        "\n",
        "<span id=\"readout-error-mitigation-for-the-sampler-primitive-using-m3\" />\n",
        "\n",
        "# Atténuation des erreurs de lecture pour la primitive Sampler à l'aide d' M3\n",
        "\n",
        "*Estimation de l'utilisation : moins d'une minute sur un processeur Heron r2 (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)*\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Contrairement à la primitive Estimateur, la primitive Échantillonneur ne dispose pas d'un support intégré pour l'atténuation des erreurs.\n",
        "Plusieurs des méthodes utilisées par l'estimateur sont spécifiquement conçues pour les valeurs d'espérance et ne sont donc pas applicables à l'échantillonneur primitif. L'atténuation des erreurs de lecture constitue une exception : il s'agit d'une méthode très efficace qui s'applique également à l'échantillonneur primitif.\n",
        "\n",
        "Le [module complémentaire Qiskit de M3](https://qiskit.github.io/qiskit-addon-mthree/) met en œuvre une méthode efficace de réduction des erreurs de lecture. Ce tutoriel explique comment utiliser l'addon Qiskit de M3 pour réduire les erreurs de lecture de la primitive Sampler.\n",
        "\n",
        "<span id=\"what-is-readout-error\" />\n",
        "\n",
        "### Qu'est-ce qu'une erreur de lecture?\n",
        "\n",
        "Immédiatement avant la mesure, l'état d'un registre de qubits est décrit par une superposition d'états de base de calcul décrit par une superposition d'états de base de calcul, ou par une matrice de densité.\n",
        "La mesure du registre de qubits en un registre de bits classique se fait ensuite en deux étapes.\n",
        "La mesure quantique proprement dite est d'abord effectuée.\n",
        "Cela signifie que l'état du registre de qubits est projeté sur un état de base unique caractérisé par une chaîne de par une chaîne de $1$ s et $0$ s.\n",
        "La deuxième étape consiste à lire la chaîne de bits caractérisant cet état de base et à l'écrire dans la mémoire classique de l'ordinateur.\n",
        "Nous appelons cette étape \" *lecture* \".\n",
        "Il s'avère que la deuxième étape (lecture) comporte plus d'erreurs que la première étape (projection sur les états de base).\n",
        "Cela est logique si l'on se souvient que la lecture nécessite la détection d'un état quantique microscopique et son amplification dans le domaine macroscopique microscopique et de l'amplifier dans le domaine macroscopique. Un résonateur de lecture est couplé au qubit (transmon) le qubit (transmon), subissant ainsi un très faible décalage de fréquence. Une impulsion de micro-ondes est ensuite rebondie sur le résonateur, qui subit à son tour de légères modifications de ses caractéristiques caractéristiques.  L'impulsion réfléchie est ensuite amplifiée et analysée.  Il s'agit d'un processus il s'agit d'un processus délicat et sujet à de nombreuses erreurs.\n",
        "\n",
        "Le point important est que, bien que la mesure quantique et la lecture soient toutes deux sujettes à l'erreur, c'est la dernière qui subit l'erreur dominante, appelée erreur de lecture cette dernière subit l'erreur dominante, appelée erreur de lecture, qui fait l'objet de ce tutoriel.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba74fc81-30ac-4365-9ba6-a891ba082bd6",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "<span id=\"theoretical-background\" />\n",
        "\n",
        "### Contexte théorique\n",
        "\n",
        "Si la chaîne de bits échantillonnée (stockée dans la mémoire classique) diffère de la chaîne de bits caractérisant l'état quantique projeté, nous disons qu'une erreur de lecture s'est produite l'état quantique projeté, nous disons qu'une erreur de lecture s'est produite.\n",
        "On observe que ces erreurs sont aléatoires et non corrélées d'un échantillon à l'autre.\n",
        "Il s'est avéré utile de modéliser l'erreur de lecture comme un *canal classique bruyant*.\n",
        "En d'autres termes, pour chaque paire de chaînes de bits chaînes de bits $i$ et $j$, il existe une probabilité fixe qu'une valeur réelle de $j$ soit lue à tort comme soit incorrectement lue comme $i$.\n",
        "\n",
        "Plus précisément, pour chaque paire de chaînes de bits $(i, j)$, il existe une probabilité (conditionnelle) ${M}_{i,j}$ que $i$ soit lu, étant donné que la vraie valeur est $j.$ C'est-à-dire,\n",
        "\n",
        "$$\n",
        "    {M}_{i,j} =  \\Pr(\\text{readout value is } i | \\text{true value is } j)\n",
        "    \\text{ for } i,j \\in (0,...,2^n - 1), \\tag{1}\n",
        "$$\n",
        "\n",
        "où $n$ est le nombre de bits dans le registre de lecture.\n",
        "Pour être concret, nous supposons que $i$ est un nombre entier décimal dont la représentation binaire est la chaîne de bits qui désigne les états de la base de calcul.\n",
        "Nous appelons la matrice $2^n \\times 2^n$ ${M}$ la *matrice d'affectation*.\n",
        "Pour une valeur réelle fixe $j$, la somme de la probabilité sur tous les résultats bruyants $i$ doit donner $1$. C'est-à-dire\n",
        "\n",
        "$$\n",
        "    \\sum_{i=0}^{2^n - 1} {M}_{i,j} = 1 \\text{ for all } j\n",
        "$$\n",
        "\n",
        "Une matrice sans entrées négatives qui satisfait à (1) est appelée *stochastique gauche*.\n",
        "Une matrice stochastique à gauche est également appelée *stochastique à colonnes* car chacune de ses colonnes s'additionne à $1$. Nous déterminons expérimentalement des valeurs approximatives pour chaque élément ${M}_{i,j}$ en préparant de manière répétée chaque état de base $|j \\rangle$ et en calculant les fréquences d'apparition des chaînes de bits échantillonnées de l'occurrence des chaînes de bits échantillonnées.\n",
        "\n",
        "Si une expérience consiste à estimer une distribution de probabilité sur les chaînes de bits de sortie par échantillonnage répété, nous pouvons utiliser pour atténuer l'erreur de lecture au niveau de la distribution, nous pouvons utiliser ${M}$ pour atténuer l'erreur de lecture au niveau de la distribution.\n",
        "La première étape consiste à répéter plusieurs fois un circuit fixe intéressant, créer un histogramme de chaînes de bits échantillonnées.\n",
        "L'histogramme normalisé est la distribution de probabilité mesurée sur les chaînes de bits possibles les $2^n$ chaînes de bits possibles, que nous désignons par ${\\tilde{p}} \\in \\mathbb{R}^{2^n}$. La probabilité (estimée) ${{\\tilde{p}}}_i$ d'échantillonner une chaîne de bits $i$ est égale à la somme de toutes les chaînes binaires réelles $j$, chacune étant pondérée par la probabilité qu'elle soit confondue avec la probabilité qu'elle soit confondue avec $i$. Cet énoncé sous forme de matrice est le suivant\n",
        "\n",
        "$$\n",
        "    {\\tilde{p}} = {M} {\\vec{p}}, \\tag{2},\n",
        "$$\n",
        "\n",
        "où ${\\vec{p}}$ est la vraie distribution. En d'autres termes, l'erreur de lecture a pour effet de multiplier la distribution idéale sur les chaînes de bits ${\\vec{p}}$ par la matrice d'affectation ${M}$ pour pour produire la distribution observée ${\\tilde{p}}$. Nous avons mesuré ${\\tilde{p}}$ et ${M}$, mais nous n'avons pas d'accès direct à ${\\vec{p}}$. En principe, nous obtiendrons la véritable distribution des chaînes de bits pour notre circuit obtenir la véritable distribution des chaînes de bits pour notre circuit en résolvant numériquement l'équation (2) pour ${\\vec{p}}$.\n",
        "\n",
        "Avant de poursuivre, il convient de noter quelques caractéristiques importantes de cette approche naïve.\n",
        "\n",
        "* Dans la pratique, l'équation (2) n'est pas résolue en inversant ${M}$. Les routines d'algèbre linéaire dans les bibliothèques de logiciels utilisent des méthodes plus stables, plus précises et plus efficaces.\n",
        "* Lors de l'estimation de ${M}$, nous avons supposé que seules des erreurs de lecture se produisaient. En particulier, nous supposons qu'il n'y a pas eu d'erreurs de préparation de l'état et de mesure quantique - ou du moins qu'elles ont été atténuées ou du moins qu'elles ont été atténuées.\n",
        "  Dans la mesure où il s'agit d'une bonne hypothèse, ${M}$ ne représente en réalité qu'une erreur de lecture seulement une erreur de lecture. Mais lorsque nous *utilisons* ${M}$ pour corriger une distribution mesurée sur des chaînes de bits, nous ne faisons pas cette hypothèse. En fait, nous nous attendons à ce qu'un circuit intéressant qu'un circuit intéressant introduise du bruit, par exemple des erreurs de porte. La \"vraie\" distribution comprend toujours les effets des erreurs qui ne sont pas atténuées d'une autre manière.\n",
        "\n",
        "Cette méthode, bien qu'utile dans certaines circonstances, souffre de quelques limitations.\n",
        "\n",
        "L'espace et le temps nécessaires à l'estimation de ${M}$ augmentent de façon exponentielle dans $n$ :\n",
        "\n",
        "* L'estimation de ${M}$ et ${\\tilde{p}}$ est sujette à des erreurs statistiques dues à un échantillonnage limité.\n",
        "  Ce bruit peut être rendu aussi faible que souhaité au prix d'un plus grand nombre de tirs (jusqu'à l'échelle de temps de la dérive des paramètres matériels qui entraînent des erreurs systématiques dans ${M}$ ). Toutefois, si aucune hypothèse n'est faite sur les chaînes de bits observées lors de l'exécution de l'atténuation, le nombre de tirs requis pour estimer ${M}$ croît au moins de manière exponentielle au moins de manière exponentielle dans $n$.\n",
        "* ${M}$ est une matrice $2^n \\times 2^n$.\n",
        "  Lorsque $n>10$, la quantité de mémoire nécessaire pour stocker ${M}$ est supérieure à la mémoire disponible dans un ordinateur portable puissant est supérieure à la mémoire disponible dans un ordinateur portable puissant.\n",
        "\n",
        "Les autres limitations sont les suivantes :\n",
        "\n",
        "* La distribution récupérée ${\\vec{p}}$ peut avoir une ou plusieurs probabilités négatives (tout en étant égale à un) ou plusieurs probabilités négatives (dont la somme est toujours égale à un). Une solution est de minimiser $||{M} {\\vec{p}} - {\\tilde{p}}||^2$ sous la contrainte que chaque entrée de ${\\vec{p}}$ soit non négative. Cependant, la durée d'exécution d'une telle méthode est beaucoup plus longue que la résolution directe de l'équation (2) est beaucoup plus longue que la résolution directe de l'équation (2).\n",
        "* Cette procédure d'atténuation fonctionne au niveau d'une distribution de probabilité sur les chaînes de bits. En particulier, il ne peut pas corriger une erreur dans une chaîne de bits individuelle observée individuelle observée.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c98d8409-66ad-452f-b87a-d8df886d77d4",
      "metadata": {},
      "source": [
        "<span id=\"qiskit-m3-addon-scaling-to-longer-bitstrings\" />\n",
        "\n",
        "### Module complémentaire Qiskit M3 : mise à l'échelle vers des chaînes de bits plus longues\n",
        "\n",
        "La résolution de l'équation (2) à l'aide de routines d'algèbre linéaire numérique standard est limitée à des chaînes de bits d'une longueur maximale d'environ 10 bits. M3 peut cependant traiter des chaînes de bits beaucoup plus longues. Les deux principales propriétés de M3 qui rendent cela possible sont les suivantes :\n",
        "\n",
        "* Les corrélations de l'erreur de lecture d'ordre trois et plus entre les collections de bits sont considérées comme négligeables et sont ignorées. En principe, au prix d'un plus grand nombre de tirs, on pourrait également estimer des corrélations plus élevées.\n",
        "* Plutôt que de construire explicitement ${M}$, nous utilisons une matrice effective beaucoup plus petite qui enregistre les probabilités uniquement pour les chaînes de bits collectées lors de la construction de probabilités uniquement pour les chaînes de bits collectées lors de la construction de ${\\tilde{p}}$.\n",
        "\n",
        "À un niveau élevé, la procédure fonctionne comme suit.\n",
        "\n",
        "Tout d'abord, nous construisons des blocs de construction à partir desquels nous pouvons construire une description simplifiée et efficace de ${M}$. Ensuite, nous exécutons de manière répétée le circuit qui nous intéresse et collectons des chaînes de bits que nous utilisons pour construire à la fois et, à l'aide des blocs de construction, une description efficace de ${\\tilde{p}}$ et, à l'aide des blocs de construction, une description efficace de ${M}$.\n",
        "\n",
        "Plus précisément,\n",
        "\n",
        "* Les matrices d'affectation d'un qubit unique sont estimées pour chaque qubit. Pour ce faire, nous préparons à plusieurs reprises préparons le registre de qubits dans l'état tout-zéro $|0 ... 0 \\rangle$ puis dans l'état tout-un $|1 ... 1 \\rangle$, et enregistrons la probabilité que chaque qubit soit lu de manière incorrecte incorrecte.\n",
        "* Les corrélations d'ordre trois et plus sont supposées négligeables et sont ignorées.\n",
        "\n",
        "  Au lieu de cela, nous construisons un certain nombre de $n$ de $2 \\times 2$ matrices d'affectation à un qubit et un certain nombre de $n(n-1)/2$ de $4 \\times 4$ matrices d'affectation à deux qubits deux qubits. Ces matrices d'affectation à un ou deux qubits sont stockées pour une utilisation ultérieure pour une utilisation ultérieure.\n",
        "* Après avoir échantillonné de façon répétée un circuit pour construire ${\\tilde{p}}$, nous construisons une approximation efficace de ${M}$ en utilisant uniquement des chaînes de bits échantillonnées lors de la construction de ${\\tilde{p}}$. Cette matrice effective est construite à l'aide des matrices à un et deux qubits décrites au point précédent.\n",
        "  La dimension linéaire de cette matrice est au plus de l'ordre du nombre de clichés utilisés dans la construction de, ce qui est beaucoup plus petit que le nombre de clichés utilisés dans la construction de de plans utilisés pour construire ${\\tilde{p}}$, ce qui est beaucoup plus petit que la dimension la dimension $2^n$ de la matrice d'affectation complète ${M}$.\n",
        "\n",
        "Pour plus de détails techniques sur M3, vous pouvez consulter [*Scalable Mitigation of Measurement Errors on Quantum Computers*](https://journals.aps.org/prxquantum/abstract/10.1103/PRXQuantum.2.040326).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "14f04ac4-5142-4436-93e1-f712d47dde1a",
      "metadata": {},
      "source": [
        "<span id=\"application-of-m3-to-a-quantum-algorithm\" />\n",
        "\n",
        "### Application de l'algorithme de l'échec de l'interférence quantique ( M3 ) à un algorithme quantique\n",
        "\n",
        "Nous appliquerons l'atténuation de la lecture de M3 au problème du décalage caché. Le problème du décalage caché et les problèmes étroitement liés tels que le [problème du sous-groupe caché](https://en.wikipedia.org/wiki/Hidden_subgroup_problem) ont été conçus à l'origine dans un cadre tolérant aux pannes (plus précisément, avant que les QPU tolérantes aux pannes ne s'avèrent possibles!) Mais ils sont également étudiés avec les processeurs disponibles. Un exemple d'accélération exponentielle algorithmique obtenue pour une variante du problème du décalage caché sur des QPUs IBM® de 127 qubits est présenté dans [cet article](https://journals.aps.org/prx/accepted/a9074K06A8e1590147da9c69f8c4b64c28247be5a) ( [version arXiv](https://arxiv.org/abs/2401.07934) ).\n",
        "\n",
        "Dans ce qui suit, toute l'arithmétique est booléenne.\n",
        "En d'autres termes, pour $a, b \\in \\mathbb{Z}_2 = \\{0, 1\\}$, l'addition, $a + b$ est la fonction logique XOR.\n",
        "En outre, la multiplication $a \\times b$ (ou $a b$ ) est la fonction logique ET. Pour $x, y \\in \\{0, 1\\}^n$, $x + y$ est défini par l'application bit à bit de XOR.\n",
        "Le produit de points $\\cdot: {\\mathbb{Z}_2^n} \\rightarrow \\mathbb{Z}_2$ est défini par $x \\cdot y = \\sum_i x_i y_i$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "81cb34b4-566b-4ee1-8309-7464a5fd3bb7",
      "metadata": {},
      "source": [
        "<span id=\"hadamard-operator-and-fourier-transform\" />\n",
        "\n",
        "#### Opérateur de Hadamard et transformée de Fourier\n",
        "\n",
        "Lors de la mise en œuvre d'algorithmes quantiques, il est très courant d'utiliser l'opérateur de Hadamard comme transformée de Fourier.\n",
        "Les états de base de calcul sont parfois appelés *états classiques*. Ils ont une relation biunivoque avec les chaînes de bits classiques une relation biunivoque avec les chaînes de bits classiques.\n",
        "L'opérateur de Hadamard $n$ -qubit sur les états classiques peut être considéré comme une transformée de Fourier sur l'hypercube booléen :\n",
        "\n",
        "$$\n",
        "H^{\\otimes n} =  \\frac{1}{\\sqrt{2^n}} \\sum_{x,y \\in {\\mathbb{Z}_2^n}} (-1)^{x \\cdot y} {|{y}\\rangle}{\\langle{x}|}.\n",
        "$$\n",
        "\n",
        "Considérons un état ${|{s}\\rangle}$ correspondant à une chaîne de bits fixe $s$. En appliquant $H^{\\otimes n}$, et en utilisant ${\\langle {x}|{s}\\rangle} = \\delta_{x,s}$, nous voyons que la transformée de Fourier de ${|{s}\\rangle}$ peut être écrite comme suit\n",
        "\n",
        "$$\n",
        "   H^{\\otimes n} {|{s}\\rangle} =  \\frac{1}{\\sqrt{2^n}} \\sum_{y \\in {\\mathbb{Z}_2^n}} (-1)^{s \\cdot y} {|{y}\\rangle}.\n",
        "$$\n",
        "\n",
        "Le Hadamard est son propre inverse, c'est-à-dire, $H^{\\otimes n} H^{\\otimes n} = (H H)^{\\otimes n} = I^{\\otimes n}$. Ainsi, la transformée de Fourier inverse est le même opérateur, $H^{\\otimes n}$. Explicitement, nous avons,\n",
        "\n",
        "$$\n",
        "  {|{s}\\rangle} =  H^{\\otimes n} H^{\\otimes n} {|{s}\\rangle}  =  H^{\\otimes n} \\frac{1}{\\sqrt{2^n}} \\sum_{y \\in {\\mathbb{Z}_2^n}} (-1)^{s \\cdot y} {|{y}\\rangle}.\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d10fce93-3e08-4a27-b641-eda5e7b05ce0",
      "metadata": {},
      "source": [
        "<span id=\"the-hidden-shift-problem\" />\n",
        "\n",
        "#### Le problème du changement caché\n",
        "\n",
        "Nous considérons un exemple simple de *problème de décalage caché*.\n",
        "Le problème consiste à identifier un changement constant dans l'entrée d'une fonction.\n",
        "La fonction que nous considérons est le produit de points. C'est le membre le plus simple d'une grande classe de fonctions qui admettent une accélération quantique pour le problème de la grâce à des techniques similaires à celles présentées ci-dessous.\n",
        "\n",
        "Soit $x,y \\in {\\mathbb{Z}_2^m}$ des chaînes de bits de longueur $m$. Nous définissons ${f}: {\\mathbb{Z}_2^m} \\times {\\mathbb{Z}_2^m} \\rightarrow \\{-1,1\\}$ par\n",
        "\n",
        "$$\n",
        "  {f}(x, y) = (-1)^{x \\cdot y}.\n",
        "$$\n",
        "\n",
        "Soit $a,b \\in {\\mathbb{Z}_2^m}$ des chaînes de bits fixes de longueur $m$. Nous définissons en outre $g: {\\mathbb{Z}_2^m} \\times {\\mathbb{Z}_2^m} \\rightarrow \\{-1,1\\}$ par\n",
        "\n",
        "$$\n",
        "  g(x, y) = {f}(x+a, y+b) = (-1)^{(x+a) \\cdot (y+b)},\n",
        "$$\n",
        "\n",
        "où $a$ et $b$ sont des paramètres (cachés).\n",
        "Nous disposons de deux boîtes noires, l'une implémentant $f$, et l'autre $g$. Nous supposons que nous savons qu'elles calculent les fonctions définies ci-dessus, sauf que nous ne connaissons ni $a$ ni $b$. Le jeu consiste à déterminer les chaînes de bits cachées (décalages) $a$ et $b$ en interrogeant $f$ et $g$. Il est clair que si nous jouons le jeu de manière classique, nous avons besoin des requêtes $O(2m)$ pour déterminer $a$ et $b$. Par exemple, nous pouvons interroger $g$ avec toutes les paires de chaînes de caractères telles qu'un élément de la paire est entièrement composé de zéros et que l'autre élément a exactement un élément défini sur $1$. À chaque interrogation, nous apprenons un élément de $a$ ou de $b$. Cependant, nous verrons que, si les boîtes noires sont implémentées comme des circuits quantiques, nous pouvons déterminer $a$ et $b$ en interrogeant une seule fois $f$ et $g$.\n",
        "\n",
        "Dans le contexte de la complexité algorithmique, une boîte noire est appelée *oracle*.\n",
        "En plus d'être opaque, un oracle a la propriété de consommer l'entrée et de produire la sortie instantanément produit la sortie instantanément, n'ajoutant rien au budget de complexité de l'algorithme dans lequel il est intégré dans lequel il est intégré. En fait, dans le cas présent, les oracles implémentant $f$ et $g$ seront considérés comme efficaces.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d5e4c1e2-d6f5-4093-afdb-f97e9f062179",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "<span id=\"quantum-circuits-for-$f$-and-$g$\" />\n",
        "\n",
        "#### Circuits quantiques pour l' $f$ et l' $g$\n",
        "\n",
        "Nous avons besoin des ingrédients suivants pour mettre en œuvre $f$ et $g$ en tant que circuits quantiques.\n",
        "\n",
        "Pour les états classiques à un qubit ${|{x_1}\\rangle}, {|{y_1}\\rangle}$, avec $x_1,y_1 \\in \\mathbb{Z}_2$, la porte $Z$ contrôlée ${CZ}$ peut être écrite comme suit\n",
        "\n",
        "$$\n",
        "{CZ} {|{x_1}\\rangle}{|{y_1}\\rangle}{x_1} = (-1)^{x_1 y_1} {|{x_1}\\rangle}{x_1}{|{y_1}\\rangle}.\n",
        "$$\n",
        "\n",
        "Nous opérerons avec des portes $m$ CZ, une sur $(x_1, y_1)$, et une sur $(x_2, y_2)$, et ainsi de suite, jusqu'à $(x_m, y_m)$. Nous appelons cet opérateur ${CZ}_{x,y}$.\n",
        "\n",
        "$U_f = {CZ}_{x,y}$ est une version quantique de ${f} = {f}(x,y)$ :\n",
        "\n",
        "$$\n",
        "%\\CZ_{x,y} {|#1\\rangle}{z} =\n",
        "U_f {|{x}\\rangle}{|{y}\\rangle} = {CZ}_{x,y} {|{x}\\rangle}{|{y}\\rangle} = (-1)^{x \\cdot y}  {|{x}\\rangle}{|{y}\\rangle}.\n",
        "$$\n",
        "\n",
        "Nous devons également mettre en œuvre un décalage de chaîne de bits.\n",
        "Nous désignons l'opérateur sur le registre $x$ $X^{a_1}\\cdots X^{a_m}$ par $X_a$ et de même sur le registre $y$ $X_b =  X^{b_1}\\cdots X^{b_m}$. Ces opérateurs appliquent $X$ partout où un seul bit est $1$, et l'identité $I$ partout où elle est $0$. Nous avons donc\n",
        "\n",
        "$$\n",
        " X_a X_b  {|{x}\\rangle}{|{y}\\rangle} = {|{x+a}\\rangle}{|{y+b}\\rangle}.\n",
        "$$\n",
        "\n",
        "La deuxième boîte noire $g$ est mise en œuvre par l'unité $U_g$, donnée par\n",
        "\n",
        "$$\n",
        "%U_g {|{x}\\rangle}{|{y}\\rangle} = X_aX_b \\CZ_{x,y} X_aX_b {|{x}\\rangle}{|{y}\\rangle}.\n",
        "U_g = X_aX_b {CZ}_{x,y} X_aX_b.\n",
        "$$\n",
        "\n",
        "Pour ce faire, nous appliquons les opérateurs de droite à gauche à l'état ${|{x}\\rangle}{|{y}\\rangle}$. D'abord\n",
        "\n",
        "$$\n",
        " X_a X_b  {|{x}\\rangle}{|{y}\\rangle} = {|{x+a}\\rangle}{|{y+b}\\rangle}.\n",
        "$$\n",
        "\n",
        "Ensuite,\n",
        "\n",
        "$$\n",
        "  {CZ}_{x,y}  {|{x+a}\\rangle}{|{y+b}\\rangle} = (-1)^{(x+a)\\cdot (y+b)} {|{x+a}\\rangle}{|{y+b}\\rangle}.\n",
        "$$\n",
        "\n",
        "Enfin,\n",
        "\n",
        "$$\n",
        "  X^a X^b (-1)^{(x+a)\\cdot (y+b)} {|{x+a}\\rangle}{|{y+b}\\rangle} = (-1)^{(x+a)\\cdot (y+b)} {|{x}\\rangle}{|{y}\\rangle},\n",
        "$$\n",
        "\n",
        "qui est en effet la version quantique de $f(x+a, y+b)$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "de0bb5f9-517e-4bea-91ab-f65e012e8ae9",
      "metadata": {
        "editable": true,
        "jp-MarkdownHeadingCollapsed": true,
        "slideshow": {
          "slide_type": ""
        },
        "tags": []
      },
      "source": [
        "<span id=\"the-hidden-shift-algorithm\" />\n",
        "\n",
        "#### L'algorithme de changement caché\n",
        "\n",
        "Nous allons maintenant assembler les pièces du puzzle pour résoudre le problème du décalage caché.\n",
        "Nous commençons par appliquer les Hadamards aux registres initialisés à l'état zéro.\n",
        "\n",
        "$$\n",
        "H^{\\otimes 2m} = H^{\\otimes m} \\otimes H^{\\otimes m} {{|{0}\\rangle}^{\\otimes m}}{{|{0}\\rangle}^{\\otimes m}} = \\frac{1}{\\sqrt{2^{2m}}} \\sum_{x, y \\in {\\mathbb{Z}_2^m}} (-1)^{x \\cdot y} {|{x}\\rangle}{|{y}\\rangle}.\n",
        "$$\n",
        "\n",
        "Ensuite, nous interrogeons l'oracle $g$ pour obtenir\n",
        "\n",
        "$$\n",
        "U_g H^{\\otimes 2m} {{|{0}\\rangle}^{\\otimes m}}{{|{0}\\rangle}^{\\otimes m}}\n",
        "= \\frac{1}{\\sqrt{2^{2m}}} \\sum_{x, y \\in {\\mathbb{Z}_2^m}} (-1)^{(x+a) \\cdot (y+b)} {|{x}\\rangle}{|{y}\\rangle}\n",
        "$$\n",
        "\n",
        "$$\n",
        "\\approx \\frac{1}{\\sqrt{2^{2m}}} \\sum_{x, y \\in {\\mathbb{Z}_2^m}} (-1)^{x \\cdot y + x \\cdot b + y \\cdot a} {|{x}\\rangle}{|{y}\\rangle}.\n",
        "$$\n",
        "\n",
        "Dans la dernière ligne, nous avons omis le facteur de phase global constant $(-1)^{a \\cdot b}$, et dénotons l'égalité jusqu'à une phase par $\\approx$. Ensuite, l'application de l'oracle $f$ introduit un autre facteur de $(-1)^{x \\cdot y}$, annulant celui déjà présent déjà présent. Nous avons alors :\n",
        "\n",
        "$$\n",
        "U_f U_g H^{\\otimes 2m} {{|{0}\\rangle}^{\\otimes m}}{{|{0}\\rangle}^{\\otimes m}}\n",
        "\\approx \\frac{1}{\\sqrt{2^{2m}}} \\sum_{x, y \\in {\\mathbb{Z}_2^m}} (-1)^{x \\cdot b + y \\cdot a} {|{x}\\rangle}{|{y}\\rangle}.\n",
        "$$\n",
        "\n",
        "L'étape finale consiste à appliquer la transformée de Fourier inverse, $H^{\\otimes 2m} = H^{\\otimes m} \\otimes H^{\\otimes m}$, ce qui donne\n",
        "\n",
        "$$\n",
        "H^{\\otimes 2m} U_f U_g  H^{\\otimes 2m} {{|{0}\\rangle}^{\\otimes m}}{{|{0}\\rangle}^{\\otimes m}}\n",
        "\\approx {|{b}\\rangle}{|{a}\\rangle}.\n",
        "$$\n",
        "\n",
        "Le circuit est terminé. En l'absence de bruit, l'échantillonnage des registres quantiques renvoie les chaînes de bits retournera les chaînes de bits $b, a$ avec la probabilité $1$.\n",
        "\n",
        "Le produit intérieur booléen est un exemple de ce que l'on appelle les fonctions coudées.\n",
        "Nous ne définirons pas ici les fonctions coudées mais nous nous contenterons de noter qu'elles \"sont maximalement résistantes aux attaques qui cherchent à exploiter une dépendance des sorties sur un sous-espace linéaire des entrées des sorties sur un sous-espace linéaire des entrées\"\n",
        "Cette citation est tirée de l'article [*Quantum algorithms for highly non-linear Boolean functions*](https://arxiv.org/abs/0811.3208), qui donne des algorithmes efficaces de décalage caché pour plusieurs classes de fonctions coudées.\n",
        "L'algorithme de ce tutoriel figure à la section 3.1 de l'article.\n",
        "\n",
        "Dans le cas plus général, le circuit permettant de trouver un décalage caché $s \\in \\mathbb{Z}^n$ est le suivant\n",
        "\n",
        "$$\n",
        " H^{\\otimes n} U_{\\tilde{f}}  H^{\\otimes n} U_g  H^{\\otimes n} {|{0}\\rangle}^{\\otimes n} = {|{s}\\rangle}.\n",
        "$$\n",
        "\n",
        "Dans le cas général, $f$ et $g$ sont des fonctions d'une seule variable.\n",
        "Notre exemple de produit intérieur a cette forme si nous laissons $f(x, y) \\to f(z)$, avec $z$ égal à la concaténation de $x$ et $y$, et $s$ égal à la concaténation de et de $a$ et $b$. Le cas général nécessite exactement deux oracles : Un oracle pour $g$ et un pour $\\tilde{f}$, où ce dernier est une fonction connue comme le *dual* de la fonction coudée $f$. La fonction produit intérieur possède la propriété d'auto-dualité $\\tilde{f}=f$.\n",
        "\n",
        "Dans notre circuit pour le décalage caché sur le produit intérieur, nous avons omis la couche intermédiaire de Hadamards qui apparaît dans le circuit pour le cas général de Hadamards qui apparaît dans le circuit pour le cas général. Bien que dans le cas général cette couche est nécessaire, nous avons gagné un peu de profondeur en l'omettant, au prix d'un peu de post-traitement, car le résultat est au lieu de de post-traitement car la sortie est ${|{b}\\rangle}{|{a}\\rangle}$ au lieu de ${|{a}\\rangle}{|{b}\\rangle}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ccc0aec5-63d3-4a43-8fad-fea56ea24ec7",
      "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.1 ou plus tard, avec prise en charge de [la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.41 ou plus tard (`pip install qiskit-ibm-runtime`)\n",
        "* M3 Qiskit addon v3.0 (`pip install mthree`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c090b8dd-754f-4390-93e5-663f818e61d0",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "233dfc67-d280-4827-910d-8e7170ee95ad",
      "metadata": {},
      "outputs": [],
      "source": [
        "from collections.abc import Iterator, Sequence\n",
        "from random import Random\n",
        "from qiskit.circuit import (\n",
        "    CircuitInstruction,\n",
        "    QuantumCircuit,\n",
        "    QuantumRegister,\n",
        "    Qubit,\n",
        ")\n",
        "from qiskit.circuit.library import CZGate, HGate, XGate\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "import timeit\n",
        "import matplotlib.pyplot as plt\n",
        "from qiskit_ibm_runtime import SamplerV2 as Sampler\n",
        "import mthree"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5cf89647-261d-4af1-af54-75548345df6e",
      "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": "940cc113-5cf3-4688-9747-93268be10088",
      "metadata": {},
      "source": [
        "Tout d'abord, nous écrivons les fonctions permettant de mettre en œuvre le problème du décalage caché sous la forme d'un site `QuantumCircuit`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "675fe72b-6a86-42ea-a65f-b29a60725ecb",
      "metadata": {
        "editable": true,
        "scrolled": true,
        "slideshow": {
          "slide_type": ""
        },
        "tags": []
      },
      "outputs": [],
      "source": [
        "def apply_hadamards(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:\n",
        "    \"\"\"Apply a Hadamard gate to every qubit.\"\"\"\n",
        "    for q in qubits:\n",
        "        yield CircuitInstruction(HGate(), [q], [])\n",
        "\n",
        "\n",
        "def apply_shift(\n",
        "    qubits: Sequence[Qubit], shift: int\n",
        ") -> Iterator[CircuitInstruction]:\n",
        "    \"\"\"Apply X gates where the bits of the shift are equal to 1.\"\"\"\n",
        "    for i, q in zip(range(shift.bit_length()), qubits):\n",
        "        if shift >> i & 1:\n",
        "            yield CircuitInstruction(XGate(), [q], [])\n",
        "\n",
        "\n",
        "def oracle_f(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:\n",
        "    \"\"\"Apply the f oracle.\"\"\"\n",
        "    for i in range(0, len(qubits) - 1, 2):\n",
        "        yield CircuitInstruction(CZGate(), [qubits[i], qubits[i + 1]])\n",
        "\n",
        "\n",
        "def oracle_g(\n",
        "    qubits: Sequence[Qubit], shift: int\n",
        ") -> Iterator[CircuitInstruction]:\n",
        "    \"\"\"Apply the g oracle.\"\"\"\n",
        "    yield from apply_shift(qubits, shift)\n",
        "    yield from oracle_f(qubits)\n",
        "    yield from apply_shift(qubits, shift)\n",
        "\n",
        "\n",
        "def determine_hidden_shift(\n",
        "    qubits: Sequence[Qubit], shift: int\n",
        ") -> Iterator[CircuitInstruction]:\n",
        "    \"\"\"Determine the hidden shift.\"\"\"\n",
        "    yield from apply_hadamards(qubits)\n",
        "    yield from oracle_g(qubits, shift)\n",
        "    # We omit this layer in exchange for post processing\n",
        "    # yield from apply_hadamards(qubits)\n",
        "    yield from oracle_f(qubits)\n",
        "    yield from apply_hadamards(qubits)\n",
        "\n",
        "\n",
        "def run_hidden_shift_circuit(n_qubits, rng):\n",
        "    hidden_shift = rng.getrandbits(n_qubits)\n",
        "\n",
        "    qubits = QuantumRegister(n_qubits, name=\"q\")\n",
        "    circuit = QuantumCircuit.from_instructions(\n",
        "        determine_hidden_shift(qubits, hidden_shift), qubits=qubits\n",
        "    )\n",
        "    circuit.measure_all()\n",
        "    # Format the hidden shift as a string.\n",
        "    hidden_shift_string = format(hidden_shift, f\"0{n_qubits}b\")\n",
        "    return (circuit, hidden_shift, hidden_shift_string)\n",
        "\n",
        "\n",
        "def display_circuit(circuit):\n",
        "    return circuit.remove_final_measurements(inplace=False).draw(\n",
        "        \"mpl\", idle_wires=False, scale=0.5, fold=-1\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6960aa96-f612-40b3-9f26-3d3afb07262b",
      "metadata": {},
      "source": [
        "Commençons par un petit exemple :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "8297843e-00c3-4bb5-9d33-a7e558d1698c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Hidden shift string 011010\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/readout-error-mitigation-sampler/extracted-outputs/8297843e-00c3-4bb5-9d33-a7e558d1698c-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "n_qubits = 6\n",
        "random_seed = 12345\n",
        "rng = Random(random_seed)\n",
        "circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(\n",
        "    n_qubits, rng\n",
        ")\n",
        "\n",
        "print(f\"Hidden shift string {hidden_shift_string}\")\n",
        "\n",
        "display_circuit(circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c136f784-e105-4244-a483-b8221577afc3",
      "metadata": {
        "editable": true,
        "slideshow": {
          "slide_type": ""
        },
        "tags": []
      },
      "source": [
        "<span id=\"step-2-optimize-circuits-for-quantum-hardware-execution\" />\n",
        "\n",
        "## Étape 2 : Optimiser les circuits pour l'exécution sur du matériel quantique\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "ee4d384a-15e2-4a3f-a52a-def3d184c4fa",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "['shift 011010', 'n_qubits 6', 'seed = 12345']"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "job_tags = [\n",
        "    f\"shift {hidden_shift_string}\",\n",
        "    f\"n_qubits {n_qubits}\",\n",
        "    f\"seed = {random_seed}\",\n",
        "]\n",
        "job_tags"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f2b77d93-c34a-43a4-b436-e7a25024a94a",
      "metadata": {
        "editable": true,
        "scrolled": true,
        "slideshow": {
          "slide_type": ""
        },
        "tags": []
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Using backend ibm_kingston\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/readout-error-mitigation-sampler/extracted-outputs/f2b77d93-c34a-43a4-b436-e7a25024a94a-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Uncomment this to run the circuits on a quantum computer on IBMCloud.\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=100\n",
        ")\n",
        "\n",
        "# from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2\n",
        "# backend = FakeMelbourneV2()\n",
        "# backend.refresh(service)\n",
        "\n",
        "print(f\"Using backend {backend.name}\")\n",
        "\n",
        "\n",
        "def get_isa_circuit(circuit, backend):\n",
        "    pass_manager = generate_preset_pass_manager(\n",
        "        optimization_level=3, backend=backend, seed_transpiler=1234\n",
        "    )\n",
        "    isa_circuit = pass_manager.run(circuit)\n",
        "    return isa_circuit\n",
        "\n",
        "\n",
        "isa_circuit = get_isa_circuit(circuit, backend)\n",
        "display_circuit(isa_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f2f426da-3601-4442-9593-16bb19abf91e",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-circuits-using-qiskit-primitives\" />\n",
        "\n",
        "## Étape 3 : Exécutez les circuits à l'aide d' Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6ebaea63-e352-4d7c-a7ee-57a2f0a66306",
      "metadata": {},
      "outputs": [],
      "source": [
        "# submit job for solving the hidden shift problem using the Sampler primitive\n",
        "NUM_SHOTS = 50_000\n",
        "\n",
        "\n",
        "def run_sampler(backend, isa_circuit, num_shots):\n",
        "    sampler = Sampler(mode=backend)\n",
        "    sampler.options.environment.job_tags\n",
        "    pubs = [(isa_circuit, None, NUM_SHOTS)]\n",
        "    job = sampler.run(pubs)\n",
        "    return job\n",
        "\n",
        "\n",
        "def setup_mthree_mitigation(isa_circuit, backend):\n",
        "    # retrieve the final qubit mapping so mthree knows which qubits to calibrate\n",
        "    qubit_mapping = mthree.utils.final_measurement_mapping(isa_circuit)\n",
        "\n",
        "    # submit jobs for readout error calibration\n",
        "    mit = mthree.M3Mitigation(backend)\n",
        "    mit.cals_from_system(qubit_mapping, rep_delay=None)\n",
        "\n",
        "    return mit, qubit_mapping"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "9b76b943-fd71-4287-804b-980cacc078f8",
      "metadata": {},
      "outputs": [],
      "source": [
        "job = run_sampler(backend, isa_circuit, NUM_SHOTS)\n",
        "mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "674ad6e1-c496-44ca-9598-470edf4c15dc",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-results-in-classical-format\" />\n",
        "\n",
        "## Étape 4 : Post-traitement et restitution des résultats dans un format classique\n",
        "\n",
        "Dans la discussion théorique ci-dessus, nous avons déterminé que pour l'entrée $ab$, nous attendons la sortie $ba$. Une complication supplémentaire est que, afin d'avoir un circuit plus simple (pré-transposé), nous avons inséré les portes CZ requises entre les paires de qubits voisines paires de qubits voisines. Cela revient à entrelacer les chaînes de bits $a$ et $b$ sous la forme $a1 b1 a2 b2 \\ldots$. La chaîne de sortie $ba$ sera entrelacée de la même manière : $b1 a1 b2 a2 \\ldots$. La fonction `unscramble` ci-dessous transforme la chaîne de sortie de $b1 a1 b2 a2 \\ldots$ en $a1 b1 a2 b2 \\ldots$ afin que les chaînes d'entrée et de sortie puissent être comparées directement.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "aada9b41-0600-4814-a5a3-917d7fb171f9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# retrieve bitstring counts\n",
        "def get_bitstring_counts(job):\n",
        "    result = job.result()\n",
        "    pub_result = result[0]\n",
        "    counts = pub_result.data.meas.get_counts()\n",
        "    return counts, pub_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "3930e290-89ff-404d-946b-cdf90119cb48",
      "metadata": {},
      "outputs": [],
      "source": [
        "counts, pub_result = get_bitstring_counts(job)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "138ef822-8e66-4f01-94f6-5a634465d2ac",
      "metadata": {},
      "source": [
        "La distance de Hamming entre deux chaînes de bits est le nombre d'indices auxquels les bits diffèrent.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "3488f07f-7705-4ef2-b033-ebc2cbe19d45",
      "metadata": {},
      "outputs": [],
      "source": [
        "def hamming_distance(s1, s2):\n",
        "    weight = 0\n",
        "    for c1, c2 in zip(s1, s2):\n",
        "        (c1, c2) = (int(c1), int(c2))\n",
        "        if (c1 == 1 and c2 == 1) or (c1 == 0 and c2 == 0):\n",
        "            weight += 1\n",
        "\n",
        "    return weight"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "9e2f66e7-cad3-4893-9fde-ddaec077d938",
      "metadata": {
        "scrolled": true
      },
      "outputs": [],
      "source": [
        "# Replace string of form a1b1a2b2... with b1a1b2a1...\n",
        "# That is, reverse order of successive pairs of bits.\n",
        "def unscramble(bitstring):\n",
        "    ps = [bitstring[i : i + 2][::-1] for i in range(0, len(bitstring), 2)]\n",
        "    return \"\".join(ps)\n",
        "\n",
        "\n",
        "def find_hidden_shift_bitstring(counts, hidden_shift_string):\n",
        "    # convert counts to probabilities\n",
        "    probs = {\n",
        "        unscramble(bitstring): count / NUM_SHOTS\n",
        "        for bitstring, count in counts.items()\n",
        "    }\n",
        "\n",
        "    # Retrieve the most probable bitstring.\n",
        "    most_probable = max(probs, key=lambda x: probs[x])\n",
        "\n",
        "    print(f\"Expected hidden shift string: {hidden_shift_string}\")\n",
        "    if most_probable == hidden_shift_string:\n",
        "        print(\"Most probable bitstring matches hidden shift 😊.\")\n",
        "    else:\n",
        "        print(\"Most probable bitstring didn't match hidden shift ☹️.\")\n",
        "    print(\"Top 10 bitstrings and their probabilities:\")\n",
        "    display(\n",
        "        {\n",
        "            k: (v, hamming_distance(hidden_shift_string, k))\n",
        "            for k, v in sorted(\n",
        "                probs.items(), key=lambda x: x[1], reverse=True\n",
        "            )[:10]\n",
        "        }\n",
        "    )\n",
        "\n",
        "    return probs, most_probable"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "9d3dd975-b628-48fc-8565-86b0ed88c973",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Expected hidden shift string: 011010\n",
            "Most probable bitstring matches hidden shift 😊.\n",
            "Top 10 bitstrings and their probabilities:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'011010': (0.9743, 6),\n",
              " '001010': (0.00812, 5),\n",
              " '010010': (0.0063, 5),\n",
              " '011000': (0.00554, 5),\n",
              " '011011': (0.00492, 5),\n",
              " '011110': (0.00044, 5),\n",
              " '001000': (0.00012, 4),\n",
              " '010000': (8e-05, 4),\n",
              " '001011': (6e-05, 4),\n",
              " '000010': (6e-05, 4)}"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "probs, most_probable = find_hidden_shift_bitstring(\n",
        "    counts, hidden_shift_string\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "67a88958-c900-4bd1-8329-d530ba3427ea",
      "metadata": {},
      "source": [
        "Enregistrons la probabilité de la chaîne de bits la plus probable avant d'appliquer la réduction des erreurs de lecture à l'aide de M3.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "a95daf38-009e-44ea-a9c5-8041bf7b22e2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.9743"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "max_probability_before_M3 = probs[most_probable]\n",
        "max_probability_before_M3"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9531e7bd-e134-4110-ba00-d5a08fe612d9",
      "metadata": {},
      "source": [
        "Nous appliquons maintenant la correction de lecture apprise par M3 aux comptages.\n",
        "La fonction `apply_corrections` renvoie une distribution quasi-probabiliste. Voici une liste `float` d'objets dont la somme est égale à $1$. Cependant, certaines valeurs peuvent être négatives.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "3a24d85c-a980-48cf-96ab-5237d370d7af",
      "metadata": {},
      "outputs": [],
      "source": [
        "def perform_mitigation(mit, counts, qubit_mapping):\n",
        "    # mitigate readout error\n",
        "    quasis = mit.apply_correction(counts, qubit_mapping)\n",
        "\n",
        "    # print results\n",
        "    most_probable_after_m3 = unscramble(max(quasis, key=lambda x: quasis[x]))\n",
        "\n",
        "    is_hidden_shift_identified = most_probable_after_m3 == hidden_shift_string\n",
        "    if is_hidden_shift_identified:\n",
        "        print(\"Most probable bitstring matches hidden shift 😊.\")\n",
        "    else:\n",
        "        print(\"Most probable bitstring didn't match hidden shift ☹️.\")\n",
        "    print(\"Top 10 bitstrings and their quasi-probabilities:\")\n",
        "    topten = {\n",
        "        unscramble(k): f\"{v:.2e}\"\n",
        "        for k, v in sorted(quasis.items(), key=lambda x: x[1], reverse=True)[\n",
        "            :10\n",
        "        ]\n",
        "    }\n",
        "    max_probability_after_M3 = float(topten[most_probable_after_m3])\n",
        "    display(topten)\n",
        "\n",
        "    return max_probability_after_M3, is_hidden_shift_identified"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "d304c727-f163-4842-a96a-dad8a067c8d1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Expected hidden shift string: 011010\n",
            "Most probable bitstring matches hidden shift 😊.\n",
            "Top 10 bitstrings and their quasi-probabilities:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'011010': '1.01e+00',\n",
              " '001010': '8.75e-04',\n",
              " '001000': '7.38e-05',\n",
              " '010000': '4.51e-05',\n",
              " '111000': '2.18e-05',\n",
              " '001011': '1.74e-05',\n",
              " '000010': '6.42e-06',\n",
              " '011001': '-7.18e-06',\n",
              " '011000': '-4.53e-04',\n",
              " '010010': '-1.28e-03'}"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "print(f\"Expected hidden shift string: {hidden_shift_string}\")\n",
        "max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(\n",
        "    mit, counts, qubit_mapping\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b62fac82-e883-4030-9793-86af8299abaa",
      "metadata": {},
      "source": [
        "<span id=\"compare-identifying-the-hidden-shift-string-before-and-after-applying-m3-correction\" />\n",
        "\n",
        "#### Comparez l'identification de la chaîne de décalage cachée avant et après l'application de la correction d' M3\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "a3864692-582e-4fe0-8059-f0be59cfd229",
      "metadata": {},
      "outputs": [],
      "source": [
        "def compare_before_and_after_M3(\n",
        "    max_probability_before_M3,\n",
        "    max_probability_after_M3,\n",
        "    is_hidden_shift_identified,\n",
        "):\n",
        "    is_probability_improved = (\n",
        "        max_probability_after_M3 > max_probability_before_M3\n",
        "    )\n",
        "    print(f\"Most probable probability before M3: {max_probability_before_M3}\")\n",
        "    print(f\"Most probable probability after M3: {max_probability_after_M3}\")\n",
        "    if is_hidden_shift_identified and is_probability_improved:\n",
        "        print(\"Readout error mitigation effective! 😊\")\n",
        "    else:\n",
        "        print(\"Readout error mitigation not effective. ☹️\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "98f92ec2-50b5-4456-b40a-3f619cc42ff1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Most probable probability before M3: 0.9743\n",
            "Most probable probability after M3: 1.01\n",
            "Readout error mitigation effective! 😊\n"
          ]
        }
      ],
      "source": [
        "compare_before_and_after_M3(\n",
        "    max_probability_before_M3,\n",
        "    max_probability_after_M3,\n",
        "    is_hidden_shift_identified,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90c3bec4-9a3d-43b7-8160-bcb615230d01",
      "metadata": {},
      "source": [
        "<span id=\"plot-how-cpu-time-required-by-m3-scales-with-shots\" />\n",
        "\n",
        "### Tracer la courbe représentant l'évolution du temps CPU requis par M3 en fonction du nombre de clichés\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "33addc38-f738-48ed-a29d-9790f446c036",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Applying M3 correction to 5000 shots...\n",
            "\tDone in 0.003321983851492405 seconds.\n",
            "Applying M3 correction to 7500 shots...\n",
            "\tDone in 0.004425413906574249 seconds.\n",
            "Applying M3 correction to 10000 shots...\n",
            "\tDone in 0.006366567220538855 seconds.\n",
            "Applying M3 correction to 12500 shots...\n",
            "\tDone in 0.0071477219462394714 seconds.\n",
            "Applying M3 correction to 15000 shots...\n",
            "\tDone in 0.00860048783943057 seconds.\n",
            "Applying M3 correction to 17500 shots...\n",
            "\tDone in 0.010026784148067236 seconds.\n",
            "Applying M3 correction to 20000 shots...\n",
            "\tDone in 0.011459112167358398 seconds.\n",
            "Applying M3 correction to 22500 shots...\n",
            "\tDone in 0.012727141845971346 seconds.\n",
            "Applying M3 correction to 25000 shots...\n",
            "\tDone in 0.01406092382967472 seconds.\n",
            "Applying M3 correction to 27500 shots...\n",
            "\tDone in 0.01546052098274231 seconds.\n",
            "Applying M3 correction to 30000 shots...\n",
            "\tDone in 0.016769016161561012 seconds.\n",
            "Applying M3 correction to 32500 shots...\n",
            "\tDone in 0.019537431187927723 seconds.\n",
            "Applying M3 correction to 35000 shots...\n",
            "\tDone in 0.019739801064133644 seconds.\n",
            "Applying M3 correction to 37500 shots...\n",
            "\tDone in 0.021093040239065886 seconds.\n",
            "Applying M3 correction to 40000 shots...\n",
            "\tDone in 0.022840639110654593 seconds.\n",
            "Applying M3 correction to 42500 shots...\n",
            "\tDone in 0.023974396288394928 seconds.\n",
            "Applying M3 correction to 45000 shots...\n",
            "\tDone in 0.026412792038172483 seconds.\n",
            "Applying M3 correction to 47500 shots...\n",
            "\tDone in 0.026364430785179138 seconds.\n",
            "Applying M3 correction to 50000 shots...\n",
            "\tDone in 0.02820305060595274 seconds.\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "Text(0.5, 1.0, 'Time to apply M3 correction')"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/readout-error-mitigation-sampler/extracted-outputs/33addc38-f738-48ed-a29d-9790f446c036-2.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Collect samples for numbers of shots varying from 5000 to 25000.\n",
        "shots_range = range(5000, NUM_SHOTS + 1, 2500)\n",
        "times = []\n",
        "for shots in shots_range:\n",
        "    print(f\"Applying M3 correction to {shots} shots...\")\n",
        "    t0 = timeit.default_timer()\n",
        "    _ = mit.apply_correction(\n",
        "        pub_result.data.meas.slice_shots(range(shots)).get_counts(),\n",
        "        qubit_mapping,\n",
        "    )\n",
        "    t1 = timeit.default_timer()\n",
        "    print(f\"\\tDone in {t1 - t0} seconds.\")\n",
        "    times.append(t1 - t0)\n",
        "\n",
        "fig, ax = plt.subplots()\n",
        "ax.plot(shots_range, times, \"o--\")\n",
        "ax.set_xlabel(\"Shots\")\n",
        "ax.set_ylabel(\"Time (s)\")\n",
        "ax.set_title(\"Time to apply M3 correction\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bdfbeddd-e90e-4a5c-ad19-810603c4f695",
      "metadata": {},
      "source": [
        "<span id=\"interpreting-the-plot\" />\n",
        "\n",
        "#### Interpréter l'intrigue\n",
        "\n",
        "Le graphique ci-dessus montre que le temps nécessaire à l'application de la correction M3 augmente linéairement avec le nombre de prises de vue.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4988be5c-46ad-473d-b1b1-c8280d8f6324",
      "metadata": {},
      "source": [
        "<span id=\"scaling-up\" />\n",
        "\n",
        "## Mise à l'échelle par augmentation\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "1bf46124-9261-472e-864f-ee2ed0209079",
      "metadata": {
        "jp-MarkdownHeadingCollapsed": true
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Hidden shift string 00000010100110101011101110010001010000110011101001101010101001111001100110000111\n"
          ]
        }
      ],
      "source": [
        "n_qubits = 80\n",
        "rng = Random(12345)\n",
        "circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(\n",
        "    n_qubits, rng\n",
        ")\n",
        "\n",
        "print(f\"Hidden shift string {hidden_shift_string}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "53cd9086-27f8-481b-ba96-9bac9b146188",
      "metadata": {},
      "outputs": [],
      "source": [
        "isa_circuit = get_isa_circuit(circuit, backend)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "b176b2b3-de30-4a9b-905d-a06c7365376a",
      "metadata": {},
      "outputs": [],
      "source": [
        "job = run_sampler(backend, isa_circuit, NUM_SHOTS)\n",
        "mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "c4dda06e-e2c6-46b9-9e74-d90bb318419b",
      "metadata": {},
      "outputs": [],
      "source": [
        "counts, pub_result = get_bitstring_counts(job)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "3421d2f3-083f-44a9-bee4-5f05e43ebb4f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111\n",
            "Most probable bitstring matches hidden shift 😊.\n",
            "Top 10 bitstrings and their probabilities:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': (0.50402,\n",
              "  80),\n",
              " '00000010100110101011101110010001010000110011100001101010101001111001100110000111': (0.0396,\n",
              "  79),\n",
              " '00000010100110101011101110010001010000110011101001101010101001111001100100000111': (0.0323,\n",
              "  79),\n",
              " '00000010100110101011101110010001010000110011101001101010101001101001100110000111': (0.01936,\n",
              "  79),\n",
              " '00000010100110101011101110010011010000110011101001101010101001111001100110000111': (0.01432,\n",
              "  79),\n",
              " '00000010100110101011101110010001010000110011101001101010101001011001100110000111': (0.0101,\n",
              "  79),\n",
              " '00000010100110101011101110010001010000110011101001101010101001110001100110000111': (0.00924,\n",
              "  79),\n",
              " '00000010100110101011101110010001010000010011101001101010101001111001100110000111': (0.00908,\n",
              "  79),\n",
              " '00000010100110101011100110010001010000110011101001101010101001111001100110000111': (0.00888,\n",
              "  79),\n",
              " '00000010100110101011101110010001010000110011101001100010101001111001100110000111': (0.0082,\n",
              "  79)}"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "probs, most_probable = find_hidden_shift_bitstring(\n",
        "    counts, hidden_shift_string\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2ec8335f-d3ef-4951-8bbd-b4d01f899be1",
      "metadata": {},
      "source": [
        "Nous constatons que la chaîne de caractères cachée correcte a été trouvée. En outre, les neuf chaînes de bits les plus probables qui suivent ne sont erronées que dans une seule position.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "98d1fb7b-e696-4cef-a8b8-18e44b96eece",
      "metadata": {},
      "source": [
        "Enregistrez la probabilité la plus probable :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "c5a9dbdc-ebb7-470a-ac31-10b41217eeed",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.50402"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "max_probability_before_M3 = probs[most_probable]\n",
        "max_probability_before_M3"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "b4514b88-3f2d-4e96-88ce-6d291576568d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111\n",
            "Most probable bitstring matches hidden shift 😊.\n",
            "Top 10 bitstrings and their quasi-probabilities:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': '9.85e-01',\n",
              " '00000010100110101011101110010001010000110011100001101010101001111001100110000111': '6.84e-03',\n",
              " '00000010100110101011100110010001010000110011101001101010101001111001100110000111': '3.87e-03',\n",
              " '00000010100110101011101110010011010000110011101001101010101001111001100110000111': '3.42e-03',\n",
              " '00000010100110101011101110010001010000110011101001101010101001111001100100000111': '3.30e-03',\n",
              " '00000010100110101011101110010001010000110011101001101010101001110001100110000111': '3.28e-03',\n",
              " '00000010100010101011101110010001010000110011101001101010101001111001100110000111': '2.62e-03',\n",
              " '00000010100110101011101110010001010000110011101001101010101001101001100110000111': '2.43e-03',\n",
              " '00000010100110101011101110010000010000110011101001101010101001111001100110000111': '1.73e-03',\n",
              " '00000010100110101011101110010001010000110011101001101010101001111001000110000111': '1.63e-03'}"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "print(f\"Expected hidden shift string: {hidden_shift_string}\")\n",
        "max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(\n",
        "    mit, counts, qubit_mapping\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "ccd14379-6c34-4be8-93ab-728356bf81e0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Most probable probability before M3: 0.54348\n",
            "Most probable probability after M3: 0.99\n",
            "Readout error mitigation effective! 😊\n"
          ]
        }
      ],
      "source": [
        "compare_before_and_after_M3(\n",
        "    max_probability_before_M3,\n",
        "    max_probability_after_M3,\n",
        "    is_hidden_shift_identified,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96b6648b-5c26-4a0c-92f2-2d1810fa14b8",
      "metadata": {},
      "source": [
        "Les résultats montrent que l'erreur de lecture est la principale source d'erreur et que l'atténuation M3 a été efficace.\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"
    },
    "toc": {
      "base_numbering": 0
    },
    "hours": 1,
    "qpuSeconds": 60
  },
  "nbformat": 4,
  "nbformat_minor": 4
}