{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "eb419bc5-908c-4f0d-a4ff-c4d13f04e332",
      "metadata": {
        "tags": []
      },
      "source": [
        "---\n",
        "title: \"Estimation de l'énergie de l'état fondamental de la chaîne de Heisenberg avec VQE\"\n",
        "description: \"Créez, déployez et exécutez un modèle Qiskit permettant de simuler une chaîne de Heisenberg et d'estimer l'énergie de son état fondamental.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore hyperparameters, forall, nabla, nparams */}\n",
        "\n",
        "<span id=\"ground-state-energy-estimation-of-the-heisenberg-chain-with-vqe\" />\n",
        "\n",
        "# Estimation de l'énergie de l'état fondamental de la chaîne de Heisenberg avec VQE\n",
        "\n",
        "*Estimation de la durée d'exécution : 37 minutes sur un processeur Heron (REMARQUE : il s'agit uniquement d'une estimation. (La durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e28ad544",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Résultats d'apprentissage\n",
        "\n",
        "À l'issue de ce tutoriel, vous devriez être en mesure de comprendre les points suivants :\n",
        "\n",
        "* Comment modéliser une chaîne de spins de Heisenberg sous forme d'hamiltonien quantique à l'aide de Qiskit\n",
        "* Comment utiliser l'optimiseur SPSA pour estimer l'énergie de l'état fondamental d'un système quantique\n",
        "* Comment exécuter des workflows variationnels sur le matériel quantique d’ IBM® à l’aide des primitives et des sessions d’ IBM Quantum\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prérequis\n",
        "\n",
        "Nous vous recommandons de vous familiariser avec les sujets suivants :\n",
        "\n",
        "* [Notions de base sur l’information quantique](/learning/courses/basics-of-quantum-information)\n",
        "* [Introduction aux modèles Qiskit](/docs/guides/intro-to-patterns)\n",
        "* [Conception d'algorithmes variationnels](/learning/courses/variational-algorithm-design)\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "La chaîne de spins de Heisenberg est l'un des modèles les plus étudiés en physique de la matière condensée et en magnétisme quantique. Il décrit un réseau unidimensionnel de spins quantiques en interaction, dans lequel les spins voisins sont couplés par des interactions d'échange. L'hamiltonien du modèle de Heisenberg isotrope soumis à un champ magnétique externe est donné par :\n",
        "\n",
        "$H = \\sum_{\\langle i,j \\rangle} \\left( J_x X_i X_j + J_y Y_i Y_j + J_z Z_i Z_j \\right) + \\sum_{i} h_i Z_i,$\n",
        "\n",
        "où $X_i$, $Y_i$ et $Z_i$ sont les opérateurs de Pauli agissant sur le site $i$, la somme $\\langle i,j \\rangle$ porte sur les paires de voisins immédiats, $J_x = J_y = J_z = 0.5$ sont les constantes de couplage d'échange (isotropes dans ce tutoriel), et $h_i$ représente un champ magnétique externe dépendant du site. Dans ce tutoriel, les valeurs du champ magnétique sont échantillonnées de manière aléatoire sur l'intervalle $[-1, 1]$. Il convient de noter que, dans l'implémentation ci-dessous, l'ensemble des paires de « voisins les plus proches » est déterminé par le couplage natif du backend matériel entre les premiers qubits de l' $N$, qui peuvent ne pas former une chaîne linéaire stricte selon la topologie du dispositif.\n",
        "\n",
        "Comprendre l'énergie de l'état fondamental de cet hamiltonien revêt une importance fondamentale en physique. L'état fondamental contient des informations sur les transitions de phase quantiques, la structure d'intrication et l'ordre magnétique. En règle générale, le calcul de l'énergie exacte de l'état fondamental devient impossible à réaliser lorsque le nombre de spins augmente, car la dimension de l'espace de Hilbert croît de manière exponentielle selon la formule $2^N$ pour $N$ spins. Cela en fait un candidat tout désigné pour la simulation quantique.\n",
        "\n",
        "Le Variational Quantum Eigensolver (VQE) est un algorithme hybride quantique-classique conçu pour estimer l'énergie de l'état fondamental d'un hamiltonien. Le procédé consiste à préparer un état quantique paramétré $|\\psi(\\theta)\\rangle$ (appelé « ansatz ») sur un ordinateur quantique, puis à mesurer la valeur d'espérance $\\langle \\psi(\\theta) | H | \\psi(\\theta) \\rangle$. Un optimiseur classique ajuste ensuite les paramètres de manière itérative $\\theta$ afin de minimiser cette énergie, en s'appuyant sur le principe variationnel qui garantit que l'énergie mesurée constitue toujours une borne supérieure de l'énergie réelle de l'état fondamental.\n",
        "\n",
        "Dans ce tutoriel, nous utilisons l'ansatz `efficient_su2` de la bibliothèque de circuits de Qiskit, qui construit des couches de rotations de qubits uniques et de portes d'intrication. L'optimisation est réalisée à l'aide de l'algorithme SPSA (Simultaneous Perturbation Stochastic Approximation), particulièrement adapté au matériel quantique sujet au bruit, car il estime les gradients en n'utilisant que deux évaluations de fonction par itération, quel que soit le nombre de paramètres.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bc52f763",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :\n",
        "\n",
        "* Qiskit SDK v2.0 ou version ultérieure, avec prise en charge [de la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.44 ou version ultérieure (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a46e9e3e",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "e7754922",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "from typing import Sequence\n",
        "\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.primitives import BaseEstimatorV2\n",
        "from qiskit.circuit.library import XGate\n",
        "from qiskit.circuit.library import efficient_su2\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.transpiler.passes.scheduling import (\n",
        "    ALAPScheduleAnalysis,\n",
        "    PadDynamicalDecoupling,\n",
        ")\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2\n",
        "\n",
        "\n",
        "def visualize_results(results):\n",
        "    plt.plot(results[\"cost_history\"], lw=2)\n",
        "    plt.xlabel(\"Number of function evaluations\")\n",
        "    plt.ylabel(\"Energy\")\n",
        "    plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "890d9c81",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-example\" />\n",
        "\n",
        "## Exemple à petite échelle\n",
        "\n",
        "Dans cette section, nous passons en revue chaque étape du modèle Qiskit à petite échelle, en expliquant les éléments clés au fur et à mesure que nous mettons en place le flux de travail.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "132fb15f-10b4-4d7e-83d8-f512a6f675d1",
      "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",
        "* Entrée : Nombre de tours\n",
        "* Sortie : Ansatz et hamiltonien modélisant la chaîne de Heisenberg\n",
        "\n",
        "Élaborez un ansatz et un hamiltonien permettant de modéliser une chaîne de Heisenberg à 10 spins. Au cours de cette étape, nous allons construire un hamiltonien de Heisenberg à 10 spins sur la carte de couplage du backend le moins sollicité et préparer l'ansatz `efficient_su2` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "7e8d2f10-f1d6-4ec2-bac9-9db23499c9e1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/7e8d2f10-f1d6-4ec2-bac9-9db23499c9e1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_spins = 10\n",
        "ansatz = efficient_su2(num_qubits=num_spins, reps=2)\n",
        "\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=num_spins, simulator=False\n",
        ")\n",
        "\n",
        "coupling = backend.target.build_coupling_map()\n",
        "reduced_coupling = coupling.reduce(list(range(num_spins)))\n",
        "\n",
        "edge_list = reduced_coupling.graph.edge_list()\n",
        "ham_list = []\n",
        "\n",
        "for edge in edge_list:\n",
        "    ham_list.append((\"ZZ\", edge, 0.5))\n",
        "    ham_list.append((\"YY\", edge, 0.5))\n",
        "    ham_list.append((\"XX\", edge, 0.5))\n",
        "\n",
        "for qubit in reduced_coupling.physical_qubits:\n",
        "    ham_list.append((\"Z\", [qubit], np.random.random() * 2 - 1))\n",
        "\n",
        "hamiltonian = SparsePauliOp.from_sparse_list(ham_list, num_qubits=num_spins)\n",
        "\n",
        "ansatz.draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ab79119b-5e56-49d8-a20e-1c8e665baec0",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique\n",
        "\n",
        "* Entrée : Circuit abstrait, observable\n",
        "* Résultat : Circuit cible et observable, optimisés pour la QPU sélectionnée\n",
        "\n",
        "Utilisez la fonction `generate_preset_pass_manager` de Qiskit pour générer automatiquement une routine d'optimisation pour notre circuit par rapport à la QPU sélectionnée. Nous choisissons `optimization_level=3`, qui offre le plus haut niveau d'optimisation des gestionnaires de passage prédéfinis. Nous incluons également `ALAPScheduleAnalysis` et `PadDynamicalDecoupling` pour supprimer les erreurs de décohérence.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a0a5f1c8-5c31-4d9f-ae81-37bd67271d44",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/a0a5f1c8-5c31-4d9f-ae81-37bd67271d44-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(optimization_level=3, target=target)\n",
        "pm.scheduling = PassManager(\n",
        "    [\n",
        "        ALAPScheduleAnalysis(durations=target.durations()),\n",
        "        PadDynamicalDecoupling(\n",
        "            durations=target.durations(),\n",
        "            dd_sequence=[XGate(), XGate()],\n",
        "            pulse_alignment=target.pulse_alignment,\n",
        "        ),\n",
        "    ]\n",
        ")\n",
        "isa_ansatz = pm.run(ansatz)\n",
        "isa_observable = hamiltonian.apply_layout(isa_ansatz.layout)\n",
        "isa_ansatz.draw(\"mpl\", scale=0.6, style=\"iqp\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9e889d0b-30b5-4e6b-84c9-d1f096abf132",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n",
        "* Entrée : Circuit cible et observable\n",
        "* Sortie : Résultats de l'optimisation\n",
        "\n",
        "Réduire au minimum l'énergie estimée de l'état fondamental du système en optimisant les paramètres du circuit. Utilisez la primitive `Estimator` « IBM Quantum » pour évaluer la fonction de coût pendant l'optimisation.\n",
        "\n",
        "Comme nous avons optimisé le circuit pour le backend à l'étape 2, nous pouvons éviter la transpilation sur le serveur Quantum Compute en définissant et `skip_transpilation=True` en transmettant le circuit optimisé. Pour cette démonstration, nous allons effectuer l'exécution sur un QPU à l'aide de primitives « IBM Quantum ». Pour utiliser des primitives basées `qiskit` sur des vecteurs d'état, remplacez le bloc de code utilisant les primitives de type « IBM Quantum » par le bloc commenté.\n",
        "\n",
        "Dans ce tutoriel, nous utilisons l'approximation stochastique par perturbation simultanée (SPSA), qui est un optimiseur basé sur le gradient. Nous allons maintenant vous en présenter brièvement les principes et vous fournir le code permettant de mettre en œuvre l'algorithme SPSA à l'aide de Qiskit v2.0.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64f97e6f",
      "metadata": {},
      "source": [
        "<span id=\"introducing-spsa\" />\n",
        "\n",
        "### Présentation de SPSA\n",
        "\n",
        "L'approximation stochastique par perturbation simultanée (SPSA) [\\[1\\]](#references) est un algorithme d'optimisation qui permet d'approximer l'intégralité du vecteur gradient en n'utilisant que deux appels de fonction à chaque itération. Soit $f:\\mathbb{R}^p\\rightarrow \\mathbb{R}$ la fonction de coût avec $p$ les paramètres à optimiser, et $x_i\\in \\mathbb{R}^p$ le vecteur de paramètres à l'étape $i^{th}$ de l'itération. Pour calculer le gradient, on crée un vecteur aléatoire $\\Delta_i$ de taille $p$, où chaque élément $\\Delta_{ij}$, $\\forall$ $j\\in \\{1,2,...,p\\}$ est échantillonné de manière uniforme à partir de $\\{-1, 1\\}$. Ensuite, chaque élément du vecteur aléatoire $\\Delta_i$ est multiplié par une petite valeur $c_i$ afin de créer une perturbation aléatoire. Le gradient est alors estimé comme suit :\n",
        "\n",
        "$[\\nabla f(x_i)]_j \\approx \\frac{f(x_i + c_i \\Delta_i) - f(x_i - c_i \\Delta_i)}{2c_i\\Delta_{ij}}.$\n",
        "\n",
        "Intuitivement, étant donné qu'une perturbation aléatoire est appliquée lors de l'estimation du gradient, on peut s'attendre à ce que de légers écarts par rapport aux valeurs exactes d' $f$, dus au bruit, puissent être tolérés et pris en compte. En effet, l'algorithme SPSA est particulièrement réputé pour sa résistance au bruit et ne nécessite que deux appels matériels par itération. Il s'agit donc de l'un des optimiseurs les plus prisés pour la mise en œuvre d'algorithmes variationnels.\n",
        "\n",
        "Dans ce tutoriel, les hyperparamètres pour l'itération de l' $i^{th}$, $a_i$ et $c_i$, sont calculés comme suit :\n",
        "\n",
        "$a_i = \\frac{a}{(A + i + 1)^\\alpha} \\quad \\text{and} \\quad c_i = \\frac{c}{(i+1)^\\gamma},$\n",
        "\n",
        "où les valeurs des constantes sont respectivement de l'ordre de $A = 30$, $\\alpha = 0.9$, $a = 0.3$, $c = 0.1$ et $\\gamma = 0.4$. Ces valeurs sont tirées de [\\[2\\]](#references). Un réglage adéquat des hyperparamètres est nécessaire pour obtenir de bonnes performances avec le SPSA.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "73a9352c",
      "metadata": {},
      "outputs": [],
      "source": [
        "def spsa(\n",
        "    fun, x0, args=(), A=30, alpha=0.9, a=0.3, c=0.1, gamma=0.4, maxiter=100\n",
        "):\n",
        "    nparams = len(x0)\n",
        "    x = np.copy(x0)\n",
        "\n",
        "    for i in range(maxiter):\n",
        "        a_i = a / (A + i + 1) ** alpha\n",
        "        c_i = c / (i + 1) ** gamma\n",
        "        delta_i = np.random.choice([-1, 1], nparams)\n",
        "\n",
        "        # two hardware calls\n",
        "        eval_1 = fun(x + c_i * delta_i, *args)\n",
        "        eval_2 = fun(x - c_i * delta_i, *args)\n",
        "\n",
        "        # compute the gradient and update the parameters\n",
        "        grad = (eval_1 - eval_2) / (2 * c_i) * np.reciprocal(delta_i)\n",
        "        x = x - a_i * grad\n",
        "\n",
        "    return x"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32ca3b6a",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(\n",
        "    params: Sequence,\n",
        "    ansatz: QuantumCircuit,\n",
        "    hamiltonian: SparsePauliOp,\n",
        "    estimator: BaseEstimatorV2,\n",
        "    cost_history_dict: dict,\n",
        ") -> float:\n",
        "    \"\"\"Ground state energy evaluation.\"\"\"\n",
        "    energy = (\n",
        "        estimator.run([(ansatz, hamiltonian, [params])]).result()[0].data.evs\n",
        "    )\n",
        "\n",
        "    cost_history_dict[\"iters\"] += 1\n",
        "    cost_history_dict[\"prev_vector\"] = list(params)\n",
        "    cost_history_dict[\"cost_history\"].append(float(energy[0]))\n",
        "\n",
        "    print(\n",
        "        f\"Fx Iters. done: {cost_history_dict['iters']} [Current cost: {round(energy[0], 5)}]\",\n",
        "        end=\"\\r\",\n",
        "    )\n",
        "\n",
        "    return energy\n",
        "\n",
        "\n",
        "def solve(x0, isa_ansatz, isa_observable, maxiter=150):\n",
        "    cost_history_dict = {\n",
        "        \"prev_vector\": None,\n",
        "        \"iters\": 0,\n",
        "        \"cost_history\": [],\n",
        "        \"y_min\": None,\n",
        "    }\n",
        "\n",
        "    # Evaluate the problem using a QPU via `qiskit-ibm-runtime`\n",
        "    with Session(backend=backend) as session:\n",
        "        estimator = EstimatorV2(mode=session)\n",
        "        estimator.skip_transpilation = True\n",
        "        estimator.options.environment.job_tags = [\"TUT_HSVQE\"]\n",
        "        x_opt = spsa(\n",
        "            cost_func,\n",
        "            x0=x0,\n",
        "            args=(isa_ansatz, isa_observable, estimator, cost_history_dict),\n",
        "            maxiter=maxiter,\n",
        "        )\n",
        "\n",
        "        y_min = cost_func(\n",
        "            x_opt, isa_ansatz, isa_observable, estimator, cost_history_dict\n",
        "        )\n",
        "\n",
        "    return y_min, cost_history_dict"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "f418b372",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "num_params = ansatz.num_parameters\n",
        "params = 2 * np.pi * np.random.random(num_params)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3bf42923",
      "metadata": {},
      "source": [
        "C'est ici que nous définissons le `maxiter = 50`. Il convient de noter que, comme chaque itération nécessite deux appels à la fonction pour calculer le gradient, le nombre total d'appels à la fonction sera de $2 \\times \\text{maxiter}$. La valeur de `maxiter` peut être augmentée à une valeur supérieure pour obtenir une meilleure estimation de l'énergie.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "1732ce37",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Fx Iters. done: 101 [Current cost: -3.03843]"
          ]
        }
      ],
      "source": [
        "maxiter = 50\n",
        "spsa_min, spsa_history = solve(\n",
        "    params, isa_ansatz, isa_observable, maxiter=maxiter\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "33abbb3f-6245-4610-a05d-e2bc4cc551f0",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Étape 4 : Post-traitement et restitution du résultat dans le format classique souhaité\n",
        "\n",
        "* Données d'entrée : estimations de l'énergie de l'état fondamental pendant l'optimisation\n",
        "* Résultat : Énergie estimée de l'état fondamental\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "e5b58771-d543-4e75-9746-fbc7b28e4360",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Estimated ground state energy: [-3.03842968]\n"
          ]
        }
      ],
      "source": [
        "print(f\"Estimated ground state energy: {spsa_min}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "ecd7762a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/spin-chain-vqe/extracted-outputs/ecd7762a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "results = {\n",
        "    \"spsa\": spsa_history,\n",
        "}\n",
        "\n",
        "visualize_results(spsa_history)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3f4171f",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Exemple de matériel à grande échelle\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "746e6133",
      "metadata": {},
      "source": [
        "Ce tutoriel ne comprend pas d'exemple concret à grande échelle. À mesure que le nombre de qubits augmente, l'optimisation VQE se heurte à des difficultés majeures en raison du phénomène [de plateau stérile](/learning/courses/variational-algorithm-design/optimization-loops#barren-plateaus) : le gradient de la fonction de coût s'annule de manière exponentielle avec la taille du système, rendant l'optimisation pratiquement impossible pour les grands circuits. Si l'on ajoute à cela le bruit matériel, cela signifie que l'extension de la VQE à des chaînes de spin plus longues ne permet pas d'obtenir des résultats reproductibles de manière fiable. Pour découvrir des approches permettant de surmonter ces limites, consultez la section « Étapes suivantes » ci-dessous.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f14492c",
      "metadata": {},
      "source": [
        "<span id=\"challenge\" />\n",
        "\n",
        "## Le défi\n",
        "\n",
        "Maintenant que vous disposez d'une implémentation VQE fonctionnelle pour la chaîne de Heisenberg, essayez ce qui suit :\n",
        "\n",
        "1. **Jouez sur la profondeur de l'approximation :** modifiez le `reps` paramètre dans `efficient_su2` (par exemple, essayez `reps=1` et `reps=3`). Dans quelle mesure la profondeur de l'approximation influe-t-elle sur l'estimation de l'énergie de l'état fondamental et sur la vitesse de convergence? À partir de quel moment constatez-vous une baisse de rendement ou une instabilité?\n",
        "2. **Régler les hyperparamètres SPSA :** ajuster les paramètres du programme de taux d'apprentissage (`a`, `c`, `alpha`, `gamma`, `A`) et observer leur incidence sur la convergence. Pouvez-vous trouver une configuration qui converge plus rapidement que les paramètres par défaut utilisés ici?\n",
        "3. **Comparez les topologies de couplage :** au lieu d'utiliser la carte de couplage native du backend, essayez de construire une simple chaîne linéaire de voisins les plus proches et comparez les résultats. En quoi la connectivité du matériel physique influe-t-elle sur la profondeur du circuit transpilé et sur l'estimation finale de la consommation énergétique?\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "217a9379",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Références\n",
        "\n",
        "\\[1] Spall, J. C. (2002). Mise en œuvre de l'algorithme de perturbation simultanée pour l'optimisation stochastique.\n",
        "IEEE Transactions on Aerospace and Electronic Systems, 34(3), p. 817-823.\n",
        "\n",
        "\\[2] Sahin, M. Emre, et al. (2025). Machine Learning Qiskit : une bibliothèque open source destinée aux tâches d'apprentissage automatique quantique à grande échelle sur du matériel quantique et des simulateurs classiques. arXiv:2505.17756.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8aefe54c",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Etapes suivantes\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Recommandations\">\n",
        "  Si ce travail vous a intéressé, les documents suivants pourraient vous intéresser :\n",
        "\n",
        "  * **Essayez la diagonalisation quantique par échantillonnage (SQD) :** comme le montre ce tutoriel, la VQE se heurte à des difficultés à grande échelle en raison des plateaux stériles et de la charge importante liée aux mesures. IBM a mis au point [la « diagonalisation quantique par échantillonnage » (SQD)](/docs/addons/qiskit-addon-sqd) comme alternative plus évolutive. Contrairement à la VQE, la SQD évite totalement l'optimisation variationnelle; à la place, un ordinateur quantique génère des échantillons et un ordinateur classique projette l'hamiltonien sur un sous-espace engendré par ces échantillons, puis le diagonalise. Cette méthode fournit une borne supérieure à l'énergie de l'état fondamental avec un nombre nettement moindre de mesures et sans risque de plateaux stériles. Suivez le [tutoriel SQD](/docs/tutorials/sample-based-quantum-diagonalization) pour découvrir cette approche en pratique.\n",
        "  * **Découvrez le cours « Algorithmes de diagonalisation quantique » :** approfondissez vos connaissances sur les méthodes VQE et SQD, y compris leurs avantages et leurs inconvénients, dans le cadre du cours «[ Algorithmes de diagonalisation quantique](/learning/courses/quantum-diagonalization-algorithms) » disponible sur IBM Quantum Learning.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "hours": 1.5,
    "qpuSeconds": 2220
  },
  "nbformat": 4,
  "nbformat_minor": 5
}