{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Démarrage rapide\"\n",
        "description: \"Guide de démarrage rapide pour la dernière version de la diagonalisation quantique par échantillonnage (SQD)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Démarrage rapide\n",
        "\n",
        "`qiskit-addon-sqd` Ce guide présente un exemple minimal fonctionnel du paquet. Nous utilisons la diagonalisation quantique par échantillonnage (SQD) pour affiner des échantillons bruités prélevés à partir d'un circuit quantique qui approxime l'état fondamental d'un hamiltonien chimique. Les énergies de l'état fondamental sont estimées en diagonalisant l'hamiltonien dans les sous-espaces formés par les échantillons affinés.\n",
        "\n",
        "Pour découvrir d'autres workflows de bout en bout explicatifs s'exécutant sur du matériel quantique, consultez les tutoriels suivants :\n",
        "\n",
        "* [Diagonalisation quantique par échantillonnage](/docs/tutorials/sample-based-quantum-diagonalization) : flux de travail pour l'hamiltonien chimique\n",
        "* [Diagonalisation quantique de Krylov par échantillonnage](/docs/tutorials/sample-based-krylov-quantum-diagonalization) : procédure pour un modèle de réseau fermionique\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "prepare-header",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-sqd\" />\n",
        "\n",
        "## 1. Préparer les données d'entrée pour le SQD\n",
        "\n",
        "Tout d'abord, nous définissons l'hamiltonien chimique et le circuit quantique qui en approximent l'état fondamental. Le système étudié dans cet exemple est le N₂ en géométrie d'équilibre, dans une base d STO-3G, avec deux orbitales centrales figées. Ce système comporte `8` des orbitales spatiales et `10` des électrons, ce qui signifie que le circuit quantique est défini sur `16` des qubits, où les qubits `0-7` représentent les orbitales à spin vers le haut et les qubits `8-15` représentent les orbitales à spin vers le bas. `10`Comme il s'agit d'un système à couche fermée comportant `10` des électrons, nous savons que chaque échantillon physiquement valable doit avoir un poids de Hamming, où le poids de Hamming des deux moitiés (gauche et droite) de la chaîne binaire est `5`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "load-molecule",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Spatial orbitals: 8\n",
            "Qubits: 16\n",
            "Electrons (alpha, beta): (5, 5)\n"
          ]
        }
      ],
      "source": [
        "from pyscf import ao2mo, gto, mcscf, scf\n",
        "\n",
        "# Construct the Hamiltonian, pull out integrals and other information about the system\n",
        "mol = gto.M(\n",
        "    atom=\"N 0 0 0; N 0 0 1.09768\", basis=\"sto-3g\", spin=0, charge=0, verbose=0\n",
        ")\n",
        "mf = scf.RHF(mol).run()\n",
        "cas = mcscf.CASCI(mf, ncas=8, nelecas=10)\n",
        "hcore, nuclear_repulsion_energy = cas.get_h1eff()\n",
        "eri = cas.get_h2eff()\n",
        "num_orbitals = cas.ncas\n",
        "eri = ao2mo.restore(1, eri, num_orbitals)\n",
        "n_alpha, n_beta = cas.nelecas\n",
        "nelec = (n_alpha, n_beta)\n",
        "\n",
        "print(\n",
        "    f\"Spatial orbitals: {num_orbitals}\\nQubits: {num_orbitals * 2}\\nElectrons (alpha, beta): {nelec}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "samples-explanation",
      "metadata": {},
      "source": [
        "<span id=\"2-generate-noisy-samples\" />\n",
        "\n",
        "## 2. Générer des échantillons bruités\n",
        "\n",
        "Dans un flux de travail réel, vous concevriez un circuit « ansatz », vous l'optimiseriez pour le matériel, puis vous le simuleriez sur le QPU. Ici, nous ne nous attardons pas là-dessus et nous générons `10,000` plutôt des chaînes de bits aléatoires uniformément réparties. Bien que nos échantillons ne contiennent aucune information sur l'état fondamental réel, nous verrons que le protocole SQD peut tout de même déterminer cet état fondamental en affinant ces échantillons de manière itérative, en les diagonalisant et en utilisant les énergies obtenues à chaque itération pour orienter la prochaine itération d'affinement de la chaîne de bits. Nous pouvons finalement déterminer avec exactitude l'état fondamental de cette manière, car le sous-espace contenant l'état fondamental est suffisamment petit pour permettre un calcul exact. `5`Plus précisément, puisque nous savons que chaque moitié de chaque chaîne binaire (`8` bits) doit avoir un poids de Hamming, nous savons que la taille totale du sous-espace de Hilbert du système est de $\\binom{8}{5}^2=3136$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "generate-samples",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Generated 10000 uniformly-random, 16-qubit samples.\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit_addon_sqd.counts import generate_bit_array_uniform\n",
        "\n",
        "# Generate some uniformly-random bitstrings to simulate QPU samples\n",
        "rng = np.random.default_rng(24)\n",
        "bit_array = generate_bit_array_uniform(\n",
        "    10_000, num_orbitals * 2, rand_seed=rng\n",
        ")\n",
        "print(\n",
        "    f\"Generated {bit_array.num_shots} uniformly-random, {bit_array.num_bits}-qubit samples.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postprocess-header",
      "metadata": {},
      "source": [
        "<span id=\"3-post-process-with-sqd\" />\n",
        "\n",
        "## 3. Post-traitement avec SQD\n",
        "\n",
        "L'objectif de l'algorithme SQD est d'affiner les échantillons bruités provenant d'un QPU et, à terme, de diagonaliser l'hamiltonien dans un sous-espace engendré par ces échantillons affinés. Cette opération s'effectue de manière itérative, comme suit :\n",
        "\n",
        "* Affiner les échantillons en ne retenant que ceux dont le nombre de particules est correct pour les sous-systèmes « spin-up » et « spin-down »\n",
        "* Diagonaliser dans de petits sous-espaces engendrés par les échantillons affinés et recueillir des informations sur les sous-espaces qui produisent les meilleures énergies (les plus faibles).\n",
        "* À chaque itération :\n",
        "  * Affiner les échantillons bruités en inversant de manière probabiliste les bits des échantillons dont le nombre de particules est incorrect. Les probabilités sont calculées à partir des informations relatives au sous-espace issues de la dernière diagonalisation.\n",
        "  * Diagonaliser dans de petits sous-espaces engendrés par les échantillons affinés\n",
        "  * Recueillir des informations sur les sous-espaces donnant les meilleures énergies (les plus faibles)\n",
        "\n",
        "On constate d'après les résultats que l'algorithme converge vers l'énergie exacte de l'état fondamental après `6` itérations et que la diagonalisation est effectuée sur l'ensemble du sous-espace de Hilbert de dimension $\\binom{8}{5}^2=3136$ Bien entendu, la diagonalisation sur l'ensemble du sous-espace n'est généralement pas réalisable; par conséquent, ce sont les systèmes dont les états fondamentaux ont un support clairsemé qui sont les plus susceptibles de tirer profit des techniques de diagonalisation par sous-espace telles que la SQD.\n",
        "\n",
        "Pour plus d'informations sur la fonction `diagonalize\\_fermionic\\_hamiltonian`, consultez la [documentation de l'API](/docs/api/qiskit-addon-sqd/fermion#diagonalize_fermionic_hamiltonian).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "run-sqd",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Exact (FCI) reference energy: -107.652521 Ha\n",
            "\n",
            "Iteration 1\n",
            "  Subsample 0\n",
            "    Energy: -106.908481\n",
            "    Subspace dimension: 784\n",
            "    Error vs exact: 0.744040 Ha\n",
            "Iteration 2\n",
            "  Subsample 0\n",
            "    Energy: -106.934921\n",
            "    Subspace dimension: 1764\n",
            "    Error vs exact: 0.717600 Ha\n",
            "Iteration 3\n",
            "  Subsample 0\n",
            "    Energy: -106.953430\n",
            "    Subspace dimension: 2209\n",
            "    Error vs exact: 0.699091 Ha\n",
            "Iteration 4\n",
            "  Subsample 0\n",
            "    Energy: -107.651946\n",
            "    Subspace dimension: 2916\n",
            "    Error vs exact: 0.000575 Ha\n",
            "Iteration 5\n",
            "  Subsample 0\n",
            "    Energy: -107.652003\n",
            "    Subspace dimension: 3025\n",
            "    Error vs exact: 0.000518 Ha\n",
            "Iteration 6\n",
            "  Subsample 0\n",
            "    Energy: -107.652521\n",
            "    Subspace dimension: 3136\n",
            "    Error vs exact: 0.000000 Ha\n",
            "Iteration 7\n",
            "  Subsample 0\n",
            "    Energy: -107.652521\n",
            "    Subspace dimension: 3136\n",
            "    Error vs exact: 0.000000 Ha\n"
          ]
        }
      ],
      "source": [
        "from functools import partial\n",
        "\n",
        "from pyscf import fci\n",
        "from qiskit_addon_sqd.fermion import (\n",
        "    SCIResult,\n",
        "    diagonalize_fermionic_hamiltonian,\n",
        "    solve_sci_batch,\n",
        ")\n",
        "\n",
        "exact_energy, _ = fci.direct_spin1.kernel(\n",
        "    hcore, eri, int(num_orbitals), nelec\n",
        ")\n",
        "exact_energy += nuclear_repulsion_energy\n",
        "print(f\"Exact (FCI) reference energy: {exact_energy:.6f} Ha\\n\")\n",
        "\n",
        "sci_solver = partial(solve_sci_batch, spin_sq=0.0, max_cycle=200)\n",
        "\n",
        "result_history = []\n",
        "\n",
        "\n",
        "def callback(results: list[SCIResult]):\n",
        "    result_history.append(results)\n",
        "    iteration = len(result_history)\n",
        "    print(f\"Iteration {iteration}\")\n",
        "    for i, result in enumerate(results):\n",
        "        error = result.energy + nuclear_repulsion_energy - exact_energy\n",
        "        print(f\"  Subsample {i}\")\n",
        "        print(f\"    Energy: {result.energy + nuclear_repulsion_energy:.6f}\")\n",
        "        print(\n",
        "            f\"    Subspace dimension: {np.prod(result.sci_state.amplitudes.shape)}\"\n",
        "        )\n",
        "        print(f\"    Error vs exact: {error:.6f} Ha\")\n",
        "\n",
        "\n",
        "samples_per_batch = 20\n",
        "result = diagonalize_fermionic_hamiltonian(\n",
        "    hcore,\n",
        "    eri,\n",
        "    bit_array,\n",
        "    samples_per_batch=samples_per_batch,\n",
        "    norb=num_orbitals,\n",
        "    nelec=nelec,\n",
        "    occupancies_tol=1e-7,\n",
        "    max_iterations=30,\n",
        "    sci_solver=sci_solver,\n",
        "    symmetrize_spin=True,\n",
        "    callback=callback,\n",
        "    seed=np.random.default_rng(32),\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}