{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "509f7bd9-b597-4d23-b3af-0a76a7b4d33d",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Diagonalisation quantique de Krylov des hamiltoniens de réseau\"\n",
        "description: \"Implémenter l'algorithme de diagonalisation quantique de Krylov (KQD) dans le contexte des modèles Qiskit.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore prefactors */}\n",
        "\n",
        "<span id=\"krylov-quantum-diagonalization-of-lattice-hamiltonians\" />\n",
        "\n",
        "# Diagonalisation quantique de Krylov des hamiltoniens de réseau\n",
        "\n",
        "*Estimation de l'utilisation : 20 minutes sur un Héron r2 (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "921c7b04-5b5d-4cfc-aba0-15a61334e619",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Arrière-plan\n",
        "\n",
        "Ce tutoriel montre comment mettre en œuvre l'algorithme de diagonalisation quantique de Krylov (KQD) dans le contexte des modèles Qiskit. Vous découvrirez d'abord la théorie qui sous-tend l'algorithme, puis vous assisterez à une démonstration de son exécution sur une QPU.\n",
        "\n",
        "Toutes disciplines confondues, nous nous intéressons à l'apprentissage des propriétés de l'état fondamental des systèmes quantiques. Il s'agit par exemple de comprendre la nature fondamentale des particules et des forces, de prévoir et de comprendre le comportement de matériaux complexes et de comprendre les interactions et les réactions biochimiques. En raison de la croissance exponentielle de l'espace de Hilbert et des corrélations qui apparaissent dans les systèmes intriqués, les algorithmes classiques peinent à résoudre ce problème pour des systèmes quantiques de taille croissante. À une extrémité du spectre se trouve l'approche existante qui tire parti du matériel quantique en se concentrant sur les méthodes quantiques variationnelles (par exemple, l' [eigensolver quantique variationnel](/docs/tutorials/spin-chain-vqe) ). Ces techniques se heurtent à des difficultés avec les dispositifs actuels en raison du nombre élevé d'appels de fonction requis dans le processus d'optimisation, ce qui ajoute un surcoût important en termes de ressources une fois que des techniques avancées d'atténuation des erreurs sont introduites, limitant ainsi leur efficacité aux systèmes de petite taille. À l'autre extrémité du spectre, il existe des méthodes quantiques tolérantes aux fautes avec des garanties de performance (par exemple, l' [estimation quantique de la phase](https://arxiv.org/abs/quant-ph/0604193) ), qui nécessitent des circuits profonds qui ne peuvent être exécutés que sur un dispositif tolérant aux fautes. Pour ces raisons, nous présentons ici un algorithme quantique basé sur des méthodes de sous-espace (telles que décrites dans cet [article](https://arxiv.org/abs/2312.00178) ), l'algorithme de diagonalisation quantique de Krylov (KQD). Cet algorithme fonctionne bien à grande échelle [\\[1\\]](#references) sur le matériel quantique existant, offre des [garanties de performance](https://arxiv.org/abs/2110.07492) similaires à celles de l'estimation de phase, est compatible avec des techniques avancées d'atténuation des erreurs et pourrait fournir des résultats qui sont classiquement inaccessibles.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5f698c82-95ca-4dc4-b9fa-d6e741e2c02c",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Exigences\n",
        "\n",
        "Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :\n",
        "\n",
        "* Qiskit SDK v2.0 ou plus tard, avec prise en charge de [la visualisation](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.22 ou plus tard ( `pip install qiskit-ibm-runtime` )\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c44956e4-47ab-4b0f-9d6d-553080110062",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configuration\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ce7d5adc-3ef2-4654-b865-14d5141ce41a",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import scipy as sp\n",
        "import matplotlib.pylab as plt\n",
        "from typing import Union, List\n",
        "import itertools as it\n",
        "import copy\n",
        "from sympy import Matrix\n",
        "import warnings\n",
        "\n",
        "warnings.filterwarnings(\"ignore\")\n",
        "\n",
        "from qiskit.quantum_info import SparsePauliOp, Pauli, StabilizerState\n",
        "from qiskit.circuit import Parameter, IfElseOp\n",
        "from qiskit import QuantumCircuit, QuantumRegister\n",
        "from qiskit.circuit.library import PauliEvolutionGate\n",
        "from qiskit.synthesis import LieTrotter\n",
        "from qiskit.transpiler import Target, CouplingMap\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "\n",
        "from qiskit_ibm_runtime import (\n",
        "    QiskitRuntimeService,\n",
        "    EstimatorV2 as Estimator,\n",
        ")\n",
        "\n",
        "\n",
        "def solve_regularized_gen_eig(\n",
        "    h: np.ndarray,\n",
        "    s: np.ndarray,\n",
        "    threshold: float,\n",
        "    k: int = 1,\n",
        "    return_dimn: bool = False,\n",
        ") -> Union[float, List[float]]:\n",
        "    \"\"\"\n",
        "    Method for solving the generalized eigenvalue problem with regularization\n",
        "\n",
        "    Args:\n",
        "        h (numpy.ndarray):\n",
        "            The effective representation of the matrix in the Krylov subspace\n",
        "        s (numpy.ndarray):\n",
        "            The matrix of overlaps between vectors of the Krylov subspace\n",
        "        threshold (float):\n",
        "            Cut-off value for the eigenvalue of s\n",
        "        k (int):\n",
        "            Number of eigenvalues to return\n",
        "        return_dimn (bool):\n",
        "            Whether to return the size of the regularized subspace\n",
        "\n",
        "    Returns:\n",
        "        lowest k-eigenvalue(s) that are the solution of the\n",
        "        regularized generalized eigenvalue problem\n",
        "\n",
        "\n",
        "    \"\"\"\n",
        "    s_vals, s_vecs = sp.linalg.eigh(s)\n",
        "    s_vecs = s_vecs.T\n",
        "    good_vecs = np.array(\n",
        "        [vec for val, vec in zip(s_vals, s_vecs) if val > threshold]\n",
        "    )\n",
        "    h_reg = good_vecs.conj() @ h @ good_vecs.T\n",
        "    s_reg = good_vecs.conj() @ s @ good_vecs.T\n",
        "    if k == 1:\n",
        "        if return_dimn:\n",
        "            return sp.linalg.eigh(h_reg, s_reg)[0][0], len(good_vecs)\n",
        "        else:\n",
        "            return sp.linalg.eigh(h_reg, s_reg)[0][0]\n",
        "    else:\n",
        "        if return_dimn:\n",
        "            return sp.linalg.eigh(h_reg, s_reg)[0][:k], len(good_vecs)\n",
        "        else:\n",
        "            return sp.linalg.eigh(h_reg, s_reg)[0][:k]\n",
        "\n",
        "\n",
        "def single_particle_gs(H_op, n_qubits):\n",
        "    \"\"\"\n",
        "    Find the ground state of the single particle(excitation) sector\n",
        "    \"\"\"\n",
        "    H_x = []\n",
        "    for p, coeff in H_op.to_list():\n",
        "        H_x.append(set([i for i, v in enumerate(Pauli(p).x) if v]))\n",
        "\n",
        "    H_z = []\n",
        "    for p, coeff in H_op.to_list():\n",
        "        H_z.append(set([i for i, v in enumerate(Pauli(p).z) if v]))\n",
        "\n",
        "    H_c = H_op.coeffs\n",
        "\n",
        "    print(\"n_sys_qubits\", n_qubits)\n",
        "\n",
        "    n_exc = 1\n",
        "    sub_dimn = int(sp.special.comb(n_qubits + 1, n_exc))\n",
        "    print(\"n_exc\", n_exc, \", subspace dimension\", sub_dimn)\n",
        "\n",
        "    few_particle_H = np.zeros((sub_dimn, sub_dimn), dtype=complex)\n",
        "\n",
        "    # list all of the possible sets of n_exc indices of 1s in\n",
        "    # n_exc-particle states\n",
        "    sparse_vecs = [\n",
        "        set(vec) for vec in it.combinations(range(n_qubits + 1), r=n_exc)\n",
        "    ]\n",
        "\n",
        "    m = 0\n",
        "    for i, i_set in enumerate(sparse_vecs):\n",
        "        for j, j_set in enumerate(sparse_vecs):\n",
        "            m += 1\n",
        "\n",
        "            if len(i_set.symmetric_difference(j_set)) <= 2:\n",
        "                for p_x, p_z, coeff in zip(H_x, H_z, H_c):\n",
        "                    if i_set.symmetric_difference(j_set) == p_x:\n",
        "                        sgn = ((-1j) ** len(p_x.intersection(p_z))) * (\n",
        "                            (-1) ** len(i_set.intersection(p_z))\n",
        "                        )\n",
        "                    else:\n",
        "                        sgn = 0\n",
        "\n",
        "                    few_particle_H[i, j] += sgn * coeff\n",
        "\n",
        "    gs_en = min(np.linalg.eigvalsh(few_particle_H))\n",
        "    print(\"single particle ground state energy: \", gs_en)\n",
        "    return gs_en"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "76a1c616-a749-48a2-b5fd-8beff760760b",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## Étape 1 : Mettre en correspondance les entrées classiques avec un problème quantique\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a166e2a1-3003-4799-9c15-59f5e78b6ea8",
      "metadata": {},
      "source": [
        "<span id=\"the-krylov-space\" />\n",
        "\n",
        "### L'espace de Krylov\n",
        "\n",
        "L'espace de Krylov $\\mathcal{K}^r$ d'ordre $r$ est l'espace couvert par les vecteurs obtenus en multipliant les puissances supérieures d'une matrice $A$, jusqu'à $r-1$, avec un vecteur de référence $\\vert v \\rangle$.\n",
        "\n",
        "$$\n",
        "\\mathcal{K}^r = \\left\\{ \\vert v \\rangle, A \\vert v \\rangle, A^2 \\vert v \\rangle, ..., A^{r-1} \\vert v \\rangle \\right\\}\n",
        "$$\n",
        "\n",
        "Si la matrice $A$ est l'hamiltonien $H$, nous appellerons l'espace correspondant l'espace de Krylov des puissances $\\mathcal{K}_P$. Dans le cas où $A$ est l'opérateur d'évolution temporelle généré par l'hamiltonien $U=e^{-iHt}$, nous appellerons l'espace l'espace de Krylov unitaire $\\mathcal{K}_U$. Le sous-espace de Krylov puissance que nous utilisons classiquement ne peut pas être généré directement sur un ordinateur quantique car $H$ n'est pas un opérateur unitaire. Au lieu de cela, nous pouvons utiliser l'opérateur d'évolution temporelle $U = e^{-iHt}$, dont on peut montrer qu'il offre des [garanties de convergence](https://arxiv.org/abs/2110.07492) similaires à celles de la méthode de la puissance. Les puissances de $U$ deviennent alors des pas de temps différents $U^k = e^{-iH(kt)}$.\n",
        "\n",
        "$$\n",
        "\\mathcal{K}_U^r = \\left\\{ \\vert \\psi \\rangle, U \\vert \\psi \\rangle, U^2 \\vert \\psi \\rangle, ..., U^{r-1} \\vert \\psi \\rangle \\right\\}\n",
        "$$\n",
        "\n",
        "Voir l'annexe pour une dérivation détaillée de la manière dont l'espace de Krylov unitaire permet de représenter avec précision les états propres à faible énergie.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "5573ca7e-ab16-4488-88b3-d8d1eba9e20c",
      "metadata": {},
      "source": [
        "<span id=\"krylov-quantum-diagonalization-algorithm\" />\n",
        "\n",
        "### Algorithme de diagonalisation quantique de Krylov\n",
        "\n",
        "Étant donné un hamiltonien $H$ que nous souhaitons diagonaliser, nous considérons d'abord l'espace de Krylov unitaire correspondant $\\mathcal{K}_U$. L'objectif est de trouver une représentation compacte de l'hamiltonien dans $\\mathcal{K}_U$, que nous appellerons $\\tilde{H}$. Les éléments de la matrice de $\\tilde{H}$, la projection de l'hamiltonien dans l'espace de Krylov, peuvent être calculés en calculant les valeurs d'espérance suivantes\n",
        "\n",
        "$$\n",
        "\\tilde{H}_{mn} = \\langle \\psi_m \\vert H \\vert \\psi_n \\rangle =\n",
        "$$\n",
        "\n",
        "$$\n",
        "= \\langle \\psi \\vert  e^{i H t_m}   H e^{-i H t_n} \\vert \\psi \\rangle\n",
        "$$\n",
        "\n",
        "$$\n",
        "= \\langle \\psi \\vert  e^{i H m dt}   H e^{-i H n dt} \\vert \\psi \\rangle\n",
        "$$\n",
        "\n",
        "Où $\\vert \\psi_n \\rangle = e^{-i H t_n} \\vert \\psi \\rangle$ sont les vecteurs de l'espace de Krylov unitaire et $t_n = n dt$ sont les multiples du pas de temps $dt$ choisi. Sur un ordinateur quantique, le calcul de chaque élément de la matrice peut être effectué à l'aide de n'importe quel algorithme permettant d'obtenir un chevauchement entre les états quantiques. Ce tutoriel se concentre sur le test de Hadamard. Étant donné que le $\\mathcal{K}_U$ a la dimension $r$, l'hamiltonien projeté dans le sous-espace aura les dimensions $r \\times r$. Si $r$ est suffisamment petit (en général, $r<<100$ est suffisant pour obtenir une convergence des estimations des énergies propres), nous pouvons facilement diagonaliser l'hamiltonien projeté $\\tilde{H}$. Cependant, nous ne pouvons pas diagonaliser directement $\\tilde{H}$ en raison de la non-orthogonalité des vecteurs de l'espace de Krylov. Nous devrons mesurer leurs chevauchements et construire une matrice $\\tilde{S}$\n",
        "\n",
        "$$\n",
        "\\tilde{S}_{mn} = \\langle \\psi_m \\vert \\psi_n \\rangle\n",
        "$$\n",
        "\n",
        "Cela nous permet de résoudre le problème des valeurs propres dans un espace non orthogonal (également appelé problème généralisé des valeurs propres)\n",
        "\n",
        "$$\n",
        "\\tilde{H} \\ \\vec{c} = E \\ \\tilde{S} \\ \\vec{c}\n",
        "$$\n",
        "\n",
        "On peut alors obtenir des estimations des valeurs propres et des états propres de $H$ en examinant ceux de $\\tilde{H}$. Par exemple, l'estimation de l'énergie de l'état fondamental est obtenue en prenant la plus petite valeur propre $c$ et l'état fondamental à partir du vecteur propre correspondant $\\vec{c}$. Les coefficients de $\\vec{c}$ déterminent la contribution des différents vecteurs qui couvrent $\\mathcal{K}_U$.\n",
        "\n",
        "![fig1.png](https://quantum.cloud.ibm.com/docs/images/tutorials/krylov-subspace-diagonalization/fc662b76-8ad7-4a6c-8c49-5f08c125aee8.avif)\n",
        "\n",
        "La figure montre une représentation du circuit du test de Hadamard modifié, une méthode utilisée pour calculer le chevauchement entre différents états quantiques. Pour chaque élément de la matrice $\\tilde{H}_{i,j}$, un test de Hadamard entre les états $\\vert \\psi_i \\rangle$, $\\vert \\psi_j \\rangle$ est effectué. Ceci est mis en évidence dans la figure par le schéma de couleurs pour les éléments de la matrice et les opérations $\\text{Prep} \\; \\psi_i$, $\\text{Prep} \\; \\psi_j$ correspondantes. Ainsi, un ensemble de tests de Hadamard pour toutes les combinaisons possibles de vecteurs de l'espace de Krylov est nécessaire pour calculer tous les éléments de la matrice de l'hamiltonien projeté $\\tilde{H}$. Le fil supérieur du circuit de test de Hadamard est un qubit d'ancilla qui est mesuré soit dans la base X, soit dans la base Y. Sa valeur d'espérance détermine la valeur du chevauchement entre les états. Le fil du bas représente tous les qubits de l'hamiltonien du système. L'opération $\\text{Prep} \\; \\psi_i$ prépare le qubit du système dans l'état $\\vert \\psi_i \\rangle$ contrôlé par l'état du qubit ancillaire (de même pour $\\text{Prep} \\; \\psi_j$ ) et l'opération $P$ représente la décomposition de Pauli de l'hamiltonien du système $H = \\sum_i P_i$. Une dérivation plus détaillée des opérations calculées par le test de Hadamard est donnée ci-dessous.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1a6d7a4f-6c1f-4069-93d1-f5b670645d7f",
      "metadata": {},
      "source": [
        "<span id=\"define-hamiltonian\" />\n",
        "\n",
        "#### Définir Hamiltonien\n",
        "\n",
        "Considérons l'hamiltonien de Heisenberg pour $N$ qubits sur une chaîne linéaire : $H= \\sum_{i,j}^N X_i X_j + Y_i Y_j - J Z_i Z_j$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "82163249-bafd-4bc6-9741-20a4077971a7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[('ZZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IZZIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIZZIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIZZIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIZZIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIZZIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIZZIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIZZIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIZZIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIZZIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIZZIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIZZIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIZZIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIZZIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIZZIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIZZIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIZZIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIZZIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIZZIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIZZIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIZZIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIZZIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIZZIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIZZIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIZZIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIZZIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIZZII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIZZI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ', 1), ('XXIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IXXIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIXXIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIXXIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIXXIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIXXIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIXXIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIXXIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIXXIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIXXIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIXXIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIXXIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIXXIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIXXIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIXXIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIXXIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIXXIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIXXIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIXXIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIXXIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIXXIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIXXIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIXXIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIXXIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIXXIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIXXIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIXXII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIXXI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIXX', 1), ('YYIIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IYYIIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIYYIIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIYYIIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIYYIIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIYYIIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIYYIIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIYYIIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIYYIIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIYYIIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIYYIIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIYYIIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIYYIIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIYYIIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIYYIIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIYYIIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIYYIIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIYYIIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIYYIIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIYYIIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIYYIIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIYYIIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIYYIIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIYYIIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIYYIIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIYYIII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIYYII', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIYYI', 1), ('IIIIIIIIIIIIIIIIIIIIIIIIIIIIYY', 1)]\n"
          ]
        }
      ],
      "source": [
        "# Define problem Hamiltonian.\n",
        "n_qubits = 30\n",
        "J = 1  # coupling strength for ZZ interaction\n",
        "\n",
        "# Define the Hamiltonian:\n",
        "H_int = [[\"I\"] * n_qubits for _ in range(3 * (n_qubits - 1))]\n",
        "for i in range(n_qubits - 1):\n",
        "    H_int[i][i] = \"Z\"\n",
        "    H_int[i][i + 1] = \"Z\"\n",
        "for i in range(n_qubits - 1):\n",
        "    H_int[n_qubits - 1 + i][i] = \"X\"\n",
        "    H_int[n_qubits - 1 + i][i + 1] = \"X\"\n",
        "for i in range(n_qubits - 1):\n",
        "    H_int[2 * (n_qubits - 1) + i][i] = \"Y\"\n",
        "    H_int[2 * (n_qubits - 1) + i][i + 1] = \"Y\"\n",
        "H_int = [\"\".join(term) for term in H_int]\n",
        "H_tot = [(term, J) if term.count(\"Z\") == 2 else (term, 1) for term in H_int]\n",
        "\n",
        "# Get operator\n",
        "H_op = SparsePauliOp.from_list(H_tot)\n",
        "print(H_tot)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f26578ba-24ad-42f3-baad-c348d3c05699",
      "metadata": {},
      "source": [
        "<span id=\"set-parameters-for-the-algorithm\" />\n",
        "\n",
        "#### Définir les paramètres de l'algorithme\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8d376c0e-fb7a-4a41-838a-ae041d5c9afa",
      "metadata": {},
      "source": [
        "Nous choisissons de manière heuristique une valeur pour le pas de temps `dt` (sur la base des limites supérieures de la norme hamiltonienne). Ref [\\[2\\]](#references) a montré qu'un pas de temps suffisamment petit est $\\pi/\\vert \\vert H \\vert \\vert$, et qu'il est préférable jusqu'à un certain point de sous-estimer cette valeur plutôt que de la surestimer, car une surestimation peut permettre aux contributions des états à haute énergie de corrompre même l'état optimal dans l'espace de Krylov. D'autre part, si l'on choisit $dt$ comme étant trop petit, le conditionnement du sous-espace de Krylov est moins bon, car les vecteurs de base de Krylov diffèrent moins d'un pas de temps à l'autre.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "963dd2e9-f4ea-456d-b4ed-e3d05a114082",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.float64(0.10833078115826875)"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Get Hamiltonian restricted to single-particle states\n",
        "single_particle_H = np.zeros((n_qubits, n_qubits))\n",
        "for i in range(n_qubits):\n",
        "    for j in range(i + 1):\n",
        "        for p, coeff in H_op.to_list():\n",
        "            p_x = Pauli(p).x\n",
        "            p_z = Pauli(p).z\n",
        "            if all(\n",
        "                p_x[k] == ((i == k) + (j == k)) % 2 for k in range(n_qubits)\n",
        "            ):\n",
        "                sgn = (\n",
        "                    (-1j) ** sum(p_z[k] and p_x[k] for k in range(n_qubits))\n",
        "                ) * ((-1) ** p_z[i])\n",
        "            else:\n",
        "                sgn = 0\n",
        "            single_particle_H[i, j] += sgn * coeff\n",
        "for i in range(n_qubits):\n",
        "    for j in range(i + 1, n_qubits):\n",
        "        single_particle_H[i, j] = np.conj(single_particle_H[j, i])\n",
        "\n",
        "# Set dt according to spectral norm\n",
        "dt = np.pi / np.linalg.norm(single_particle_H, ord=2)\n",
        "dt"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d2bcc2b9-ca7c-4147-8e6b-0f15208297ce",
      "metadata": {},
      "source": [
        "Et définir d'autres paramètres de l'algorithme. Pour les besoins de ce tutoriel, nous nous contenterons d'utiliser un espace de Krylov à cinq dimensions seulement, ce qui est assez restrictif.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "ddde76b8-446f-4cd5-bc4b-8f00e2ec726c",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Set parameters for quantum Krylov algorithm\n",
        "krylov_dim = 5  # size of Krylov subspace\n",
        "num_trotter_steps = 6\n",
        "dt_circ = dt / num_trotter_steps"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3c0dcc08-6963-4e14-a50c-21c5dd7f8ade",
      "metadata": {},
      "source": [
        "<span id=\"state-preparation\" />\n",
        "\n",
        "#### Préparation de l'État\n",
        "\n",
        "Choisissez un état de référence $\\vert \\psi \\rangle$ qui présente un certain chevauchement avec l'état fondamental. Pour cet hamiltonien, nous utilisons l'état a avec une excitation dans le qubit du milieu $\\vert 00..010...00 \\rangle$ comme état de référence.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "70161afe-8ace-4642-894a-cd21ed77a3b9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/krylov-quantum-diagonalization/extracted-outputs/70161afe-8ace-4642-894a-cd21ed77a3b9-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc_state_prep = QuantumCircuit(n_qubits)\n",
        "qc_state_prep.x(int(n_qubits / 2) + 1)\n",
        "qc_state_prep.draw(\"mpl\", scale=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1786de6b-476f-47ea-9c20-3464d3cfbbdb",
      "metadata": {},
      "source": [
        "<span id=\"time-evolution\" />\n",
        "\n",
        "#### Évolution dans le temps\n",
        "\n",
        "Nous pouvons réaliser l'opérateur d'évolution temporelle généré par un hamiltonien donné : $U=e^{-iHt}$ via l' [approximation de Lie-Trotter](/docs/api/qiskit/qiskit.synthesis.LieTrotter).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "a23b9e9c-5dc8-447f-8c73-b4fa01630c8f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<qiskit.circuit.instructionset.InstructionSet at 0x11eef9be0>"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "t = Parameter(\"t\")\n",
        "\n",
        "## Create the time-evo op circuit\n",
        "evol_gate = PauliEvolutionGate(\n",
        "    H_op, time=t, synthesis=LieTrotter(reps=num_trotter_steps)\n",
        ")\n",
        "\n",
        "qr = QuantumRegister(n_qubits)\n",
        "qc_evol = QuantumCircuit(qr)\n",
        "qc_evol.append(evol_gate, qargs=qr)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "41c2cc43-51a2-42c3-88ee-a3b7b30eabb4",
      "metadata": {},
      "source": [
        "<span id=\"hadamard-test\" />\n",
        "\n",
        "#### test de Hadamard\n",
        "\n",
        "![fig2.png](https://quantum.cloud.ibm.com/docs/images/tutorials/krylov-subspace-diagonalization/c5263851-6067-4ca2-8e0c-a835631cdc7f.avif)\n",
        "\n",
        "$$\n",
        "\\begin{equation*}\n",
        "    |0\\rangle|0\\rangle^N \\quad\\longrightarrow\\quad \\frac{1}{\\sqrt{2}}\\Big(|0\\rangle + |1\\rangle \\Big)|0\\rangle^N \\quad\\longrightarrow\\quad \\frac{1}{\\sqrt{2}}\\Big(|0\\rangle|0\\rangle^N+|1\\rangle |\\psi_i\\rangle\\Big) \\quad\\longrightarrow\\quad \\frac{1}{\\sqrt{2}}\\Big(|0\\rangle |0\\rangle^N+|1\\rangle P |\\psi_i\\rangle\\Big) \\quad\\longrightarrow\\quad\\frac{1}{\\sqrt{2}}\\Big(|0\\rangle |\\psi_j\\rangle+|1\\rangle P|\\psi_i\\rangle\\Big)\n",
        "\\end{equation*}\n",
        "$$\n",
        "\n",
        "Où $P$ est l'un des termes de la décomposition de l'hamiltonien $H=\\sum P$ et $\\text{Prep} \\; \\psi_i$, $\\text{Prep} \\; \\psi_j$ sont des opérations contrôlées qui préparent $|\\psi_i\\rangle$, $|\\psi_j\\rangle$ les vecteurs de l'espace de Krylov unitaire, avec $|\\psi_k\\rangle = e^{-i H k dt } \\vert \\psi \\rangle = e^{-i H k dt } U_{\\psi} \\vert 0 \\rangle^N$. Pour mesurer $X$, appliquez d'abord $H$...\n",
        "\n",
        "$$\n",
        "\\begin{equation*}\n",
        "    \\longrightarrow\\quad\\frac{1}{2}|0\\rangle\\Big( |\\psi_j\\rangle + P|\\psi_i\\rangle\\Big) + \\frac{1}{2}|1\\rangle\\Big(|\\psi_j\\rangle - P|\\psi_i\\rangle\\Big)\n",
        "\\end{equation*}\n",
        "$$\n",
        "\n",
        "... puis mesurer :\n",
        "\n",
        "$$\n",
        "\\begin{equation*}\n",
        "\\begin{split}\n",
        "    \\Rightarrow\\quad\\langle X\\rangle &= \\frac{1}{4}\\Bigg(\\Big\\|| \\psi_j\\rangle + P|\\psi_i\\rangle \\Big\\|^2-\\Big\\||\\psi_j\\rangle - P|\\psi_i\\rangle\\Big\\|^2\\Bigg) \\\\\n",
        "    &= \\text{Re}\\Big[\\langle\\psi_j| P|\\psi_i\\rangle\\Big].\n",
        "\\end{split}\n",
        "\\end{equation*}\n",
        "$$\n",
        "\n",
        "D'après l'identité $|a + b\\|^2 = \\langle a + b | a + b \\rangle = \\|a\\|^2 + \\|b\\|^2 + 2\\text{Re}\\langle a | b \\rangle$. De même, en mesurant $Y$, on obtient\n",
        "\n",
        "$$\n",
        "\\begin{equation*}\n",
        "    \\langle Y\\rangle = \\text{Im}\\Big[\\langle\\psi_j| P|\\psi_i\\rangle\\Big].\n",
        "\\end{equation*}\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "7c1efca7-7db9-43a9-bcb7-053ba274d6f6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Circuit for calculating the real part of the overlap in S via Hadamard test\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/krylov-quantum-diagonalization/extracted-outputs/7c1efca7-7db9-43a9-bcb7-053ba274d6f6-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "## Create the time-evo op circuit\n",
        "evol_gate = PauliEvolutionGate(\n",
        "    H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)\n",
        ")\n",
        "\n",
        "## Create the time-evo op dagger circuit\n",
        "evol_gate_d = PauliEvolutionGate(\n",
        "    H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)\n",
        ")\n",
        "evol_gate_d = evol_gate_d.inverse()\n",
        "\n",
        "# Put pieces together\n",
        "qc_reg = QuantumRegister(n_qubits)\n",
        "qc_temp = QuantumCircuit(qc_reg)\n",
        "qc_temp.compose(qc_state_prep, inplace=True)\n",
        "for _ in range(num_trotter_steps):\n",
        "    qc_temp.append(evol_gate, qargs=qc_reg)\n",
        "for _ in range(num_trotter_steps):\n",
        "    qc_temp.append(evol_gate_d, qargs=qc_reg)\n",
        "qc_temp.compose(qc_state_prep.inverse(), inplace=True)\n",
        "\n",
        "# Create controlled version of the circuit\n",
        "controlled_U = qc_temp.to_gate().control(1)\n",
        "\n",
        "# Create hadamard test circuit for real part\n",
        "qr = QuantumRegister(n_qubits + 1)\n",
        "qc_real = QuantumCircuit(qr)\n",
        "qc_real.h(0)\n",
        "qc_real.append(controlled_U, list(range(n_qubits + 1)))\n",
        "qc_real.h(0)\n",
        "\n",
        "print(\n",
        "    \"Circuit for calculating the real part of the overlap in S via Hadamard test\"\n",
        ")\n",
        "qc_real.draw(\"mpl\", fold=-1, scale=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c72fd5e5-a586-498e-bbc1-0f48ecea49d5",
      "metadata": {},
      "source": [
        "Le circuit de test de Hadamard peut être un circuit profond une fois que nous le décomposons en portes natives (ce qui augmentera encore si nous tenons compte de la topologie de l'appareil)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "06f22c05-6e1c-4540-9dac-884b3400a50e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Number of layers of 2Q operations 112753\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    \"Number of layers of 2Q operations\",\n",
        "    qc_real.decompose(reps=2).depth(lambda x: x[0].num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "8e15903d-d4a8-4fbe-8f9e-e960e686629e",
      "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",
        "<span id=\"efficient-hadamard-test\" />\n",
        "\n",
        "### Test de Hadamard efficace\n",
        "\n",
        "Nous pouvons optimiser les circuits profonds pour le test de Hadamard que nous avons obtenu en introduisant certaines approximations et en nous appuyant sur certaines hypothèses concernant l'hamiltonien du modèle. Par exemple, considérons le circuit suivant pour le test de Hadamard :\n",
        "\n",
        "![fig3.png](https://quantum.cloud.ibm.com/docs/images/tutorials/krylov-subspace-diagonalization/35b13797-5a46-486c-b50e-97c205cc9747.avif)\n",
        "\n",
        "Supposons que nous puissions calculer classiquement $E_0$, la valeur propre de $|0\\rangle^N$ sous l'hamiltonien $H$. Cette condition est remplie lorsque l'hamiltonien préserve la symétrie U(1). Bien que cette hypothèse puisse sembler forte, il existe de nombreux cas où l'on peut supposer qu'il existe un état de vide (dans ce cas, il s'agit de l'état $|0\\rangle^N$ ) qui n'est pas affecté par l'action de l'hamiltonien. C'est le cas, par exemple, des hamiltoniens de chimie qui décrivent une molécule stable (où le nombre d'électrons est conservé).\n",
        "Étant donné que la porte $\\text{Prep} \\; \\psi$, prépare l'état de référence souhaité $\\ket{psi} = \\text{Prep} \\; \\psi \\ket{0} = e^{-i H 0 dt} U_{\\psi} \\ket{0}$, par exemple, préparer l'état HF pour la chimie $\\text{Prep} \\; \\psi$ serait un produit de NOT à un seul qubit, de sorte que controlled- $\\text{Prep} \\; \\psi$ est simplement un produit de CNOT.\n",
        "Le circuit ci-dessus met alors en œuvre l'état suivant avant la mesure :\n",
        "\n",
        "$$\n",
        "\\begin{equation}\n",
        "\\begin{split}\n",
        "    \\ket{0} \\ket{0}^N\\xrightarrow{H}&\\frac{1}{\\sqrt{2}}\n",
        "    \\left(\n",
        "    \\ket{0}\\ket{0}^N+ \\ket{1} \\ket{0}^N\n",
        "    \\right)\\\\\n",
        "    \\xrightarrow{\\text{1-ctrl-init}}&\\frac{1}{\\sqrt{2}}\\left(|0\\rangle|0\\rangle^N+|1\\rangle|\\psi\\rangle\\right)\\\\\n",
        "    \\xrightarrow{U}&\\frac{1}{\\sqrt{2}}\\left(e^{i\\phi}\\ket{0}\\ket{0}^N+\\ket{1} U\\ket{\\psi}\\right)\\\\\n",
        "    \\xrightarrow{\\text{0-ctrl-init}}&\\frac{1}{\\sqrt{2}}\n",
        "    \\left(\n",
        "    e^{i\\phi}\\ket{0} \\ket{\\psi}\n",
        "    +\\ket{1} U\\ket{\\psi}\n",
        "    \\right)\\\\\n",
        "    =&\\frac{1}{2}\n",
        "    \\left(\n",
        "    \\ket{+}\\left(e^{i\\phi}\\ket{\\psi}+U\\ket{\\psi}\\right)\n",
        "    +\\ket{-}\\left(e^{i\\phi}\\ket{\\psi}-U\\ket{\\psi}\\right)\n",
        "    \\right)\\\\\n",
        "    =&\\frac{1}{2}\n",
        "    \\left(\n",
        "    \\ket{+i}\\left(e^{i\\phi}\\ket{\\psi}-iU\\ket{\\psi}\\right)\n",
        "    +\\ket{-i}\\left(e^{i\\phi}\\ket{\\psi}+iU\\ket{\\psi}\\right)\n",
        "    \\right)\n",
        "\\end{split}\n",
        "\\end{equation}\n",
        "$$\n",
        "\n",
        "où nous avons utilisé le déphasage simulable classique $ U\\ket{0}^N = e^{i\\phi}\\ket{0}^N$ dans la troisième ligne. Par conséquent, les valeurs attendues sont obtenues comme suit\n",
        "\n",
        "$$\n",
        "\\begin{equation}\n",
        "\\begin{split}\n",
        "    \\langle X\\otimes P\\rangle&=\\frac{1}{4}\n",
        "    \\Big(\n",
        "    \\left(e^{-i\\phi}\\bra{\\psi}+\\bra{\\psi}U^\\dagger\\right)P\\left(e^{i\\phi}\\ket{\\psi}+U\\ket{\\psi}\\right)\n",
        "    \\\\\n",
        "    &\\qquad-\\left(e^{-i\\phi}\\bra{\\psi}-\\bra{\\psi}U^\\dagger\\right)P\\left(e^{i\\phi}\\ket{\\psi}-U\\ket{\\psi}\\right)\n",
        "    \\Big)\\\\\n",
        "    &=\\text{Re}\\left[e^{-i\\phi}\\bra{\\psi}PU\\ket{\\psi}\\right],\n",
        "\\end{split}\n",
        "\\end{equation}\n",
        "$$\n",
        "\n",
        "$$\n",
        "\\begin{equation}\n",
        "\\begin{split}\n",
        "    \\langle Y\\otimes P\\rangle&=\\frac{1}{4}\n",
        "    \\Big(\n",
        "    \\left(e^{-i\\phi}\\bra{\\psi}+i\\bra{\\psi}U^\\dagger\\right)P\\left(e^{i\\phi}\\ket{\\psi}-iU\\ket{\\psi}\\right)\n",
        "    \\\\\n",
        "    &\\qquad-\\left(e^{-i\\phi}\\bra{\\psi}-i\\bra{\\psi}U^\\dagger\\right)P\\left(e^{i\\phi}\\ket{\\psi}+iU\\ket{\\psi}\\right)\n",
        "    \\Big)\\\\\n",
        "    &=\\text{Im}\\left[e^{-i\\phi}\\bra{\\psi}PU\\ket{\\psi}\\right].\n",
        "\\end{split}\n",
        "\\end{equation}\n",
        "$$\n",
        "\n",
        "En utilisant ces hypothèses, nous avons pu écrire les valeurs attendues des opérateurs d'intérêt avec moins d'opérations contrôlées. En fait, nous ne devons mettre en œuvre que la préparation contrôlée de l'état $\\text{Prep} \\; \\psi$ et non les évolutions temporelles contrôlées. En reformulant notre calcul comme indiqué ci-dessus, nous pourrons réduire considérablement la profondeur des circuits résultants.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2e29da5f-b5f5-4b9e-96fc-3fa7ab698398",
      "metadata": {},
      "source": [
        "<span id=\"decompose-time-evolution-operator-with-trotter-decomposition\" />\n",
        "\n",
        "### Décomposer l'opérateur d'évolution temporelle avec la décomposition de Trotter\n",
        "\n",
        "Au lieu d'implémenter exactement l'opérateur d'évolution temporelle, nous pouvons utiliser la décomposition de Trotter pour en implémenter une approximation. En répétant plusieurs fois une décomposition de Trotter d'un certain ordre, nous réduisons encore l'erreur introduite par l'approximation. Dans ce qui suit, nous construisons directement l'implémentation de Trotter de la manière la plus efficace pour le graphe d'interaction de l'hamiltonien que nous considérons (interactions avec les voisins les plus proches uniquement). En pratique, nous insérons les rotations de Pauli $R_{xx}$, $R_{yy}$, $R_{zz}$ avec un angle paramétré $t$ qui correspond à la mise en œuvre approximative de $e^{-i (XX + YY + ZZ) t}$. Étant donné la différence de définition des rotations de Pauli et l'évolution temporelle que nous essayons de mettre en œuvre, nous devrons utiliser le paramètre $2*dt$ pour obtenir une évolution temporelle de $dt$. En outre, nous inversons l'ordre des opérations pour un nombre impair de répétitions des étapes de Trotter, ce qui est fonctionnellement équivalent mais permet de synthétiser des opérations adjacentes dans une seule unité $SU(2)$. Cela donne un circuit beaucoup moins profond que celui obtenu en utilisant la fonctionnalité générique `PauliEvolutionGate()` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "267716dc-fa23-41bd-abe4-6d4e0499a0f4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/krylov-quantum-diagonalization/extracted-outputs/267716dc-fa23-41bd-abe4-6d4e0499a0f4-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "t = Parameter(\"t\")\n",
        "\n",
        "# Create instruction for rotation about XX+YY-ZZ:\n",
        "Rxyz_circ = QuantumCircuit(2)\n",
        "Rxyz_circ.rxx(t, 0, 1)\n",
        "Rxyz_circ.ryy(t, 0, 1)\n",
        "Rxyz_circ.rzz(t, 0, 1)\n",
        "Rxyz_instr = Rxyz_circ.to_instruction(label=\"RXX+YY+ZZ\")\n",
        "\n",
        "interaction_list = [\n",
        "    [[i, i + 1] for i in range(0, n_qubits - 1, 2)],\n",
        "    [[i, i + 1] for i in range(1, n_qubits - 1, 2)],\n",
        "]  # linear chain\n",
        "\n",
        "qr = QuantumRegister(n_qubits)\n",
        "trotter_step_circ = QuantumCircuit(qr)\n",
        "for i, color in enumerate(interaction_list):\n",
        "    for interaction in color:\n",
        "        trotter_step_circ.append(Rxyz_instr, interaction)\n",
        "    if i < len(interaction_list) - 1:\n",
        "        trotter_step_circ.barrier()\n",
        "reverse_trotter_step_circ = trotter_step_circ.reverse_ops()\n",
        "\n",
        "qc_evol = QuantumCircuit(qr)\n",
        "for step in range(num_trotter_steps):\n",
        "    if step % 2 == 0:\n",
        "        qc_evol = qc_evol.compose(trotter_step_circ)\n",
        "    else:\n",
        "        qc_evol = qc_evol.compose(reverse_trotter_step_circ)\n",
        "\n",
        "qc_evol.decompose().draw(\"mpl\", fold=-1, scale=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0a9c3a4d-a678-41e4-b9dd-995fe34341fb",
      "metadata": {},
      "source": [
        "<span id=\"use-an-optimized-circuit-for-state-preparation\" />\n",
        "\n",
        "### Utiliser un circuit optimisé pour la préparation d'état\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "70411715-eed3-4cf5-961d-06a6f1e04efc",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/krylov-quantum-diagonalization/extracted-outputs/70411715-eed3-4cf5-961d-06a6f1e04efc-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "control = 0\n",
        "excitation = int(n_qubits / 2) + 1\n",
        "controlled_state_prep = QuantumCircuit(n_qubits + 1)\n",
        "controlled_state_prep.cx(control, excitation)\n",
        "controlled_state_prep.draw(\"mpl\", fold=-1, scale=0.5)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2421ff11-d97a-488a-a382-a652df8c94d6",
      "metadata": {},
      "source": [
        "<span id=\"template-circuits-for-calculating-matrix-elements-of-$tilde{s}$-and-$tilde{h}$-via-hadamard-test\" />\n",
        "\n",
        "### Circuits modèles pour calculer les éléments matriciels de $\\tilde{S}$ et $\\tilde{H}$ via le test de Hadamard\n",
        "\n",
        "La seule différence entre les circuits utilisés dans le test de Hadamard sera la phase de l'opérateur d'évolution temporelle et les observables mesurés. Nous pouvons donc préparer un circuit modèle qui représente le circuit générique pour le test de Hadamard, avec des espaces réservés pour les portes qui dépendent de l'opérateur d'évolution temporelle.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "2f102112-4ddc-41ea-999c-db5863bc77ac",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Parameters for the template circuits\n",
        "parameters = []\n",
        "for idx in range(1, krylov_dim):\n",
        "    parameters.append(2 * dt_circ * (idx))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "33ec7c29-904e-4445-a654-405214349a4d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/krylov-quantum-diagonalization/extracted-outputs/33ec7c29-904e-4445-a654-405214349a4d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Create modified hadamard test circuit\n",
        "qr = QuantumRegister(n_qubits + 1)\n",
        "qc = QuantumCircuit(qr)\n",
        "qc.h(0)\n",
        "qc.compose(controlled_state_prep, list(range(n_qubits + 1)), inplace=True)\n",
        "qc.barrier()\n",
        "qc.compose(qc_evol, list(range(1, n_qubits + 1)), inplace=True)\n",
        "qc.barrier()\n",
        "qc.x(0)\n",
        "qc.compose(\n",
        "    controlled_state_prep.inverse(), list(range(n_qubits + 1)), inplace=True\n",
        ")\n",
        "qc.x(0)\n",
        "\n",
        "qc.decompose().draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "157356c9-06bb-411c-87ef-cd1d2f73be6f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The optimized circuit has 2Q gates depth:  74\n"
          ]
        }
      ],
      "source": [
        "print(\n",
        "    \"The optimized circuit has 2Q gates depth: \",\n",
        "    qc.decompose().decompose().depth(lambda x: x[0].num_qubits == 2),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a59bbc4-72c7-4a55-9a9e-57bb7b469021",
      "metadata": {},
      "source": [
        "Nous avons considérablement réduit la profondeur du test de Hadamard en combinant l'approximation de Trotter et les unitaires non contrôlés\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ca78bfe3-684f-4d3a-b8c2-3d4e55c9ec30",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## Étape 3 : Exécutez à l'aide d' Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a082f023-d752-40e4-b693-c4d0da5ba102",
      "metadata": {},
      "source": [
        "Instanciation du backend et définition des paramètres d'exécution\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0d90e4df-e262-4852-a811-ff6a1d3232ae",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(operational=True, simulator=False)\n",
        "if (\n",
        "    \"if_else\" not in backend.target.operation_names\n",
        "):  # Needed as \"op_name\" could be \"if_else\"\n",
        "    backend.target.add_instruction(IfElseOp, name=\"if_else\")\n",
        "print(backend.name)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b3f7227-e6ef-4a11-93d6-d3696054b9cc",
      "metadata": {},
      "source": [
        "<span id=\"transpiling-to-a-qpu\" />\n",
        "\n",
        "### Transpilation vers un QPU\n",
        "\n",
        "Tout d'abord, sélectionnons des sous-ensembles de la carte de couplage avec des qubits « performants » (le terme « performant » étant ici assez arbitraire, nous voulons surtout éviter les qubits vraiment peu performants) et créons une nouvelle cible pour la transpilation\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "e123fda1-6454-4893-9612-2b8591e8cfb9",
      "metadata": {},
      "outputs": [],
      "source": [
        "target = backend.target\n",
        "cmap = target.build_coupling_map(filter_idle_qubits=True)\n",
        "cmap_list = list(cmap.get_edges())\n",
        "\n",
        "cust_cmap_list = copy.deepcopy(cmap_list)\n",
        "for q in range(target.num_qubits):\n",
        "    meas_err = target[\"measure\"][(q,)].error\n",
        "    t2 = target.qubit_properties[q].t2 * 1e6\n",
        "    if meas_err > 0.02 or t2 < 100:\n",
        "        for q_pair in cmap_list:\n",
        "            if q in q_pair:\n",
        "                try:\n",
        "                    cust_cmap_list.remove(q_pair)\n",
        "                except:\n",
        "                    continue\n",
        "\n",
        "for q in cmap_list:\n",
        "    op_name = list(target.operation_names_for_qargs(q))[0]\n",
        "    twoq_gate_err = target[f\"{op_name}\"][q].error\n",
        "    if twoq_gate_err > 0.005:\n",
        "        for q_pair in cmap_list:\n",
        "            if q == q_pair:\n",
        "                try:\n",
        "                    cust_cmap_list.remove(q)\n",
        "                except:\n",
        "                    continue\n",
        "\n",
        "\n",
        "cust_cmap = CouplingMap(cust_cmap_list)\n",
        "cust_target = Target.from_configuration(\n",
        "    basis_gates=backend.configuration().basis_gates,\n",
        "    coupling_map=cust_cmap,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96f79e35-a265-4de4-b98c-f9c84f58f0d5",
      "metadata": {},
      "source": [
        "Transpilez ensuite le circuit virtuel vers la meilleure disposition physique dans cette nouvelle cible\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "62109c95-79fe-4076-b591-9bd920fd51f4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "depth 52\n",
            "num 2q ops OrderedDict([('rz', 2058), ('sx', 1703), ('cz', 728), ('x', 84), ('barrier', 8)])\n",
            "physical qubits [91, 92, 93, 94, 95, 98, 99, 108, 109, 110, 111, 113, 114, 115, 119, 127, 132, 133, 134, 135, 137, 139, 147, 148, 149, 150, 151, 152, 153, 154, 155]\n"
          ]
        }
      ],
      "source": [
        "basis_gates = list(target.operation_names)\n",
        "pm = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    target=cust_target,\n",
        "    basis_gates=basis_gates,\n",
        ")\n",
        "\n",
        "qc_trans = pm.run(qc)\n",
        "\n",
        "print(\"depth\", qc_trans.depth(lambda x: x[0].num_qubits == 2))\n",
        "print(\"num 2q ops\", qc_trans.count_ops())\n",
        "print(\n",
        "    \"physical qubits\",\n",
        "    sorted(\n",
        "        [\n",
        "            idx\n",
        "            for idx, qb in qc_trans.layout.initial_layout.get_physical_bits().items()\n",
        "            if qb._register.name != \"ancilla\"\n",
        "        ]\n",
        "    ),\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9b9b0170-b96a-47b9-b5ab-5112d02338a3",
      "metadata": {},
      "source": [
        "<span id=\"create-pubs-for-execution-with-estimator\" />\n",
        "\n",
        "### Créer des PUB pour exécution avec Estimator\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "bcfd6693-d1fb-44e4-9b06-c70e6766e877",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Define observables to measure for S\n",
        "observable_S_real = \"I\" * (n_qubits) + \"X\"\n",
        "observable_S_imag = \"I\" * (n_qubits) + \"Y\"\n",
        "\n",
        "observable_op_real = SparsePauliOp(\n",
        "    observable_S_real\n",
        ")  # define a sparse pauli operator for the observable\n",
        "observable_op_imag = SparsePauliOp(observable_S_imag)\n",
        "\n",
        "layout = qc_trans.layout  # get layout of transpiled circuit\n",
        "observable_op_real = observable_op_real.apply_layout(\n",
        "    layout\n",
        ")  # apply physical layout to the observable\n",
        "observable_op_imag = observable_op_imag.apply_layout(layout)\n",
        "observable_S_real = (\n",
        "    observable_op_real.paulis.to_labels()\n",
        ")  # get the label of the physical observable\n",
        "observable_S_imag = observable_op_imag.paulis.to_labels()\n",
        "\n",
        "observables_S = [[observable_S_real], [observable_S_imag]]\n",
        "\n",
        "\n",
        "# Define observables to measure for H\n",
        "# Hamiltonian terms to measure\n",
        "observable_list = []\n",
        "for pauli, coeff in zip(H_op.paulis, H_op.coeffs):\n",
        "    # print(pauli)\n",
        "    observable_H_real = pauli[::-1].to_label() + \"X\"\n",
        "    observable_H_imag = pauli[::-1].to_label() + \"Y\"\n",
        "    observable_list.append([observable_H_real])\n",
        "    observable_list.append([observable_H_imag])\n",
        "\n",
        "layout = qc_trans.layout\n",
        "\n",
        "observable_trans_list = []\n",
        "for observable in observable_list:\n",
        "    observable_op = SparsePauliOp(observable)\n",
        "    observable_op = observable_op.apply_layout(layout)\n",
        "    observable_trans_list.append([observable_op.paulis.to_labels()])\n",
        "\n",
        "observables_H = observable_trans_list\n",
        "\n",
        "\n",
        "# Define a sweep over parameter values\n",
        "params = np.vstack(parameters).T\n",
        "\n",
        "\n",
        "# Estimate the expectation value for all combinations of\n",
        "# observables and parameter values, where the pub result will have\n",
        "# shape (# observables, # parameter values).\n",
        "pub = (qc_trans, observables_S + observables_H, params)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bd27e84-d673-4326-9004-f3be5a65aed3",
      "metadata": {},
      "source": [
        "<span id=\"run-circuits\" />\n",
        "\n",
        "### Circuits de course\n",
        "\n",
        "Les circuits pour $t=0$ sont calculables de manière classique\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "768cd443-2ed8-4ba2-ba9d-98491b36fa54",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "(25+0j)\n"
          ]
        }
      ],
      "source": [
        "qc_cliff = qc.assign_parameters({t: 0})\n",
        "\n",
        "\n",
        "# Get expectation values from experiment\n",
        "S_expval_real = StabilizerState(qc_cliff).expectation_value(\n",
        "    Pauli(\"I\" * (n_qubits) + \"X\")\n",
        ")\n",
        "S_expval_imag = StabilizerState(qc_cliff).expectation_value(\n",
        "    Pauli(\"I\" * (n_qubits) + \"Y\")\n",
        ")\n",
        "\n",
        "# Get expectation values\n",
        "S_expval = S_expval_real + 1j * S_expval_imag\n",
        "\n",
        "H_expval = 0\n",
        "for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):\n",
        "    # Get expectation values from experiment\n",
        "    expval_real = StabilizerState(qc_cliff).expectation_value(\n",
        "        Pauli(pauli[::-1].to_label() + \"X\")\n",
        "    )\n",
        "    expval_imag = StabilizerState(qc_cliff).expectation_value(\n",
        "        Pauli(pauli[::-1].to_label() + \"Y\")\n",
        "    )\n",
        "    expval = expval_real + 1j * expval_imag\n",
        "\n",
        "    # Fill-in matrix elements\n",
        "    H_expval += coeff * expval\n",
        "\n",
        "\n",
        "print(H_expval)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "794edf4c-d539-4864-865d-52a88a450b5c",
      "metadata": {},
      "source": [
        "Exécuter des circuits pour $S$ et $\\tilde{H}$ à l'aide d'Estimator\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9059dc9e-9203-4572-92c4-76e9c85dfed9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Experiment options\n",
        "num_randomizations = 300\n",
        "num_randomizations_learning = 30\n",
        "shots_per_randomization = 100\n",
        "noise_factors = [1, 1.2, 1.4]\n",
        "learning_pair_depths = [0, 4, 24, 48]\n",
        "\n",
        "\n",
        "experimental_opts = {}\n",
        "experimental_opts[\"resilience\"] = {\n",
        "    \"measure_mitigation\": True,\n",
        "    \"measure_noise_learning\": {\n",
        "        \"num_randomizations\": num_randomizations_learning,\n",
        "        \"shots_per_randomization\": shots_per_randomization,\n",
        "    },\n",
        "    \"zne_mitigation\": True,\n",
        "    \"zne\": {\"noise_factors\": noise_factors},\n",
        "    \"layer_noise_learning\": {\n",
        "        \"max_layers_to_learn\": 10,\n",
        "        \"layer_pair_depths\": learning_pair_depths,\n",
        "        \"shots_per_randomization\": shots_per_randomization,\n",
        "        \"num_randomizations\": num_randomizations_learning,\n",
        "    },\n",
        "    \"zne\": {\n",
        "        \"amplifier\": \"pea\",\n",
        "        \"extrapolated_noise_factors\": [0] + noise_factors,\n",
        "    },\n",
        "}\n",
        "experimental_opts[\"twirling\"] = {\n",
        "    \"num_randomizations\": num_randomizations,\n",
        "    \"shots_per_randomization\": shots_per_randomization,\n",
        "    \"strategy\": \"all\",\n",
        "}\n",
        "\n",
        "estimator = Estimator(mode=backend, options=experimental_opts)\n",
        "\n",
        "\n",
        "job = estimator.run([pub])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9524e609-b457-42e9-9de7-8dbd4464ac74",
      "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"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "id": "30d72032-02e4-40e7-af28-cadda2f7f3bd",
      "metadata": {},
      "outputs": [],
      "source": [
        "results = job.result()[0]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8149d431-f069-4567-a0be-0cc4ed8d523b",
      "metadata": {},
      "source": [
        "<span id=\"calculate-effective-hamiltonian-and-overlap-matrices\" />\n",
        "\n",
        "### Calculer les matrices hamiltoniennes et de chevauchement effectives\n",
        "\n",
        "Calculer d'abord la phase accumulée par l'état $\\vert 0 \\rangle$ au cours de l'évolution temporelle incontrôlée\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "id": "34ba8b05-d52c-4b0f-8d28-acaf7a5ddffc",
      "metadata": {},
      "outputs": [],
      "source": [
        "prefactors = [\n",
        "    np.exp(-1j * sum([c for p, c in H_op.to_list() if \"Z\" in p]) * i * dt)\n",
        "    for i in range(1, krylov_dim)\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "36875c8b-36c8-464b-9f0e-7a79e17d5fef",
      "metadata": {},
      "source": [
        "Une fois que nous avons les résultats de l'exécution des circuits, nous pouvons post-traiter les données pour calculer les éléments de la matrice de $S$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "16dd2534-8d5f-40f7-a938-5d519475fd8d",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Assemble S, the overlap matrix of dimension D:\n",
        "S_first_row = np.zeros(krylov_dim, dtype=complex)\n",
        "S_first_row[0] = 1 + 0j\n",
        "\n",
        "# Add in ancilla-only measurements:\n",
        "for i in range(krylov_dim - 1):\n",
        "    # Get expectation values from experiment\n",
        "    expval_real = results.data.evs[0][0][\n",
        "        i\n",
        "    ]  # automatic extrapolated evs if ZNE is used\n",
        "    expval_imag = results.data.evs[1][0][\n",
        "        i\n",
        "    ]  # automatic extrapolated evs if ZNE is used\n",
        "\n",
        "    # Get expectation values\n",
        "    expval = expval_real + 1j * expval_imag\n",
        "    S_first_row[i + 1] += prefactors[i] * expval\n",
        "\n",
        "S_first_row_list = S_first_row.tolist()  # for saving purposes\n",
        "\n",
        "\n",
        "S_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)\n",
        "\n",
        "# Distribute entries from first row across matrix:\n",
        "for i, j in it.product(range(krylov_dim), repeat=2):\n",
        "    if i >= j:\n",
        "        S_circ[j, i] = S_first_row[i - j]\n",
        "    else:\n",
        "        S_circ[j, i] = np.conj(S_first_row[j - i])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "01c6563d-87ca-4bd6-b487-dcdaece2d8c2",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/latex": [
              "$$\n",
              "\\displaystyle \\left[\\begin{matrix}1.0 & -0.723052998582984 - 0.345085413575966 i & 0.467051960502366 + 0.516197865254034 i & -0.180546747798251 - 0.492624093654174 i & 0.0012070853532697 + 0.312052218182462 i\\\\-0.723052998582984 + 0.345085413575966 i & 1.0 & -0.723052998582984 - 0.345085413575966 i & 0.467051960502366 + 0.516197865254034 i & -0.180546747798251 - 0.492624093654174 i\\\\0.467051960502366 - 0.516197865254034 i & -0.723052998582984 + 0.345085413575966 i & 1.0 & -0.723052998582984 - 0.345085413575966 i & 0.467051960502366 + 0.516197865254034 i\\\\-0.180546747798251 + 0.492624093654174 i & 0.467051960502366 - 0.516197865254034 i & -0.723052998582984 + 0.345085413575966 i & 1.0 & -0.723052998582984 - 0.345085413575966 i\\\\0.0012070853532697 - 0.312052218182462 i & -0.180546747798251 + 0.492624093654174 i & 0.467051960502366 - 0.516197865254034 i & -0.723052998582984 + 0.345085413575966 i & 1.0\\end{matrix}\\right]\n",
              "$$"
            ],
            "text/plain": [
              "Matrix([\n",
              "[                                     1.0, -0.723052998582984 - 0.345085413575966*I,  0.467051960502366 + 0.516197865254034*I, -0.180546747798251 - 0.492624093654174*I, 0.0012070853532697 + 0.312052218182462*I],\n",
              "[-0.723052998582984 + 0.345085413575966*I,                                      1.0, -0.723052998582984 - 0.345085413575966*I,  0.467051960502366 + 0.516197865254034*I, -0.180546747798251 - 0.492624093654174*I],\n",
              "[ 0.467051960502366 - 0.516197865254034*I, -0.723052998582984 + 0.345085413575966*I,                                      1.0, -0.723052998582984 - 0.345085413575966*I,  0.467051960502366 + 0.516197865254034*I],\n",
              "[-0.180546747798251 + 0.492624093654174*I,  0.467051960502366 - 0.516197865254034*I, -0.723052998582984 + 0.345085413575966*I,                                      1.0, -0.723052998582984 - 0.345085413575966*I],\n",
              "[0.0012070853532697 - 0.312052218182462*I, -0.180546747798251 + 0.492624093654174*I,  0.467051960502366 - 0.516197865254034*I, -0.723052998582984 + 0.345085413575966*I,                                      1.0]])"
            ]
          },
          "execution_count": 49,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "Matrix(S_circ)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a34c72c0-2984-47fb-8579-089ea795ef39",
      "metadata": {},
      "source": [
        "Et les éléments de la matrice de $\\tilde{H}$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "9cde2419-8a29-4d7a-b5b1-9ef2d4c126d2",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Assemble S, the overlap matrix of dimension D:\n",
        "H_first_row = np.zeros(krylov_dim, dtype=complex)\n",
        "H_first_row[0] = H_expval\n",
        "\n",
        "for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):\n",
        "    # Add in ancilla-only measurements:\n",
        "    for i in range(krylov_dim - 1):\n",
        "        # Get expectation values from experiment\n",
        "        expval_real = results.data.evs[2 + 2 * obs_idx][0][\n",
        "            i\n",
        "        ]  # automatic extrapolated evs if ZNE is used\n",
        "        expval_imag = results.data.evs[2 + 2 * obs_idx + 1][0][\n",
        "            i\n",
        "        ]  # automatic extrapolated evs if ZNE is used\n",
        "\n",
        "        # Get expectation values\n",
        "        expval = expval_real + 1j * expval_imag\n",
        "        H_first_row[i + 1] += prefactors[i] * coeff * expval\n",
        "\n",
        "H_first_row_list = H_first_row.tolist()\n",
        "\n",
        "H_eff_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)\n",
        "\n",
        "# Distribute entries from first row across matrix:\n",
        "for i, j in it.product(range(krylov_dim), repeat=2):\n",
        "    if i >= j:\n",
        "        H_eff_circ[j, i] = H_first_row[i - j]\n",
        "    else:\n",
        "        H_eff_circ[j, i] = np.conj(H_first_row[j - i])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 51,
      "id": "5800d892-5554-4a92-bfe0-3750ef0a3ed7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/latex": [
              "$$\n",
              "\\displaystyle \\left[\\begin{matrix}25.0 & -14.2437089383409 - 6.50486277982165 i & 10.2857217968584 + 9.0431912203186 i & -5.15587257589417 - 8.88280836036843 i & 1.98818301405581 + 5.8897614762563 i\\\\-14.2437089383409 + 6.50486277982165 i & 25.0 & -14.2437089383409 - 6.50486277982165 i & 10.2857217968584 + 9.0431912203186 i & -5.15587257589417 - 8.88280836036843 i\\\\10.2857217968584 - 9.0431912203186 i & -14.2437089383409 + 6.50486277982165 i & 25.0 & -14.2437089383409 - 6.50486277982165 i & 10.2857217968584 + 9.0431912203186 i\\\\-5.15587257589417 + 8.88280836036843 i & 10.2857217968584 - 9.0431912203186 i & -14.2437089383409 + 6.50486277982165 i & 25.0 & -14.2437089383409 - 6.50486277982165 i\\\\1.98818301405581 - 5.8897614762563 i & -5.15587257589417 + 8.88280836036843 i & 10.2857217968584 - 9.0431912203186 i & -14.2437089383409 + 6.50486277982165 i & 25.0\\end{matrix}\\right]\n",
              "$$"
            ],
            "text/plain": [
              "Matrix([\n",
              "[                                  25.0, -14.2437089383409 - 6.50486277982165*I,   10.2857217968584 + 9.0431912203186*I, -5.15587257589417 - 8.88280836036843*I,   1.98818301405581 + 5.8897614762563*I],\n",
              "[-14.2437089383409 + 6.50486277982165*I,                                   25.0, -14.2437089383409 - 6.50486277982165*I,   10.2857217968584 + 9.0431912203186*I, -5.15587257589417 - 8.88280836036843*I],\n",
              "[  10.2857217968584 - 9.0431912203186*I, -14.2437089383409 + 6.50486277982165*I,                                   25.0, -14.2437089383409 - 6.50486277982165*I,   10.2857217968584 + 9.0431912203186*I],\n",
              "[-5.15587257589417 + 8.88280836036843*I,   10.2857217968584 - 9.0431912203186*I, -14.2437089383409 + 6.50486277982165*I,                                   25.0, -14.2437089383409 - 6.50486277982165*I],\n",
              "[  1.98818301405581 - 5.8897614762563*I, -5.15587257589417 + 8.88280836036843*I,   10.2857217968584 - 9.0431912203186*I, -14.2437089383409 + 6.50486277982165*I,                                   25.0]])"
            ]
          },
          "execution_count": 51,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "Matrix(H_eff_circ)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "896553fa-3c28-44cc-89ad-5031d2f8d35f",
      "metadata": {},
      "source": [
        "Enfin, nous pouvons résoudre le problème des valeurs propres généralisées pour $\\tilde{H}$ :\n",
        "\n",
        "$\\tilde{H} \\vec{c} = c S \\vec{c}$\n",
        "\n",
        "et obtenir une estimation de l'énergie de l'état fondamental $c_{min}$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 58,
      "id": "8b997d15-ee25-40eb-80e8-1f054a298ee9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The estimated ground state energy is:  25.0\n",
            "The estimated ground state energy is:  22.572154819954875\n",
            "The estimated ground state energy is:  21.691509219286587\n",
            "The estimated ground state energy is:  21.23882298756386\n",
            "The estimated ground state energy is:  20.965499325470294\n"
          ]
        }
      ],
      "source": [
        "gnd_en_circ_est_list = []\n",
        "for d in range(1, krylov_dim + 1):\n",
        "    # Solve generalized eigenvalue problem for different size of the Krylov space\n",
        "    gnd_en_circ_est = solve_regularized_gen_eig(\n",
        "        H_eff_circ[:d, :d], S_circ[:d, :d], threshold=9e-1\n",
        "    )\n",
        "    gnd_en_circ_est_list.append(gnd_en_circ_est)\n",
        "    print(\"The estimated ground state energy is: \", gnd_en_circ_est)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b39943f8-b75f-4bd4-b246-833ea4f9b710",
      "metadata": {},
      "source": [
        "Pour un secteur à une seule particule, nous pouvons calculer efficacement l'état fondamental de ce secteur de l'hamiltonien de manière classique\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 59,
      "id": "fcfe07e5-99e4-4276-a4d6-6f1c7e28c5f2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "n_sys_qubits 30\n",
            "n_exc 1 , subspace dimension 31\n",
            "single particle ground state energy:  21.021912418526906\n"
          ]
        }
      ],
      "source": [
        "gs_en = single_particle_gs(H_op, n_qubits)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 60,
      "id": "4bc52594-0376-497f-8a61-0949415a1fe0",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/krylov-quantum-diagonalization/extracted-outputs/4bc52594-0376-497f-8a61-0949415a1fe0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.plot(\n",
        "    range(1, krylov_dim + 1),\n",
        "    gnd_en_circ_est_list,\n",
        "    color=\"blue\",\n",
        "    linestyle=\"-.\",\n",
        "    label=\"KQD estimate\",\n",
        ")\n",
        "plt.plot(\n",
        "    range(1, krylov_dim + 1),\n",
        "    [gs_en] * krylov_dim,\n",
        "    color=\"red\",\n",
        "    linestyle=\"-\",\n",
        "    label=\"exact\",\n",
        ")\n",
        "plt.xticks(range(1, krylov_dim + 1), range(1, krylov_dim + 1))\n",
        "plt.legend()\n",
        "plt.xlabel(\"Krylov space dimension\")\n",
        "plt.ylabel(\"Energy\")\n",
        "plt.title(\n",
        "    \"Estimating Ground state energy with Krylov Quantum Diagonalization\"\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2ef2fac9-b4b9-4457-a551-c98999a88710",
      "metadata": {},
      "source": [
        "<span id=\"appendix-krylov-subspace-from-real-time-evolutions\" />\n",
        "\n",
        "## Annexe : Sous-espace de Krylov à partir d'évolutions en temps réel\n",
        "\n",
        "L'espace de Krylov unitaire est défini comme suit\n",
        "\n",
        "$$\n",
        "\\mathcal{K}_U(H, |\\psi\\rangle) = \\text{span}\\left\\{ |\\psi\\rangle,  e^{-iH\\,dt} |\\psi\\rangle, \\dots, e^{-irH\\,dt} |\\psi\\rangle \\right\\}\n",
        "$$\n",
        "\n",
        "pour un certain pas de temps $dt$ que nous déterminerons plus tard. Supposons temporairement que $r$ est pair : définissons alors $d=r/2$. Remarquez que lorsque nous projetons l'hamiltonien dans l'espace de Krylov ci-dessus, il est indiscernable de l'espace de Krylov\n",
        "\n",
        "$$\n",
        "\\mathcal{K}_U(H, |\\psi\\rangle) = \\text{span}\\left\\{ e^{i\\,d\\,H\\,dt}|\\psi\\rangle,  e^{i(d-1)H\\,dt} |\\psi\\rangle, \\dots, e^{-i(d-1)H\\,dt} |\\psi\\rangle, e^{-i\\,d\\,H\\,dt} |\\psi\\rangle \\right\\},\n",
        "$$\n",
        "\n",
        "c'est-à-dire lorsque toutes les évolutions temporelles sont décalées vers l'arrière de $d$ pas de temps.\n",
        "La raison pour laquelle il n'est pas possible de les distinguer est que les éléments de la matrice\n",
        "\n",
        "$$\n",
        "\\tilde{H}_{j,k} = \\langle\\psi|e^{i\\,j\\,H\\,dt}He^{-i\\,k\\,H\\,dt}|\\psi\\rangle=\\langle\\psi|He^{i(j-k)H\\,dt}|\\psi\\rangle\n",
        "$$\n",
        "\n",
        "sont invariants en cas de décalage global du temps d'évolution, puisque les évolutions temporelles commutent avec l'hamiltonien. Pour les $r$ impairs, nous pouvons utiliser l'analyse pour les $r-1$.\n",
        "\n",
        "Nous voulons montrer que quelque part dans cet espace de Krylov, il est garanti qu'il existe un état de basse énergie. Nous le faisons au moyen du résultat suivant, qui est dérivé du théorème 3.1 dans [\\[3\\] :](#references)\n",
        "\n",
        "**Affirmation 1 :** il existe une fonction $f$ telle que pour les énergies $E$ dans le domaine spectral de l'hamiltonien (c'est-à-dire entre l'énergie de l'état fondamental et l'énergie maximale),...\n",
        "\n",
        "1. $f(E_0)=1$\n",
        "2. $|f(E)|\\le2\\left(1 + \\delta\\right)^{-d}$ pour toutes les valeurs de $E$ qui se situent à $\\ge\\delta$ de $E_0$, c'est-à-dire qu'elle est exponentiellement supprimée\n",
        "3. $f(E)$ est une combinaison linéaire de $e^{ijE\\,dt}$ pour $j=-d,-d+1,...,d-1,d$\n",
        "\n",
        "Nous donnons une preuve ci-dessous, mais elle peut être ignorée sans risque, à moins que l'on ne veuille comprendre l'argument complet et rigoureux. Pour l'instant, nous nous concentrons sur les implications de l'affirmation ci-dessus. En vertu de la propriété 3 ci-dessus, nous pouvons voir que l'espace de Krylov décalé ci-dessus contient l'état $f(H)|\\psi\\rangle$. Il s'agit de notre état de basse énergie. Pour comprendre pourquoi, il faut écrire $|\\psi\\rangle$ dans la base propre de l'énergie :\n",
        "\n",
        "$$\n",
        "|\\psi\\rangle = \\sum_{k=0}^{N}\\gamma_k|E_k\\rangle,\n",
        "$$\n",
        "\n",
        "où $|E_k\\rangle$ est le kème état propre énergétique et $\\gamma_k$ est son amplitude dans l'état initial $|\\psi\\rangle$. Exprimé en termes de ceci, $f(H)|\\psi\\rangle$ est donné par\n",
        "\n",
        "$$\n",
        "f(H)|\\psi\\rangle = \\sum_{k=0}^{N}\\gamma_kf(E_k)|E_k\\rangle,\n",
        "$$\n",
        "\n",
        "en utilisant le fait que l'on peut remplacer $H$ par $E_k$ lorsqu'il agit sur l'état propre $|E_k\\rangle$. L'erreur énergétique de cet état est donc\n",
        "\n",
        "$$\n",
        "\\text{energy error} = \\frac{\\langle\\psi|f(H)(H-E_0)f(H)|\\psi\\rangle}{\\langle\\psi|f(H)^2|\\psi\\rangle}\n",
        "$$\n",
        "\n",
        "$$\n",
        "= \\frac{\\sum_{k=0}^{N}|\\gamma_k|^2f(E_k)^2(E_k-E_0)}{\\sum_{k=0}^{N}|\\gamma_k|^2f(E_k)^2}.\n",
        "$$\n",
        "\n",
        "Pour transformer ceci en une borne supérieure plus facile à comprendre, nous séparons d'abord la somme du numérateur en termes avec $E_k-E_0\\le\\delta$ et en termes avec $E_k-E_0>\\delta$ :\n",
        "\n",
        "$$\n",
        "\\text{energy error} = \\frac{\\sum_{E_k\\le E_0+\\delta}|\\gamma_k|^2f(E_k)^2(E_k-E_0)}{\\sum_{k=0}^{N}|\\gamma_k|^2f(E_k)^2} + \\frac{\\sum_{E_k> E_0+\\delta}|\\gamma_k|^2f(E_k)^2(E_k-E_0)}{\\sum_{k=0}^{N}|\\gamma_k|^2f(E_k)^2}.\n",
        "$$\n",
        "\n",
        "Nous pouvons limiter le premier terme par $\\delta$,\n",
        "\n",
        "$$\n",
        "\\frac{\\sum_{E_k\\le E_0+\\delta}|\\gamma_k|^2f(E_k)^2(E_k-E_0)}{\\sum_{k=0}^{N}|\\gamma_k|^2f(E_k)^2} < \\frac{\\delta\\sum_{E_k\\le E_0+\\delta}|\\gamma_k|^2f(E_k)^2}{\\sum_{k=0}^{N}|\\gamma_k|^2f(E_k)^2} \\le \\delta,\n",
        "$$\n",
        "\n",
        "où la première étape suit parce que $E_k-E_0\\le\\delta$ pour chaque $E_k$ dans la somme, et la deuxième étape suit parce que la somme dans le numérateur est un sous-ensemble de la somme dans le dénominateur. Pour le second terme, on commence par abaisser le dénominateur par $|\\gamma_0|^2$, puisque $f(E_0)^2=1$ : en additionnant le tout, on obtient\n",
        "\n",
        "$$\n",
        "\\text{energy error} \\le \\delta + \\frac{1}{|\\gamma_0|^2}\\sum_{E_k>E_0+\\delta}|\\gamma_k|^2f(E_k)^2(E_k-E_0).\n",
        "$$\n",
        "\n",
        "Pour simplifier ce qui reste, remarquez que pour tous ces $E_k$, par la définition de $f$ nous savons que $f(E_k)^2 \\le 4\\left(1 + \\delta\\right)^{-2d}$. De plus, la borne supérieure de $E_k-E_0<2\\|H\\|$ et la borne supérieure de $\\sum_{E_k>E_0+\\delta}|\\gamma_k|^2<1$ donnent\n",
        "\n",
        "$$\n",
        "\\text{energy error} \\le \\delta + \\frac{8}{|\\gamma_0|^2}\\|H\\|\\left(1 + \\delta\\right)^{-2d}.\n",
        "$$\n",
        "\n",
        "Ceci est valable pour n'importe quel $\\delta>0$, donc si nous fixons $\\delta$ égal à notre erreur cible, alors la limite d'erreur ci-dessus converge vers cela de manière exponentielle avec la dimension de Krylov $2d=r$. Notez également que si $\\delta<E_1-E_0$, le terme $\\delta$ disparaît entièrement dans la limite ci-dessus.\n",
        "\n",
        "Pour compléter l'argument, nous notons tout d'abord que ce qui précède n'est que l'erreur énergétique de l'état particulier $f(H)|\\psi\\rangle$, plutôt que l'erreur énergétique de l'état de plus faible énergie dans l'espace de Krylov. Toutefois, en vertu du principe variationnel (Rayleigh-Ritz), l'erreur énergétique de l'état de plus faible énergie dans l'espace de Krylov est limitée par l'erreur énergétique de tout état dans l'espace de Krylov, de sorte que ce qui précède est également une limite supérieure de l'erreur énergétique de l'état de plus faible énergie, c'est-à-dire la sortie de l'algorithme de diagonalisation quantique de Krylov.\n",
        "\n",
        "Il est possible d'effectuer une analyse similaire à la précédente en tenant compte du bruit et de la procédure de seuillage décrite dans le manuel. Voir [\\[2\\]](#references) et [\\[4\\]](#references) pour cette analyse.\n",
        "\n",
        "<span id=\"appendix-proof-of-claim-1\" />\n",
        "\n",
        "## Annexe : preuve de la revendication 1\n",
        "\n",
        "Ce qui suit est en grande partie dérivé de [\\[3\\]](#references), Théorème 3.1: Soit $0 < a < b$ et $\\Pi^*_d$ l'espace des polynômes résiduels (polynômes dont la valeur en 0 est 1) de degré au plus $d$. La solution de\n",
        "\n",
        "$$\n",
        "\\beta(a, b, d) = \\min_{p \\in \\Pi^*_d} \\max_{x \\in [a, b]} |p(x)| \\quad\n",
        "$$\n",
        "\n",
        "est\n",
        "\n",
        "$$\n",
        "p^*(x) = \\frac{T_d\\left(\\frac{b + a - 2x}{b - a}\\right)}{T_d\\left(\\frac{b + a}{b - a}\\right)}, \\quad\n",
        "$$\n",
        "\n",
        "et la valeur minimale correspondante est\n",
        "\n",
        "$$\n",
        "\\beta(a, b, d) = T_d^{-1}\\left(\\frac{b + a}{b - a}\\right).\n",
        "$$\n",
        "\n",
        "Nous voulons convertir cette fonction en une fonction qui peut être exprimée naturellement en termes d'exponentielles complexes, car ce sont les évolutions temporelles réelles qui génèrent l'espace de Krylov quantique.\n",
        "Pour ce faire, il est commode d'introduire la transformation suivante des énergies dans le domaine spectral de l'hamiltonien en nombres dans l'intervalle $[0,1]$ : définir\n",
        "\n",
        "$$\n",
        "g(E) = \\frac{1-\\cos\\big((E-E_0)dt\\big)}{2},\n",
        "$$\n",
        "\n",
        "où $dt$ est un pas de temps tel que $-\\pi < E_0dt < E_\\text{max}dt < \\pi$. Remarquez que $g(E_0)=0$ et $g(E)$ croissent au fur et à mesure que $E$ s'éloigne de $E_0$.\n",
        "\n",
        "En utilisant maintenant le polynôme $p^*(x)$ avec les paramètres a, b, d fixés à $a = g(E_0 + \\delta)$, $b = 1$, et d = int( r/2 ), nous définissons la fonction :\n",
        "\n",
        "$$\n",
        "f(E) = p^* \\left( g(E) \\right) = \\frac{T_d\\left(1 + 2\\frac{\\cos\\big((E-E_0)dt\\big) - \\cos\\big(\\delta\\,dt\\big)}{1 +\\cos\\big(\\delta\\,dt\\big)}\\right)}{T_d\\left(1 + 2\\frac{1-\\cos\\big(\\delta\\,dt\\big)}{1 + \\cos\\big(\\delta\\,dt\\big)}\\right)}\n",
        "$$\n",
        "\n",
        "où $E_0$ est l'énergie de l'état fondamental. Nous pouvons voir en insérant $\\cos(x)=\\frac{e^{ix}+e^{-ix}}{2}$ que $f(E)$ est un polynôme trigonométrique de degré $d$, c'est-à-dire une combinaison linéaire de $e^{ijE\\,dt}$ pour $j=-d,-d+1,...,d-1,d$. De plus, d'après la définition de $p^*(x)$ ci-dessus, nous avons que $f(E_0)=p(0)=1$ et pour tout $E$ dans le domaine spectral tel que $\\vert E-E_0 \\vert > \\delta$ nous avons\n",
        "\n",
        "$$\n",
        "|f(E)| \\le \\beta(a, b, d) = T_d^{-1}\\left(1 + 2\\frac{1-\\cos\\big(\\delta\\,dt\\big)}{1 + \\cos\\big(\\delta\\,dt\\big)}\\right)\n",
        "$$\n",
        "\n",
        "$$\n",
        "\\leq 2\\left(1 + \\delta\\right)^{-d} = 2\\left(1 + \\delta\\right)^{-\\lfloor k/2\\rfloor}.\n",
        "$$\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ccd6c21d-ed1e-4894-9cbb-8d3a9d91afe2",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Références\n",
        "\n",
        "\\[1] N. Yoshioka, M. Amico, W. Kirby et al. \"Diagonalization of large many-body Hamiltonians on a quantum processor\". [arXiv:2407.14431](https://arxiv.org/abs/2407.14431)\n",
        "\n",
        "\\[2] Ethan N. Epperly, Lin Lin et Yuji Nakatsukasa. \"Une théorie de la diagonalisation du sous-espace quantique\". SIAM Journal on Matrix Analysis and Applications 43, 1263-1290 (2022).\n",
        "\n",
        "\\[3] Å. Björck. \"Méthodes numériques dans les calculs matriciels\". Textes en mathématiques appliquées. Springer International Publishing. (2014).\n",
        "\n",
        "\\[4] William Kirby. \"Analyse des algorithmes de Krylov quantiques avec erreurs\". Quantum 8, 1457 (2024).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "74948cc7-041f-412c-ab16-2554bf164061",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## Enquête tutorielle\n",
        "\n",
        "Veuillez répondre à cette courte enquête pour nous faire part de vos commentaires sur ce didacticiel. Vos commentaires nous aideront à améliorer nos offres de contenu et l'expérience des utilisateurs.\n",
        "\n",
        "[Lien vers l'enquête](https://your.feedback.ibm.com/jfe/form/SV_82nennpKIjjD8rQ)\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": 1200
  },
  "nbformat": 4,
  "nbformat_minor": 4
}