{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "e0cf0747",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Algorithmes quantiques variationnels\"\n",
        "description: \"Ce tutoriel donne un aperçu d'un algorithme hybride quantique-classique, VQE, et du QAOA\"\n",
        "---\n",
        "\n",
        "<span id=\"quantum-algorithms-variational-quantum-algorithms\" />\n",
        "\n",
        "# Algorithmes quantiques : algorithmes quantiques variationnels\n",
        "\n",
        "<Admonition type=\"note\">\n",
        "  Takashi Imamichi (24 mai 2024)\n",
        "\n",
        "  [Télécharger le pdf](https://ibm.ent.box.com/s/blnffu0pd7yzxarq3zc3w0jv90365ny2) de la conférence originale. Notez que certains extraits de code peuvent devenir obsolètes car il s'agit d'images statiques.\n",
        "\n",
        "  *Le temps approximatif d'exécution de cette expérience par le QPU est de 9 minutes (testé sur un processeur Eagle).*\n",
        "\n",
        "  (ce cahier pourrait ne pas être évalué dans le temps imparti sur le plan ouvert. Veuillez utiliser les ressources informatiques quantiques à bon escient.)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "59fbaadc",
      "metadata": {},
      "source": [
        "<span id=\"1-introduction\" />\n",
        "\n",
        "## 1. Introduction\n",
        "\n",
        "Ce tutoriel fournit une vue d'ensemble d'un algorithme hybride quantique-classique, en se concentrant plus particulièrement sur le résolveur quantique variationnel (VQE) et l'algorithme d'optimisation approximative quantique (QAOA). L'objectif principal de ces algorithmes est de résoudre les problèmes d'optimisation en utilisant des circuits quantiques avec des portes quantiques paramétrées.\n",
        "\n",
        "Malgré les progrès de l'informatique quantique, la présence de bruit dans les dispositifs quantiques actuels rend difficile l'extraction de résultats significatifs à partir de circuits quantiques profonds. Pour relever ce défi, VQE et QAOA adoptent une approche hybride quantique-classique, qui implique l'exécution itérative de circuits quantiques relativement courts à l'aide de l'informatique quantique et l'optimisation des paramètres des circuits quantiques paramétrés cibles à l'aide de l'informatique classique.\n",
        "\n",
        "La QAOA a le potentiel de fournir des solutions optimales aux problèmes ciblés à l'échelle d'un service public, grâce à l'application de diverses techniques d'atténuation et de suppression des erreurs. La VQE a de nombreuses applications (comme la chimie quantique) dans lesquelles elle est moins évolutive. Mais un certain nombre d'approches liées aux valeurs propres sont apparues pour compléter et accroître l'EQV, notamment la diagonalisation du sous-espace de Krylov et la diagonalisation quantique basée sur l'échantillonnage (SQD). La compréhension de la VQE est une première étape importante dans la compréhension du large éventail d'algorithmes hybrides classiques-quantiques qui ont vu le jour.\n",
        "\n",
        "Ce module décrit les concepts fondamentaux et la mise en œuvre de VQE et de QAOA. D'autres tutoriels exploreront des sujets avancés et des techniques pour augmenter la taille de ces algorithmes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3f4500f",
      "metadata": {},
      "source": [
        "Vous avez besoin de la bibliothèque suivante dans votre environnement pour exécuter ce cahier.\n",
        "Si vous ne l'avez pas encore installé, vous pouvez le faire en décommentant et en exécutant la cellule suivante.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2a8e650f",
      "metadata": {},
      "outputs": [],
      "source": [
        "# % pip install 'qiskit[visualization]' qiskit-ibm-runtime"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be601762",
      "metadata": {},
      "source": [
        "<span id=\"2-computing-the-minimum-eigenvalue-of-a-simple-hamiltonian\" />\n",
        "\n",
        "## 2. Calcul de la valeur propre minimale d'un hamiltonien simple\n",
        "\n",
        "Nous commencerons par appliquer l'EQV à un cas très simple, afin de voir comment il fonctionne. Nous calculerons la valeur propre minimale de la matrice de Pauli $Z$ avec VQE. Nous commencerons par importer quelques paquets généraux.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e8f398b2",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "from qiskit.circuit import ParameterVector, QuantumCircuit\n",
        "from qiskit.primitives import StatevectorEstimator, StatevectorSampler\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from scipy.optimize import minimize"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4395012",
      "metadata": {},
      "source": [
        "Nous allons maintenant définir l'opérateur qui nous intéresse et le représenter sous forme de matrice.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "a4f374a2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array([[ 1.+0.j,  0.+0.j],\n",
              "       [ 0.+0.j, -1.+0.j]])"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "op = SparsePauliOp(\"Z\")\n",
        "op.to_matrix()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a001e71",
      "metadata": {},
      "source": [
        "Il est facile d'obtenir les valeurs propres de manière classique, ce qui nous permet de vérifier notre travail. Cela pourrait s'avérer difficile au fur et à mesure que nous nous rapprochons de l'utilité. Nous utilisons ici numpy.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "c9188662",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Eigenvalues: [-1.  1.]\n"
          ]
        }
      ],
      "source": [
        "# compute eigenvalues with numpy\n",
        "result = np.linalg.eigh(op.to_matrix())\n",
        "print(\"Eigenvalues:\", result.eigenvalues)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f9af345",
      "metadata": {},
      "source": [
        "Pour obtenir les valeurs propres à l'aide d'un algorithme quantique variationnel, nous construisons un circuit avec des portes qui prennent des paramètres variationnels :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "99d5d36b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/99d5d36b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# define a variational form\n",
        "param = ParameterVector(\"a\", 3)\n",
        "qc = QuantumCircuit(1, 1)\n",
        "qc.u(param[0], param[1], param[2], 0)\n",
        "qc_estimator = qc.copy()\n",
        "qc.measure(0, 0)\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9607ab51",
      "metadata": {},
      "source": [
        "Si nous voulons estimer la valeur de l'espérance d'un opérateur (comme $Z$ ), nous devons utiliser Estimator. Si nous voulons examiner les états du système, nous utilisons l'échantillonneur.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "df8ed8d3",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = StatevectorSampler()\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "880bccfb",
      "metadata": {},
      "source": [
        "Nous pouvons calculer le nombre de chaînes de bits 0 et 1 avec des valeurs de paramètres aléatoires `[1, 2, 3]` à l'aide de Sampler.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "5301ee71",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 783, '1': 241}"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute counts of bitstrings with random parameter values by Sampler\n",
        "result = sampler.run([(qc, [1, 2, 3])]).result()\n",
        "counts = result[0].data.c.get_counts()\n",
        "counts"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "30ef9b8f",
      "metadata": {},
      "source": [
        "Nous savons que nous pouvons calculer la valeur espérée de Z par $\\langle Z \\rangle = p_0 - p_1$ avec les probabilités $\\{0: p_0, 1: p_1\\}$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "0f220c8d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.529296875"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute the expectation value of Z based on the counts\n",
        "(counts.get(\"0\", 0) - counts.get(\"1\", 0)) / sum(counts.values())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "695ee4bd",
      "metadata": {},
      "source": [
        "Ce circuit a fonctionné, mais les valeurs des paramètres choisis ne correspondaient pas à un état de très faible énergie (ou de faible valeur propre). La valeur propre obtenue est nettement supérieure à la valeur minimale. Le résultat est similaire lorsque l'on utilise l'estimateur.\n",
        "\n",
        "Notez que l'Estimateur prend des circuits quantiques sans mesures.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c9e05530",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(0.54030231)"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "result = estimator.run([(qc_estimator, op, [1, 2, 3])]).result()\n",
        "result[0].data.evs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b538e8b5",
      "metadata": {},
      "source": [
        "Nous devrons rechercher parmi les paramètres ceux qui produisent la valeur propre la plus faible.\n",
        "Nous créons une fonction qui reçoit les valeurs des paramètres de la forme variationnelle et renvoie la valeur de l'espérance $\\langle Z \\rangle$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "97fead46",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of Z\n",
        "def cost(x):\n",
        "    result = sampler.run([(qc, x)]).result()\n",
        "    counts = result[0].data.c.get_counts()\n",
        "    expval = (counts.get(\"0\", 0) - counts.get(\"1\", 0)) / sum(counts.values())\n",
        "    # the following line shows the trajectory of the optimization\n",
        "    print(expval, counts)\n",
        "    return expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a97eaeba",
      "metadata": {},
      "source": [
        "Appliquons la fonction SciPy's `minimize` pour trouver la valeur propre minimale de Z.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "44f56300",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.0 {'0': 1024}\n",
            "0.494140625 {'0': 765, '1': 259}\n",
            "0.466796875 {'0': 751, '1': 273}\n",
            "0.564453125 {'0': 801, '1': 223}\n",
            "-0.4296875 {'1': 732, '0': 292}\n",
            "-0.984375 {'1': 1016, '0': 8}\n",
            "-0.8984375 {'1': 972, '0': 52}\n",
            "-0.990234375 {'1': 1019, '0': 5}\n",
            "-0.892578125 {'1': 969, '0': 55}\n",
            "-0.986328125 {'1': 1017, '0': 7}\n",
            "-0.861328125 {'1': 953, '0': 71}\n",
            "-1.0 {'1': 1024}\n",
            "-0.982421875 {'1': 1015, '0': 9}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.986328125 {'1': 1017, '0': 7}\n",
            "-1.0 {'1': 1024}\n",
            "-0.990234375 {'1': 1019, '0': 5}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.99609375 {'1': 1022, '0': 2}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-0.998046875 {'1': 1023, '0': 1}\n",
            "-0.994140625 {'1': 1021, '0': 3}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n",
            "-1.0 {'1': 1024}\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -1.0\n",
              "       x: [ 3.182e+00  1.338e+00  1.664e-01]\n",
              "    nfev: 63\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "min_result = minimize(cost, [0, 0, 0], method=\"COBYLA\", tol=1e-8)\n",
        "min_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "4198224e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'0': 1, '1': 1023}"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# check counts of bitstrings with the optimal parameters\n",
        "result = sampler.run([(qc, min_result.x)]).result()\n",
        "result[0].data.c.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d4e77e29",
      "metadata": {},
      "source": [
        "<span id=\"21-exercise\" />\n",
        "\n",
        "### 2.1 Exercice\n",
        "\n",
        "Calculer la valeur propre minimale de $Z \\otimes Z$ avec VQE.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "86a2d76d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['ZZ'],\n",
            "              coeffs=[1.+0.j])\n",
            "[[ 1.+0.j  0.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j -1.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j  0.+0.j  1.+0.j]]\n"
          ]
        }
      ],
      "source": [
        "z2 = SparsePauliOp(\"ZZ\")\n",
        "print(z2)\n",
        "print(z2.to_matrix())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "76092648-3b28-442e-8319-8a8416e0c9d5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# compute eigenvalues with numpy"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "f92a23b5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a variational form\n",
        "# qc = ..."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "496572fb",
      "metadata": {
        "scrolled": true
      },
      "outputs": [],
      "source": [
        "# compute counts of bitstrings with a random parameter values by Sampler\n",
        "# result = sampler.run(...)\n",
        "# result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "b4cad5d0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# compute the expectation value of ZZ based on the counts"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "5ce555fe",
      "metadata": {},
      "outputs": [],
      "source": [
        "# verify the expectation value of ZZ with Estimator"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "c210c435",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of ZZ\n",
        "# def cost(x):\n",
        "#    expval = ...\n",
        "#    return expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "a75f3303",
      "metadata": {},
      "outputs": [],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "# min_result = minimize(cost, [...], method=\"COBYLA\", tol=1e-8)\n",
        "# min_result"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "8c476943",
      "metadata": {},
      "outputs": [],
      "source": [
        "# check counts of bitstrings with the optimal parameter values\n",
        "# result = sampler.run(qc, min_result.x).result()\n",
        "# result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b82ba33a",
      "metadata": {},
      "source": [
        "<span id=\"solutions-of-the-exercise\" />\n",
        "\n",
        "#### Solutions de l'exercice\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f2e0a4e",
      "metadata": {},
      "source": [
        "Nous définissons l'opérateur qui nous intéresse et le représentons sous forme de matrice.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "a1f9d371",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "SparsePauliOp(['ZZ'],\n",
            "              coeffs=[1.+0.j])\n",
            "[[ 1.+0.j  0.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j -1.+0.j  0.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j -1.+0.j  0.+0.j]\n",
            " [ 0.+0.j  0.+0.j  0.+0.j  1.+0.j]]\n"
          ]
        }
      ],
      "source": [
        "z2 = SparsePauliOp(\"ZZ\")\n",
        "print(z2)\n",
        "print(z2.to_matrix())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b123e9d9",
      "metadata": {},
      "source": [
        "Pour obtenir les valeurs propres à l'aide d'un algorithme quantique variationnel, nous construisons un circuit avec des portes qui prennent des paramètres variationnels :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "7d5e894a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/7d5e894a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# define a variational form\n",
        "param = ParameterVector(\"a\", 6)\n",
        "qc = QuantumCircuit(2, 2)\n",
        "qc.u(param[0], param[1], param[2], 0)\n",
        "qc.u(param[3], param[4], param[5], 1)\n",
        "qc_estimator = qc.copy()\n",
        "qc.measure([0, 1], [0, 1])\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1d86563",
      "metadata": {},
      "source": [
        "Si nous voulons estimer la valeur de l'espérance d'un opérateur (comme $Z \\otimes Z$ ), nous utiliserons Estimator. Si nous voulons examiner les états du système, nous utilisons l'échantillonneur.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "e3bda5ff",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = StatevectorSampler()\n",
        "estimator = StatevectorEstimator()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "aeac09f7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'10': 661, '11': 203, '01': 47, '00': 113}"
            ]
          },
          "execution_count": 16,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute counts of bitstrings with random parameter values by Sampler\n",
        "result = sampler.run([(qc, [1, 2, 3, 4, 5, 6])]).result()\n",
        "counts = result[0].data.c.get_counts()\n",
        "counts"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "c96acbdf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "-0.3828125"
            ]
          },
          "execution_count": 17,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# compute the expectation value of ZZ based on the counts\n",
        "(\n",
        "    counts.get(\"00\", 0)\n",
        "    - counts.get(\"01\", 0)\n",
        "    - counts.get(\"10\", 0)\n",
        "    + counts.get(\"11\", 0)\n",
        ") / sum(counts.values())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d2cc2ed8",
      "metadata": {},
      "source": [
        "Ce circuit a fonctionné, mais les valeurs des paramètres choisis ne correspondaient pas à un état de très faible énergie (ou de faible valeur propre). La valeur propre obtenue est nettement supérieure à la valeur minimale. Le résultat est similaire lorsque l'on utilise l'estimateur.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "474b84c5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(-0.35316516)"
            ]
          },
          "execution_count": 18,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# verify the expectation value of ZZ with Estimator\n",
        "result = estimator.run([(qc_estimator, z2, [1, 2, 3, 4, 5, 6])]).result()\n",
        "result[0].data.evs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "25f4a655",
      "metadata": {},
      "source": [
        "Nous devrons rechercher parmi les paramètres ceux qui produisent la valeur propre la plus faible.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "f29322f3",
      "metadata": {},
      "outputs": [],
      "source": [
        "# define a cost function to look for the minimum eigenvalue of ZZ\n",
        "def cost(x):\n",
        "    result = sampler.run([(qc, x)]).result()\n",
        "    counts = result[0].data.c.get_counts()\n",
        "    expval = (\n",
        "        counts.get(\"00\", 0)\n",
        "        - counts.get(\"01\", 0)\n",
        "        - counts.get(\"10\", 0)\n",
        "        + counts.get(\"11\", 0)\n",
        "    ) / sum(counts.values())\n",
        "    print(expval, counts)\n",
        "    return expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "12d8ba03",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "1.0 {'00': 1024}\n",
            "0.578125 {'00': 808, '01': 216}\n",
            "0.5234375 {'00': 780, '01': 244}\n",
            "0.548828125 {'00': 793, '01': 231}\n",
            "0.3515625 {'00': 637, '10': 164, '11': 55, '01': 168}\n",
            "0.3359375 {'00': 638, '11': 46, '10': 174, '01': 166}\n",
            "0.283203125 {'00': 602, '10': 181, '01': 186, '11': 55}\n",
            "-0.087890625 {'01': 414, '00': 184, '10': 143, '11': 283}\n",
            "0.236328125 {'10': 27, '11': 623, '01': 364, '00': 10}\n",
            "-0.0625 {'11': 261, '01': 403, '00': 219, '10': 141}\n",
            "0.248046875 {'01': 366, '11': 628, '00': 11, '10': 19}\n",
            "-0.0625 {'10': 145, '11': 254, '01': 399, '00': 226}\n",
            "0.228515625 {'01': 373, '11': 609, '00': 20, '10': 22}\n",
            "0.0546875 {'11': 376, '10': 273, '01': 211, '00': 164}\n",
            "-0.447265625 {'01': 731, '10': 10, '11': 267, '00': 16}\n",
            "-0.71484375 {'01': 871, '11': 99, '00': 47, '10': 7}\n",
            "-0.46484375 {'01': 741, '00': 253, '10': 9, '11': 21}\n",
            "-0.87890625 {'01': 962, '00': 39, '11': 23}\n",
            "-0.640625 {'00': 176, '01': 837, '11': 8, '10': 3}\n",
            "-0.88671875 {'01': 966, '00': 41, '11': 17}\n",
            "-0.994140625 {'01': 1021, '11': 3}\n",
            "-0.91796875 {'01': 982, '11': 35, '00': 7}\n",
            "-0.994140625 {'01': 1021, '11': 2, '00': 1}\n",
            "-0.939453125 {'01': 993, '00': 31}\n",
            "-0.990234375 {'01': 1019, '11': 5}\n",
            "-0.90234375 {'01': 974, '00': 21, '11': 29}\n",
            "-0.98046875 {'01': 1014, '11': 10}\n",
            "-0.994140625 {'01': 1021, '00': 3}\n",
            "-0.990234375 {'01': 1019, '11': 4, '00': 1}\n",
            "-0.98828125 {'01': 1018, '11': 6}\n",
            "-0.990234375 {'01': 1019, '11': 4, '00': 1}\n",
            "-0.994140625 {'01': 1021, '11': 2, '00': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.99609375 {'01': 1022, '00': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.99609375 {'01': 1022, '00': 1, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.99609375 {'01': 1022, '11': 1, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.994140625 {'01': 1021, '00': 3}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '00': 1}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n",
            "-0.99609375 {'01': 1022, '11': 2}\n",
            "-1.0 {'01': 1024}\n",
            "-0.998046875 {'01': 1023, '11': 1}\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -0.998046875\n",
              "       x: [ 3.167e+00  6.940e-01  1.033e+00 -2.894e-02  8.933e-01\n",
              "            1.885e+00]\n",
              "    nfev: 128\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 20,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              " message: Optimization terminated successfully.\n",
              " success: True\n",
              "  status: 1\n",
              "     fun: -0.99609375\n",
              "       x: [ 3.098e+00 -5.402e-01  1.091e+00 -1.004e-02  3.615e-01\n",
              "            6.913e-01]\n",
              "    nfev: 115\n",
              "   maxcv: 0.0"
            ]
          },
          "execution_count": 30,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# minimize the cost function with scipy's minimize\n",
        "min_result = minimize(cost, [0, 0, 0, 0, 0, 0], method=\"COBYLA\", tol=1e-8)\n",
        "min_result"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3e2de8d",
      "metadata": {},
      "source": [
        "Nous avons obtenu une valeur propre extrêmement proche du minimum donné par numpy.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "d34a8544",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "{'01': 1024}"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# check counts of bitstrings with the optimal parameters\n",
        "result = sampler.run([(qc, min_result.x)]).result()\n",
        "result[0].data.c.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc51e7bf-e582-49ba-93f8-035624d56ccf",
      "metadata": {},
      "source": [
        "<span id=\"3-quantum-optimization-with-qiskit-patterns\" />\n",
        "\n",
        "## 3. Optimisation quantique avec les modèles Qiskit\n",
        "\n",
        "Dans ce guide pratique, nous apprendrons à connaître les motifs Qiskit et l'optimisation approximative quantique. Un modèle Qiskit est un ensemble d'étapes intuitives et reproductibles pour la mise en œuvre d'un flux de travail d'informatique quantique :\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "636ed1de-fc34-4cdd-9398-6fbd7c7fc9c6",
      "metadata": {},
      "source": [
        "![\"Fonction Qiskit](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/qiskit-function.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d3218ca-ce9e-40b4-a041-1b1d09bac8f5",
      "metadata": {},
      "source": [
        "Nous appliquerons ces modèles au domaine de **l'optimisation combinatoire** et montrerons comment résoudre le problème **du coupure maximale** à l'aide de **l** 'algorithme d'optimisation approximative quantique (QAOA), une méthode itérative hybride (quantique-classique).\n",
        "\n",
        "Notez que cette partie de QAOA est basée sur la \"Partie 1 : QAOA à petite échelle\" du tutoriel sur [l'algorithme d'optimisation approximative quantique](/docs/tutorials/quantum-approximate-optimization-algorithm). Voir le tutoriel pour savoir comment l'agrandir.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e943b1a-218a-468c-bb63-4269896ebebe",
      "metadata": {},
      "source": [
        "<span id=\"31-small-scale-qiskit-pattern-for-optimization\" />\n",
        "\n",
        "### 3.1 Modèle Qiskit (à petite échelle) pour l'optimisation\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68fd0b4f-baa4-45dc-9f4c-d9cdff01a651",
      "metadata": {},
      "source": [
        "Cette partie s'appuiera sur un problème de « max-cut » à petite échelle pour illustrer les étapes nécessaires à la résolution d'un problème d'optimisation à l'aide d'un ordinateur quantique.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74b92ba5-c48a-405c-9c4b-04e985a7afbc",
      "metadata": {},
      "source": [
        "Le problème du « max-cut » est un problème d'optimisation difficile à résoudre (plus précisément, il s'agit d'un problème NP-difficile) qui trouve de nombreuses applications dans le regroupement de données, la science des réseaux et la physique statistique. Ce tutoriel porte sur un graphe composé de nœuds reliés par des arêtes et vise à partitionner ces nœuds en deux ensembles en « coupant » des arêtes, de manière à maximiser le nombre d'arêtes coupées.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "887eb6ea-f58e-482f-8965-953a08fceecf",
      "metadata": {},
      "source": [
        "![\"Maxcut](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/maxcut.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "893a25f2",
      "metadata": {},
      "source": [
        "Pour replacer ce problème dans son contexte avant de le transposer en algorithme quantique, vous comprendrez mieux comment le problème du coupé maximal se transforme en un problème d'optimisation combinatoire classique en considérant d'abord la minimisation d'une fonction $f(x)$\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n}f(x),\n",
        "$$\n",
        "\n",
        "où l'entrée $x$ est un vecteur dont les composantes correspondent à chaque nœud d'un graphe.  Ensuite, contraignez chacune de ces composantes à être soit $0$, soit $1$ (ce qui représente le fait d'être inclus ou non dans la coupe). Cet exemple à petite échelle utilise un graphe avec $n=5$ nœuds.\n",
        "\n",
        "Vous pouvez écrire une fonction pour une paire de nœuds $i,j$ qui indique si l'arête correspondante $(i,j)$ est dans la coupe. Par exemple, la fonction $x_i + x_j - 2 x_i x_j$ n'est égale à 1 que si l'une des valeurs $x_i$ ou $x_j$ est égale à 1 (ce qui signifie que le bord est dans la coupe) et à zéro dans le cas contraire. Le problème de la maximisation des arêtes dans la coupe peut être formulé comme suit\n",
        "\n",
        "$$\n",
        "\\max_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)} x_i + x_j - 2 x_i x_j,\n",
        "$$\n",
        "\n",
        "qui peut être réécrite comme une minimisation de la forme\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n} \\sum_{(i,j)}  2 x_i x_j - x_i - x_j.\n",
        "$$\n",
        "\n",
        "Dans ce cas, le minimum de $f(x)$ est atteint lorsque le nombre d'arêtes traversées par la coupe est maximal. Comme vous pouvez le constater, il n'y a encore rien en rapport avec l'informatique quantique. Vous devez reformuler ce problème de manière à ce qu'un ordinateur quantique puisse le comprendre.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2105a90-027f-44d7-97d1-c2c99373d488",
      "metadata": {},
      "source": [
        "Initialisez votre problème en créant un graphe avec $n=5$ nœuds.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "d3c0dfa7",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib\n",
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "import rustworkx as rx\n",
        "from rustworkx.visualization import mpl_draw"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "99e763fe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/99e763fe-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n = 5\n",
        "\n",
        "graph = rx.PyGraph()\n",
        "graph.add_nodes_from(range(1, n + 1))\n",
        "edge_list = [\n",
        "    (0, 1, 1.0),\n",
        "    (0, 2, 1.0),\n",
        "    (1, 2, 1.0),\n",
        "    (1, 3, 1.0),\n",
        "    (2, 4, 1.0),\n",
        "    (3, 4, 1.0),\n",
        "]\n",
        "graph.add_edges_from(edge_list)\n",
        "pos = rx.spring_layout(graph, seed=2)\n",
        "mpl_draw(graph, node_size=600, pos=pos, with_labels=True, labels=str)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a06e4386-d7bd-4914-9baa-36a5cc60e3ab",
      "metadata": {},
      "source": [
        "<span id=\"32-step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### 3.2 Étape 1. Mapper les entrées classiques à un problème quantique\n",
        "\n",
        "La première étape du modèle consiste à traduire le problème classique (graphe) en **circuits** et **opérateurs** quantiques. Pour ce faire, il y a trois étapes principales à franchir :\n",
        "\n",
        "1. Utiliser une série de reformulations mathématiques pour représenter ce problème à l'aide de la notation des problèmes d'optimisation binaire quadratique sans contrainte (QUBO).\n",
        "2. Réécrire le problème d'optimisation sous la forme d'un hamiltonien pour lequel l'état fondamental correspond à la solution qui minimise la fonction de coût.\n",
        "3. Créez un circuit quantique qui préparera l'état fondamental de cet hamiltonien par un processus similaire au recuit quantique.\n",
        "\n",
        "**Remarque :** dans la méthodologie QAOA, vous souhaitez disposer d'un opérateur (**hamiltonien** ) qui représente la **fonction de coût de** notre algorithme hybride, ainsi que d'un circuit paramétré (**Ansatz** ) qui représente les états quantiques avec des solutions candidates au problème. Vous pouvez prélever un échantillon de ces états candidats, puis les évaluer à l'aide de la fonction de coût.\n",
        "\n",
        "<span id=\"graph-→-optimization-problem\" />\n",
        "\n",
        "#### Graphique → problème d'optimisation\n",
        "\n",
        "La première étape de la mise en correspondance est un changement de notation. Le problème est exprimé ci-dessous en notation QUBO :\n",
        "\n",
        "$$\n",
        "\\min_{x\\in \\{0, 1\\}^n}x^T Q x,\n",
        "$$\n",
        "\n",
        "où $Q$ est une matrice $n\\times n$ de nombres réels, $n$ correspond au nombre de nœuds dans votre graphe, $x$ est le vecteur de variables binaires introduit ci-dessus, et $x^T$ indique la transposée du vecteur $x$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c00a3493-abab-46d8-86a9-5b853979a575",
      "metadata": {},
      "source": [
        "```\n",
        "Problem name: maxcut\n",
        "\n",
        "Minimize\n",
        "  2*x_1*x_2 + 2*x_1*x_3 + 2*x_2*x_3 + 2*x_2*x_4 + 2*x_3*x_5 + 2*x_4*x_5 - 2*x_1\n",
        "  - 3*x_2 - 3*x_3 - 2*x_4 - 2*x_5\n",
        "\n",
        "Subject to\n",
        "  No constraints\n",
        "\n",
        "  Binary variables (5)\n",
        "    x_1 x_2 x_3 x_4 x_5\n",
        "```\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a5b9e551-38a1-4543-b9f1-caaefb0ef3a9",
      "metadata": {},
      "source": [
        "<span id=\"optimization-problem-→-hamiltonian\" />\n",
        "\n",
        "#### Problème d'optimisation → Hamiltonien\n",
        "\n",
        "Vous pouvez alors reformuler le problème QUBO sous la forme d'un **hamiltonien** (ici, une matrice qui représente l'énergie d'un système) :\n",
        "\n",
        "$$\n",
        "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n",
        "$$\n",
        "\n",
        "**Étapes de reformulation du problème QAOA à l'hamiltonien**\n",
        "\n",
        "Pour démontrer comment le problème du QAOA peut être réécrit de cette manière, il faut d'abord remplacer les variables binaires $x_i$ par un nouvel ensemble de variables $z_i\\in\\{-1, 1\\}$ par l'intermédiaire de\n",
        "\n",
        "$$\n",
        "x_i = \\frac{1-z_i}{2}.\n",
        "$$\n",
        "\n",
        "On voit ici que si $x_i$ est $0$, alors $z_i$ doit être $1$. Lorsque les $x_i$ sont remplacés par les $z_i$ dans le problème d'optimisation ( $x^TQx$ ), on obtient une formulation équivalente.\n",
        "\n",
        "$$\n",
        "x^TQx=\\sum_{ij}Q_{ij}x_ix_j \\\\ =\\frac{1}{4}\\sum_{ij}Q_{ij}(1-z_i)(1-z_j) \\\\=\\frac{1}{4}\\sum_{ij}Q_{ij}z_iz_j-\\frac{1}{4}\\sum_{ij}(Q_{ij}+Q_{ji})z_i + \\frac{n^2}{4}.\n",
        "$$\n",
        "\n",
        "Si nous définissons $b_i=-\\sum_{j}(Q_{ij}+Q_{ji})$, supprimons le préfacteur et le terme constant $n^2$, nous obtenons deux formulations équivalentes du même problème d'optimisation.\n",
        "\n",
        "$$\n",
        "min_{x\\in\\{0,1\\}^n} x^TQx\\Longleftrightarrow \\min_{z\\in\\{-1,1\\}^n}z^TQz + b^Tz\n",
        "$$\n",
        "\n",
        "Ici, $b$ dépend de $Q$. Notez que pour obtenir $z^TQz + b^Tz$ nous avons abandonné le facteur 1/4 et un décalage constant de $n^2$ qui ne jouent pas de rôle dans l'optimisation.\n",
        "\n",
        "Maintenant, pour obtenir une formulation quantique du problème, il faut promouvoir les variables $z_i$ en une matrice de Pauli $Z$, telle qu'une matrice $2\\times 2$ de la forme\n",
        "\n",
        "$$\n",
        "Z_i = \\begin{pmatrix}1 & 0 \\\\ 0 & -1\\end{pmatrix}.\n",
        "$$\n",
        "\n",
        "En remplaçant ces matrices dans le problème d'optimisation ci-dessus, on obtient l'hamiltonien suivant\n",
        "\n",
        "$$\n",
        "H_C=\\sum_{ij}Q_{ij}Z_iZ_j + \\sum_i b_iZ_i.\n",
        "$$\n",
        "\n",
        "*Rappelez-vous également que les matrices $Z$ sont intégrées dans l'espace de calcul de l'ordinateur quantique, c'est-à-dire un espace de Hilbert de taille $2^n\\times 2^n$. Par conséquent, vous devez comprendre des termes tels que $Z_iZ_j$ comme le produit tensoriel $Z_i\\otimes Z_j$ intégré dans l'espace de Hilbert $2^n\\times 2^n$. Par exemple, dans un problème comportant cinq variables de décision, le terme $Z_1Z_3$ est compris comme signifiant $I\\otimes Z_3\\otimes I\\otimes Z_1\\otimes I$ où $I$ est la matrice d'identité $2\\times 2$.*\n",
        "\n",
        "Cet hamiltonien est appelé <b>fonction de coût Hamiltonien</b>. Il a la propriété que son état fondamental correspond à la solution que <b>minimise la fonction de coût $f(x)$</b>. Par conséquent, pour résoudre votre problème d'optimisation, vous devez maintenant préparer l'état fondamental de $H_C$ (ou un état ayant un fort recouvrement avec lui) sur l'ordinateur quantique. L'échantillonnage de cet état permet alors, avec une forte probabilité, d'obtenir la solution de $\\min~f(x)$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "47256f53-8e02-494e-864a-a6f45b10442a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def build_max_cut_operator(graph: rx.PyGraph) -> tuple[SparsePauliOp, float]:\n",
        "    sp_list = []\n",
        "    constant = 0\n",
        "    for s, t in graph.edge_list():\n",
        "        w = graph.get_edge_data(s, t)\n",
        "        sp_list.append((\"ZZ\", [s, t], w / 2))\n",
        "        constant -= 1 / 2\n",
        "    return SparsePauliOp.from_sparse_list(\n",
        "        sp_list, num_qubits=graph.num_nodes()\n",
        "    ), constant"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "01a2d8eb-b63b-40bc-93c5-0b547c06b194",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Cost Function Hamiltonian: SparsePauliOp(['IIIZZ', 'IIZIZ', 'IIZZI', 'IZIZI', 'ZIZII', 'ZZIII'],\n",
            "              coeffs=[0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j])\n",
            "Constant: -3.0\n"
          ]
        }
      ],
      "source": [
        "cost_hamiltonian, constant = build_max_cut_operator(graph)\n",
        "print(\"Cost Function Hamiltonian:\", cost_hamiltonian)\n",
        "print(\"Constant:\", constant)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33f71b0d-4a2a-4082-8c1a-ce9d2b769048",
      "metadata": {},
      "source": [
        "<span id=\"hamiltonian-→-quantum-circuit\" />\n",
        "\n",
        "#### Circuit quantique hamiltonien\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "00431c46-30c2-40f9-99df-40baf8da98f6",
      "metadata": {},
      "source": [
        "L'hamiltonien $H_C$ contient la définition quantique de votre problème. Vous pouvez maintenant créer un circuit quantique qui permettra d' *échantillonner les* bonnes solutions de l'ordinateur quantique. Le QAOA s'inspire du recuit quantique et applique des couches alternées d'opérateurs dans le circuit quantique.\n",
        "\n",
        "L'idée générale est de partir de l'état fondamental d'un système connu, $H^{\\otimes n}|0\\rangle$ ci-dessus, puis d'orienter le système vers l'état fondamental de l'opérateur de coût qui vous intéresse. Pour ce faire, les opérateurs $\\exp\\{-i\\gamma_k H_C\\}$ et $\\exp\\{-i\\beta_k H_m\\}$ sont appliqués avec les angles $\\gamma_1,...,\\gamma_p$ et $\\beta_1,...,\\beta_p~$.\n",
        "\n",
        "Le circuit quantique que vous générez est **paramétré** par $\\gamma_i$ et $\\beta_i$, de sorte que vous pouvez essayer différentes valeurs de $\\gamma_i$ et $\\beta_i$ et échantillonner l'état résultant.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "ca09d6bf-e421-4ada-9515-1f69687bb511",
      "metadata": {},
      "source": [
        "![\"Schéma de circuit QAOA\"](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/circuit-diagram.svg)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d12bc53-7805-43e4-b4cb-754fd234b519",
      "metadata": {},
      "source": [
        "Dans ce cas, nous allons essayer un exemple avec une couche de QAOA qui contient deux paramètres : $\\gamma_1$ et $\\beta_1$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "1f6215c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit.library import QAOAAnsatz"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "7bd8c6d4-f40f-4a11-a440-0b26d9021b53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/7bd8c6d4-f40f-4a11-a440-0b26d9021b53-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit = QAOAAnsatz(cost_operator=cost_hamiltonian, reps=1)\n",
        "circuit.measure_all()\n",
        "circuit.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "148d2d62",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/148d2d62-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit.decompose(reps=3).draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "315c495a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "ParameterView([ParameterVectorElement(β[0]), ParameterVectorElement(γ[0])])"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit.parameters"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "82f70daa-ff68-447a-8064-8b7df7a646cf",
      "metadata": {},
      "source": [
        "<span id=\"33-step-2-optimize-circuits-for-quantum-hardware-execution\" />\n",
        "\n",
        "### 3.3 Étape 2. Optimiser les circuits pour l'exécution sur du matériel quantique\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c08be444-e3ed-4178-a10b-414069b1b411",
      "metadata": {},
      "source": [
        "Le circuit ci-dessus contient une série d'abstractions utiles pour réfléchir aux algorithmes quantiques, mais impossibles à exécuter sur le matériel. Pour pouvoir fonctionner sur une QPU, le circuit doit subir une série d'opérations qui constituent l'étape de **transpilation** ou d' **optimisation du circuit** du modèle.\n",
        "\n",
        "La bibliothèque Qiskit offre une série de **passes de transpilation** qui répondent à un large éventail de transformations de circuits. Vous devez vous assurer que votre circuit est **optimisé** pour votre objectif.\n",
        "\n",
        "La transpilation peut comporter plusieurs étapes, telles que\n",
        "\n",
        "* **Mappage initial** des qubits du circuit (tels que les variables de décision) aux qubits physiques de l'appareil.\n",
        "* **Déroulement** des instructions dans le circuit quantique vers les instructions natives du matériel que le backend comprend.\n",
        "* **Routage** de tous les qubits du circuit qui interagissent vers des qubits physiques adjacents.\n",
        "* **Suppression des erreurs** par l'ajout de portes à qubit unique pour supprimer le bruit avec découplage dynamique.\n",
        "\n",
        "De plus amples informations sur la transpilation sont disponibles dans notre [documentation](/docs/guides/transpile).\n",
        "\n",
        "Le code suivant transforme et optimise le circuit abstrait dans un format prêt à être exécuté sur l'un des appareils accessibles via le cloud en utilisant le **service Qiskit IBM® Runtime**.\n",
        "\n",
        "Notez que vous pouvez tester vos programmes localement grâce au \"mode de test local\" avant de les envoyer à de véritables ordinateurs quantiques.\n",
        "De plus amples informations sur le mode de test local sont disponibles dans la [documentation.](/docs/guides/local-testing-mode)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "95cd3eed-0348-4373-b664-16a65d42f1e7",
      "metadata": {},
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "  service = QiskitRuntimeService(channel=\"ibm_quantum_platform\")\n"
          ]
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_strasbourg')>\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/95cd3eed-0348-4373-b664-16a65d42f1e7-2.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 31,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "# Use a quantum device\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(min_num_qubits=127)\n",
        "# backend = service.backend(\"ibm_kingston\")\n",
        "\n",
        "# You can test your programs locally with a fake backend (local testing mode)\n",
        "# backend = FakeBrisbane()\n",
        "\n",
        "print(backend)\n",
        "\n",
        "# Create pass manager for transpilation\n",
        "pm = generate_preset_pass_manager(optimization_level=3, backend=backend)\n",
        "\n",
        "candidate_circuit = pm.run(circuit)\n",
        "candidate_circuit.draw(\"mpl\", fold=False, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4e75cad7-f599-4937-b5fe-f4d01f53423c",
      "metadata": {},
      "source": [
        "<span id=\"34-step-3-execute-using-ibm-quantum-primitives\" />\n",
        "\n",
        "### 3.4 Étape 3. Exécuter à l'aide des primitives « IBM Quantum »\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9b99ce67-f121-4244-b62a-536be38fea86",
      "metadata": {},
      "source": [
        "Dans le flux de travail du QAOA, les paramètres optimaux du QAOA sont trouvés dans une boucle d'optimisation itérative, qui exécute une série d'évaluations de circuits et utilise un optimiseur classique pour trouver les paramètres optimaux $\\beta_k$ et $\\gamma_k$. Cette boucle d'exécution est exécutée en suivant les étapes suivantes :\n",
        "\n",
        "1. Définir les paramètres initiaux\n",
        "2. Instanciation d'un nouveau site `Session` contenant la boucle d'optimisation et la primitive utilisée pour échantillonner le circuit\n",
        "3. Une fois qu'un ensemble optimal de paramètres a été trouvé, exécuter le circuit une dernière fois pour obtenir une distribution finale qui sera utilisée dans l'étape de post-traitement.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "00b2b0f1-9bad-4ad3-b93e-5cbf40395dbf",
      "metadata": {},
      "source": [
        "<span id=\"define-circuit-with-initial-parameters\" />\n",
        "\n",
        "#### Définir le circuit avec les paramètres initiaux\n",
        "\n",
        "Nous commençons avec des paramètres choisis arbitrairement.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "afa5747f-44dc-4e41-a875-7b6f896f13e2",
      "metadata": {},
      "outputs": [],
      "source": [
        "initial_gamma = np.pi\n",
        "initial_beta = np.pi / 2\n",
        "init_params = [initial_gamma, initial_beta]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b867f1b0-7196-4d34-9b28-e3fb1de8221c",
      "metadata": {},
      "source": [
        "<span id=\"define-backend-and-execution-primitive\" />\n",
        "\n",
        "#### Définir le backend et la primitive d'exécution\n",
        "\n",
        "Utilisez les **primitives de la bibliothèque « IBM Quantum »** pour interagir avec les backends de « IBM® ». Ces deux primitives sont « Sampler » et « Estimator »; le choix de la primitive dépend du type de mesure que vous souhaitez effectuer sur l'ordinateur quantique. Pour minimiser la fonction de coût « $H_C$ », utilisez l'estimateur « Estimator », car la mesure de cette fonction de coût correspond simplement à l'espérance de « $\\langle H_C \\rangle$ ».\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "36c789f2",
      "metadata": {},
      "source": [
        "<span id=\"run\" />\n",
        "\n",
        "#### Exécuter\n",
        "\n",
        "Les primitives offrent une variété de [modes d'exécution](/docs/guides/execution-modes) pour planifier les charges de travail sur les dispositifs quantiques, et un flux de travail QAOA s'exécute de manière itérative dans une session.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "122b7dc8-8d8a-45f2-813e-6199905d765b",
      "metadata": {},
      "source": [
        "![\"Mode d'exécution](https://quantum.cloud.ibm.com/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/execution-mode.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fff58deb",
      "metadata": {},
      "source": [
        "Vous pouvez introduire la fonction de coût basée sur l'échantillonneur dans la routine de minimisation SciPy pour trouver les paramètres optimaux.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "ff947109-cddc-4d3c-9119-2c729df73115",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func_estimator(params, ansatz, hamiltonian, estimator):\n",
        "    # transform the observable defined on virtual qubits to\n",
        "    # an observable defined on all physical qubits\n",
        "    isa_hamiltonian = hamiltonian.apply_layout(ansatz.layout)\n",
        "\n",
        "    pub = (ansatz, isa_hamiltonian, params)\n",
        "    job = estimator.run([pub])\n",
        "\n",
        "    results = job.result()[0]\n",
        "    cost = results.data.evs\n",
        "\n",
        "    objective_func_vals.append(cost)\n",
        "\n",
        "    return cost"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "f5f46775",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            " message: Optimization terminated successfully.\n",
            " success: True\n",
            "  status: 1\n",
            "     fun: -0.6557925874481715\n",
            "       x: [ 2.873e+00  9.414e-01]\n",
            "    nfev: 21\n",
            "   maxcv: 0.0\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import Session, EstimatorV2\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "objective_func_vals = []  # Global variable\n",
        "with Session(backend=backend) as session:\n",
        "    # If using qiskit-ibm-runtime<0.24.0, change `mode=` to `session=`\n",
        "    estimator = EstimatorV2(mode=session)\n",
        "    estimator.options.default_shots = 1000\n",
        "\n",
        "    # Set simple error suppression/mitigation options\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "\n",
        "    result = minimize(\n",
        "        cost_func_estimator,\n",
        "        init_params,\n",
        "        args=(candidate_circuit, cost_hamiltonian, estimator),\n",
        "        method=\"COBYLA\",\n",
        "        tol=1e-2,\n",
        "    )\n",
        "    print(result)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ad878b62",
      "metadata": {},
      "source": [
        "L'optimiseur a permis de réduire le coût et de trouver de meilleurs paramètres pour le circuit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "f923dd5d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/f923dd5d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(12, 6))\n",
        "plt.plot(objective_func_vals)\n",
        "plt.xlabel(\"Iteration\")\n",
        "plt.ylabel(\"Cost\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e2ea6359",
      "metadata": {},
      "source": [
        "Une fois que vous avez trouvé les paramètres optimaux pour le circuit, vous pouvez assigner ces paramètres et échantillonner la distribution finale obtenue avec les paramètres optimisés. C'est ici que la primitive *Sampler* doit être utilisée, car c'est la distribution de probabilité des mesures de chaînes de bits qui correspond à la coupe optimale du graphe.\n",
        "\n",
        "**Remarque :** il s'agit de préparer un état quantique $\\psi$ dans l'ordinateur et de le mesurer. Une mesure réduira l'état en un seul état de base de calcul - par exemple, `010101110000...` - qui correspond à une solution candidate $x$ à notre problème d'optimisation initial ( $\\max f(x)$ ou $\\min f(x)$ en fonction de la tâche).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "f8dddf5a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/f8dddf5a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 36,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "optimized_circuit = candidate_circuit.assign_parameters(result.x)\n",
        "optimized_circuit.draw(\"mpl\", fold=False, idle_wires=False)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "fd9669cf",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "{12: 0.0652, 31: 0.0089, 4: 0.0085, 13: 0.0731, 26: 0.0256, 28: 0.0246, 17: 0.0405, 25: 0.0591, 20: 0.031, 15: 0.0221, 8: 0.017, 21: 0.0371, 14: 0.0461, 16: 0.0229, 19: 0.0723, 23: 0.0199, 22: 0.0478, 18: 0.0708, 24: 0.0165, 6: 0.0525, 7: 0.0155, 5: 0.0245, 3: 0.0231, 29: 0.0121, 30: 0.0062, 10: 0.0363, 1: 0.0097, 9: 0.042, 27: 0.0094, 11: 0.0349, 0: 0.0129, 2: 0.0119}\n"
          ]
        }
      ],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "\n",
        "# If using qiskit-ibm-runtime<0.24.0, change `mode=` to `backend=`\n",
        "sampler = SamplerV2(mode=backend)\n",
        "\n",
        "# Set simple error suppression/mitigation options\n",
        "sampler.options.dynamical_decoupling.enable = True\n",
        "sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "sampler.options.twirling.enable_gates = True\n",
        "sampler.options.twirling.num_randomizations = \"auto\"\n",
        "\n",
        "pub = (optimized_circuit,)\n",
        "job = sampler.run([pub], shots=int(1e4))\n",
        "counts_int = job.result()[0].data.meas.get_int_counts()\n",
        "counts_bin = job.result()[0].data.meas.get_counts()\n",
        "shots = sum(counts_int.values())\n",
        "final_distribution_int = {key: val / shots for key, val in counts_int.items()}\n",
        "final_distribution_bin = {key: val / shots for key, val in counts_bin.items()}\n",
        "print(final_distribution_int)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2c89613f",
      "metadata": {},
      "source": [
        "<span id=\"35-step-4-post-process-return-result-in-classical-format\" />\n",
        "\n",
        "### 3.5 Étape 4. Post-traitement, renvoyer le résultat dans un format classique\n",
        "\n",
        "L'étape de post-traitement interprète le résultat de l'échantillonnage afin de fournir une solution au problème initial. Dans ce cas, vous vous intéressez à la chaîne de bits ayant la probabilité la plus élevée, car elle détermine le découpage optimal. Les symétries du problème permettent quatre solutions possibles, et le processus d'échantillonnage renverra l'une d'entre elles avec une probabilité légèrement supérieure, mais vous pouvez voir dans la distribution graphique ci-dessous que quatre des chaînes de bits sont nettement plus probables que les autres.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "d4f7fc70-883f-4b6b-8e92-2fc4afbbea46",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Result bitstring: [1, 0, 1, 1, 0]\n"
          ]
        }
      ],
      "source": [
        "# auxiliary functions to sample most likely bitstring\n",
        "def to_bitstring(integer, num_bits):\n",
        "    result = np.binary_repr(integer, width=num_bits)\n",
        "    return [int(digit) for digit in result]\n",
        "\n",
        "\n",
        "keys = list(final_distribution_int.keys())\n",
        "values = list(final_distribution_int.values())\n",
        "most_likely = keys[np.argmax(np.abs(values))]\n",
        "most_likely_bitstring = to_bitstring(most_likely, len(graph))\n",
        "most_likely_bitstring.reverse()\n",
        "\n",
        "print(\"Result bitstring:\", most_likely_bitstring)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "32a3020e-c1ea-4aff-988c-d7910a690fa8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/32a3020e-c1ea-4aff-988c-d7910a690fa8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "matplotlib.rcParams.update({\"font.size\": 10})\n",
        "final_bits = final_distribution_bin\n",
        "values = np.abs(list(final_bits.values()))\n",
        "top_4_values = sorted(values, reverse=True)[:4]\n",
        "positions = []\n",
        "for value in top_4_values:\n",
        "    positions.append(np.where(values == value)[0])\n",
        "fig = plt.figure(figsize=(11, 6))\n",
        "ax = fig.add_subplot(1, 1, 1)\n",
        "plt.xticks(rotation=45)\n",
        "plt.title(\"Result Distribution\")\n",
        "plt.xlabel(\"Bitstrings (reversed)\")\n",
        "plt.ylabel(\"Probability\")\n",
        "ax.bar(list(final_bits.keys()), list(final_bits.values()), color=\"tab:grey\")\n",
        "for p in positions:\n",
        "    ax.get_children()[p[0].item()].set_color(\"tab:purple\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6cfcb278",
      "metadata": {},
      "source": [
        "<span id=\"visualize-best-cut\" />\n",
        "\n",
        "#### Visualiser la meilleure coupe\n",
        "\n",
        "À partir de la chaîne de bits optimale, vous pouvez ensuite visualiser cette coupe sur le graphique original.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "22a48124-e6b4-4144-bee1-f01fa4c7ccbb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/courses/utility-scale-quantum-computing/variational-quantum-algorithms/extracted-outputs/22a48124-e6b4-4144-bee1-f01fa4c7ccbb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "colors = [\"tab:grey\" if i == 0 else \"tab:purple\" for i in most_likely_bitstring]\n",
        "mpl_draw(graph, node_size=600, pos=pos, with_labels=True, labels=str, node_color=colors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c4803ab",
      "metadata": {},
      "source": [
        "Et calculer la valeur de la coupe. La solution n'est pas optimale en raison du bruit (la valeur de coupure de la solution optimale est de 5).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "id": "7208ee7d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The value of the cut is: 5\n"
          ]
        }
      ],
      "source": [
        "from typing import Sequence\n",
        "\n",
        "\n",
        "def evaluate_sample(x: Sequence[int], graph: rx.PyGraph) -> float:\n",
        "    assert len(x) == len(\n",
        "        list(graph.nodes())\n",
        "    ), \"The length of x must coincide with the number of nodes in the graph.\"\n",
        "    return sum(\n",
        "        x[u] * (1 - x[v]) + x[v] * (1 - x[u]) for u, v in list(graph.edge_list())\n",
        "    )\n",
        "\n",
        "\n",
        "cut_value = evaluate_sample(most_likely_bitstring, graph)\n",
        "print(\"The value of the cut is:\", cut_value)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1450be7e",
      "metadata": {},
      "source": [
        "Ceci conclut le tutoriel sur l'AQAO à petite échelle.\n",
        "Vous apprendrez comment adapter le QAOA à l'échelle d'un service public dans la \"Partie 2 : passer à l'échelle supérieure\" du tutoriel sur l ['algorithme d'optimisation approximative quantique](/docs/tutorials/quantum-approximate-optimization-algorithm).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "id": "2a4f85ab",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'2.0.2'"
            ]
          },
          "execution_count": 42,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Check Qiskit version\n",
        "import qiskit\n",
        "\n",
        "qiskit.__version__"
      ]
    },
    {
      "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"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}