{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Avvio rapido\"\n",
        "description: \"Guida rapida all'ultima versione del metodo di diagonalizzazione quantistica basato su campioni (SQD)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Avvio rapido\n",
        "\n",
        "Questa guida illustra un esempio minimale funzionante del `qiskit-addon-sqd` pacchetto. Utilizziamo la diagonalizzazione quantistica basata su campioni (SQD) per affinare campioni rumorosi prelevati da un circuito quantistico che approssima lo stato fondamentale di un hamiltoniano chimico. Le energie dello stato fondamentale vengono approssimate diagonalizzando l'Hamiltoniano nei sottospazi formati dai campioni raffinati.\n",
        "\n",
        "Per ulteriori flussi di lavoro end-to-end illustrativi che vengono eseguiti su hardware quantistico, consulta i tutorial:\n",
        "\n",
        "* [Diagonalizzazione quantistica basata su campioni](/docs/tutorials/sample-based-quantum-diagonalization) : flusso di lavoro relativo all'Hamiltoniano chimico\n",
        "* [Diagonalizzazione quantistica di Krylov basata su campioni](/docs/tutorials/sample-based-krylov-quantum-diagonalization) : flusso di lavoro per un modello a reticolo fermionico\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "prepare-header",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-sqd\" />\n",
        "\n",
        "## 1. Preparare i dati di input per SQD\n",
        "\n",
        "Per prima cosa, definiamo l'Hamiltoniano chimico e il circuito quantistico che ne approssima lo stato fondamentale. Il sistema oggetto di studio in questo esempio è l'N₂ in geometria di equilibrio, in una bas STO-3G a con due orbitali centrali congelati. Questo sistema presenta `8` orbitali spaziali ed `10` elettroni, il che significa che il circuito quantistico è definito su `16` qubit, dove i qubit `0-7` rappresentano gli orbitali con spin verso l'alto e i qubit `8-15` rappresentano gli orbitali con spin verso il basso. `10`Trattandosi di un sistema a guscio chiuso con `10` elettroni, sappiamo che ogni campione fisicamente valido dovrebbe avere peso di Hamming, dove il peso di Hamming sia della metà sinistra che di quella destra della stringa di bit è `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. Generare campioni con rumore\n",
        "\n",
        "In un flusso di lavoro reale, si progetterebbe un circuito di Ansatz, lo si ottimizzerebbe per l'hardware e lo si campionerebbe sulla QPU. In questo caso tralasciamo questa fase e generiamo `10,000` invece stringhe di bit casuali uniformemente distribuite. Sebbene i nostri campioni non contengano alcuna informazione sullo stato fondamentale effettivo, vedremo che il protocollo SQD è comunque in grado di ricostruire il vero stato fondamentale perfezionando iterativamente questi campioni, diagonalizzando e utilizzando le energie ottenute ad ogni iterazione per guidare la successiva fase di perfezionamento della stringa di bit. In questo modo possiamo alla fine ricavare con esattezza lo stato fondamentale, poiché il sottospazio che contiene lo stato fondamentale è sufficientemente piccolo da poter essere calcolato con precisione. `5`In particolare, poiché sappiamo che ciascuna metà di ogni stringa di bit (`8` bit) dovrebbe avere peso di Hamming, sappiamo che il sottospazio di Hilbert completo del sistema ha dimensione $\\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-elaborazione con SQD\n",
        "\n",
        "L'obiettivo dell'algoritmo SQD è quello di raffinare i campioni rumorosi prelevati da una QPU e, in ultima analisi, di diagonalizzare l'hamiltoniano in un sottospazio generato da tali campioni raffinati. Questo processo viene eseguito in modo iterativo come segue:\n",
        "\n",
        "* Filtrare i campioni selezionando successivamente solo quelli che presentano un numero corretto di particelle sia per il sottosistema “spin-up” che per quello “spin-down”\n",
        "* Diagonalizzare i piccoli sottospazi generati dai campioni raffinati e raccogliere informazioni sui sottospazi che producono le energie migliori (più basse).\n",
        "* Per ogni iterazione:\n",
        "  * Rifinire i campioni affetti da rumore invertendo in modo probabilistico i bit nei campioni con un numero di particelle errato. Le probabilità vengono calcolate utilizzando le informazioni relative al sottospazio ricavate dalla diagonalizzazione più recente.\n",
        "  * Diagonalizzare nei piccoli sottospazi generati dai campioni raffinati\n",
        "  * Raccogliere informazioni sui sottospazi che producono le energie migliori (più basse)\n",
        "\n",
        "Dall’output si può notare che l’algoritmo converge all’energia esatta dello stato fondamentale dopo `6` iterazioni e che la diagonalizzazione viene eseguita sull’intero sottospazio di Hilbert di dimensione $\\binom{8}{5}^2=3136$. Naturalmente, la diagonalizzazione sull’intero sottospazio non è solitamente fattibile e, pertanto, i sistemi con stati fondamentali a supporto sparso sono quelli che più probabilmente trarranno vantaggio dalle tecniche di diagonalizzazione su sottospazio come l’SQD.\n",
        "\n",
        "Per ulteriori informazioni sulla funzione `diagonalize\\_fermionic\\_hamiltonian`, consulta la [documentazione dell'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
}