{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "a9fc5202-9641-4db3-ac1a-9986f28854bc",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Risolvitore di autovalori quantistici variazionale\"\n",
        "description: \"Scopri cos'è un risolutore di autovalori quantistici variazionali e calcola l'energia di attivazione di una reazion H+H=H2 a con questo strumento.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore arrowstyle UCCSD verticalalignment horizontalalignment  xytext arrowprops preparable ansätze Marov Aspuru Guzik Hartrees ansä */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bafc481a-d2d4-4668-8389-acbe9cd3f615",
      "metadata": {},
      "source": [
        "<span id=\"variational-quantum-eigensolver-vqe\" />\n",
        "\n",
        "# Risolvitore di autovalori quantistici variazionale (VQE)\n",
        "\n",
        "Per questo modulo, gli studenti devono avere un ambiente Python funzionante e le ultime versioni dei seguenti pacchetti installati:\n",
        "\n",
        "* `qiskit`\n",
        "* `qiskit_ibm_runtime`\n",
        "* `qiskit-aer`\n",
        "* `qiskit.visualization`\n",
        "* `numpy`\n",
        "* `pylatexenc`\n",
        "\n",
        "Per configurare e installare questi pacchetti, consultare la guida [Installare Qiskit](/docs/guides/install-qiskit). Per eseguire i lavori sui computer quantistici reali, gli studenti dovranno configurare un account IBM Cloud, seguendo i passaggi indicati nella guida [Set up your IBM Cloud account](/docs/guides/cloud-setup).\n",
        "\n",
        "*Questo modulo è stato testato e ha utilizzato circa 8 minuti di tempo QPU. Si tratta di una stima e l'utilizzo effettivo può variare.*\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "cda95261-1473-492c-8a1d-1e29773086c9",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Uncomment and modify this line as needed to install dependencies\n",
        "#!pip install 'qiskit>=2.1.0' 'qiskit-ibm-runtime>=0.40.1' 'qiskit-aer>=0.17.0' 'numpy' 'pylatexenc'"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "127021b2-3419-4b9d-bf13-7ef940b50029",
      "metadata": {},
      "source": [
        "<span id=\"introduction\" />\n",
        "\n",
        "## Introduzione\n",
        "\n",
        "Dallo sviluppo del modello meccanico quantistico all'inizio del XX secolo, gli scienziati hanno capito che gli elettroni non seguono percorsi fissi intorno al nucleo di un atomo, ma esistono piuttosto in regioni di probabilità chiamate orbitali. Questi orbitali corrispondono a livelli energetici specifici e discreti che gli elettroni possono occupare. Gli elettroni risiedono naturalmente nei livelli energetici più bassi disponibili, noti come stato fondamentale. Tuttavia, se un elettrone assorbe una quantità sufficiente di energia, può saltare a un livello energetico superiore, entrando in uno stato eccitato. Questo stato eccitato è temporaneo e l'elettrone alla fine tornerà a un livello energetico inferiore, rilasciando l'energia assorbita, spesso sotto forma di luce. Questo processo fondamentale di assorbimento ed emissione di energia è importante per capire come gli atomi interagiscono e formano i legami.\n",
        "\n",
        "Quando gli atomi si uniscono per formare molecole, i loro orbitali atomici si combinano per formare orbitali molecolari. La disposizione e i livelli energetici degli elettroni all'interno di questi orbitali molecolari determinano le proprietà della molecola risultante e la forza dei legami chimici. Ad esempio, nella formazione di una molecola di idrogeno ( $H_2$ ) da due singoli atomi di idrogeno, l'elettrone di ciascun atomo occupa gli orbitali atomici. Quando gli atomi si avvicinano l'uno all'altro, questi orbitali atomici si sovrappongono e si combinano per formare nuovi orbitali molecolari, uno con energia più bassa (un orbitale di legame) e uno con energia più alta (un orbitale di antilegame). I due elettroni, uno per ogni atomo di idrogeno, occuperanno preferenzialmente l'orbitale di legame a più bassa energia, portando alla formazione di un legame covalente stabile che tiene unita la molecola $H_2$. La differenza di energia tra gli atomi separati e la molecola formata, in particolare l'energia degli elettroni negli orbitali molecolari, determina la stabilità e le proprietà del legame.\n",
        "\n",
        "Nelle sezioni seguenti esploreremo questo processo di formazione molecolare, concentrandoci sulla molecola $H_2$. Utilizzeremo un vero computer quantistico, combinato con tecniche di ottimizzazione classica, per trovare l'energia di questo semplice ma fondamentale processo. Questo esperimento fornirà una dimostrazione pratica di come il calcolo quantistico possa essere applicato per risolvere problemi di chimica computazionale, fornendo approfondimenti sul ruolo dell'energia degli elettroni.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "eef4da0c-8f48-47fc-88a2-9a2979c1dbc1",
      "metadata": {},
      "source": [
        "<span id=\"vqe---a-variational-quantum-algorithm-for-eigenvalue-problems\" />\n",
        "\n",
        "## VQE - Un algoritmo quantistico variazionale per problemi di autovalori\n",
        "\n",
        "<span id=\"approximation-techniques-for-chemistry---variational-principle-and-the-basis-set\" />\n",
        "\n",
        "### Tecniche di approssimazione per la chimica - principio variazionale e insieme di base\n",
        "\n",
        "I contributi di Erwin Schrödinger alla meccanica quantistica non si limitano all'introduzione di un nuovo modello elettronico; fondamentalmente, ha stabilito la meccanica ondulatoria sviluppando la famosa equazione di Schrödinger dipendente dal tempo:\n",
        "\n",
        "$$\n",
        "i\\hbar \\frac{d}{dt}|\\psi\\rangle = \\hat{H}|\\psi\\rangle\n",
        "$$\n",
        "\n",
        "Qui, $\\hat{H}$ è l'operatore hamiltoniano, che rappresenta l'energia totale del sistema, e $|\\psi\\rangle$ è la funzione d'onda che contiene tutte le informazioni sullo stato quantistico del sistema. (Nota: $\\frac{d}{dt}$ è la derivata temporale totale e non includiamo esplicitamente l'autovalore dell'energia $E$ )\n",
        "\n",
        "Tuttavia, in molte applicazioni pratiche, come la determinazione dei livelli energetici consentiti di atomi e molecole, si utilizza l'equazione di Schrödinger indipendente dal tempo (equazione degli autovalori dell'energia), che è derivata dalla forma dipendente dal tempo assumendo uno stato stazionario. Uno stato stazionario è uno stato quantistico in cui la densità di probabilità di trovare una particella in un determinato punto dello spazio non cambia nel tempo.\n",
        "\n",
        "$\\hat{H}|\\psi\\rangle = E|\\psi\\rangle$\n",
        "\n",
        "In questa forma, $E$ rappresenta l'autovalore energetico corrispondente allo stato quantico $|\\psi\\rangle$. L'hamiltoniana include vari contributi energetici, come l'energia cinetica degli elettroni e dei nuclei, le forze attrattive tra elettroni e nuclei e le forze repulsive tra elettroni.\n",
        "\n",
        "La soluzione dell'equazione degli autovalori dell'energia ci permette di calcolare i livelli energetici quantizzati dei sistemi atomici e molecolari. Tuttavia, per le molecole, la soluzione esatta è difficile perché la funzione d'onda $\\Psi$, che descrive la distribuzione spaziale degli elettroni, è complessa e altamente dimensionale.\n",
        "\n",
        "Di conseguenza, gli scienziati utilizzano tecniche di approssimazione per ottenere soluzioni pratiche e precise. In questo lavoro ci concentreremo su due metodi chiave:\n",
        "\n",
        "1. Principio variazionale\n",
        "\n",
        "   Questo metodo approssima la funzione d'onda e la regola per avvicinarsi il più possibile all'energia target, di solito l'energia dello stato fondamentale del sistema. L'idea chiave del principio variazionale è semplice:\n",
        "\n",
        "   * Se indoviniamo una funzione d'onda $\\Psi_\\text{trial}$ (una \"funzione di prova\"), l'energia calcolata da essa sarà sempre uguale o superiore all'energia di stato fondamentale ( $E_0$ ) del sistema. $E_\\text{approx} = \\frac{\\langle \\Psi_\\text{trial}|\\hat{H}|\\Psi_\\text{trial}\\rangle}{\\langle \\Psi_\\text{trial}|\\Psi_\\text{trial}\\rangle} \\geq E_0$\n",
        "   * Regolando i parametri $\\theta$ nella funzione di prova, $|\\Psi_\\text{trial}(\\theta)\\rangle$, possiamo ottenere un'approssimazione sempre migliore dell'energia dello stato fondamentale.\n",
        "   * La sua accuratezza dipende fortemente dalla scelta della funzione d'onda di prova $\\Psi_\\text{trial}$. Una funzione di prova scelta male può portare a una stima dell'energia tutt'altro che accurata.\n",
        "\n",
        "2. Approssimazione del set di basi\n",
        "\n",
        "   Il secondo metodo di approssimazione si trova nella fase di costruzione della funzione d'onda - l'approccio degli insiemi di basi. In chimica quantistica, risolvere esattamente l'equazione di Schrödinger per le molecole è quasi impossibile. Invece, approssimiamo la complessa funzione d'onda a più elettroni costruendola a partire da funzioni matematiche più semplici e predefinite. Un insieme di basi è essenzialmente una raccolta di queste funzioni matematiche note, tipicamente centrate sugli atomi della molecola, che vengono utilizzate come elementi costitutivi per rappresentare la forma e il comportamento degli elettroni nel sistema. Pensate al tentativo di ricreare una scultura dettagliata usando solo una collezione di mattoncini LEGO standard: più tipi e dimensioni di mattoncini si hanno (più grande è il set di base), più accuratamente si può approssimare la forma originale.\n",
        "\n",
        "   Queste funzioni base sono spesso ispirate alle soluzioni analitiche per sistemi semplici come l'atomo di idrogeno, assumendo forme come funzioni gaussiane o di tipo Slater, anche se si tratta comunque di approssimazioni. Invece di lavorare con gli orbitali molecolari completi, teoricamente \"esatti\" ma intrattabili, li esprimiamo come una combinazione lineare (una somma di coefficienti) di queste funzioni base. Questo metodo è noto come approccio LCAO (Linear Combination of Atomic Orbitals) quando le funzioni base assomigliano agli orbitali atomici. Ottimizzando i coefficienti di questa combinazione lineare, possiamo trovare la migliore funzione d'onda approssimativa e la migliore energia possibile entro i limiti dell'insieme di basi scelto.\n",
        "\n",
        "   * Maggiore è il numero di funzioni incluse nell'insieme di basi, migliore è l'approssimazione, ma ciò comporta un maggiore sforzo computazionale.\n",
        "   * Un set di basi piccolo fornisce una stima approssimativa, mentre un set di basi grande fornisce risultati più precisi a costo di richiedere maggiori risorse computazionali.\n",
        "\n",
        "In sintesi, per rendere i calcoli fattibili e ridurre il costo computazionale, utilizziamo il principio variazionale approssimando la funzione d'onda, che riduce la complessità computazionale e consente un'ottimizzazione iterativa per minimizzare l'energia. Nel frattempo, l'approccio basato sugli insiemi di basi semplifica i calcoli rappresentando gli orbitali atomici come una combinazione di funzioni predefinite, anziché risolvere direttamente una funzione d'onda continua.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Consideriamo la funzione d'onda di prova $\\Psi_\\text{trial}(\\alpha,x) = Ae^{- \\alpha x^2}$ dove $A$ è una costante di normalizzazione e $\\alpha$ è un parametro regolabile.\n",
        "\n",
        "(a) Normalizzare la funzione d'onda di prova determinando un valore di $A$ tale che $\\int_{-\\infty}^{\\infty} |\\Psi_\\text{trial}|^2 dx = 1$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    Per normalizzare una data funzione d'onda di prova:\n",
        "\n",
        "    $\\int_{-\\infty}^{\\infty} |\\Psi_\\text{trial}|^2 dx = \\int_{-\\infty}^{\\infty} A^2 e^{-2 \\alpha x^2} dx = 1$\n",
        "\n",
        "    Utilizzare l'integrale gaussiano:\n",
        "\n",
        "    $ \\int_{-\\infty}^{\\infty} e^{-a x^2} dx = \\sqrt{\\frac{\\pi}{a}} \\text{,   for } a>0$\n",
        "\n",
        "    impostare $a = 2\\alpha$ e poi ottenere: $A^2\\sqrt{\\frac{\\pi}{a}} = 1$ $\\therefore A = (\\frac{2\\alpha}{\\pi})^{1/4}$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "(b) Calcolare il valore atteso dell' $\\hat{H}$ e dell'Hamiltoniano dato da $ \\hat{H} = -\\frac{\\hbar^2}{2m} \\frac{d^2}{dx^2} + V(x)$ dove $V(x)  = \\frac{1}{2}m\\omega^2x^2$, che corrisponde al potenziale di un oscillatore armonico semplice.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    L'hamiltoniano di un oscillatore armonico è:\n",
        "\n",
        "    $\\hat{H} = -\\frac{\\hbar^2}{2m} \\frac{d^2}{dx^2} + \\frac{1}{2} m \\omega^2 x^2$\n",
        "\n",
        "    **Valore atteso dell'energia cinetica**\n",
        "\n",
        "    $ \\langle T \\rangle = -\\frac{\\hbar^2}{2m} \\int_{-\\infty}^{\\infty} \\Psi_\\text{trial}^* \\frac{d^2}{dx^2} \\Psi_\\text{trial} dx$\n",
        "\n",
        "    Prendendo la derivata seconda:\n",
        "\n",
        "    $\\frac{d}{dx} \\Psi_\\text{trial} = -2\\alpha x A e^{-\\alpha x^2}$\n",
        "\n",
        "    $\\frac{d^2}{dx^2} \\Psi_\\text{trial} = A e^{-\\alpha x^2} (4\\alpha^2 x^2 - 2\\alpha)$\n",
        "\n",
        "    Così:\n",
        "\n",
        "    $T = -\\frac{\\hbar^2}{2m} \\int_{-\\infty}^{\\infty} A^2 e^{-2\\alpha x^2} (4\\alpha^2 x^2 - 2\\alpha) dx$\n",
        "\n",
        "    Utilizzando i risultati dell'integrale gaussiano standard:\n",
        "\n",
        "    $\\langle T \\rangle = \\frac{\\hbar^2 \\alpha}{2m}$\n",
        "\n",
        "    **Valore atteso dell'energia potenziale**\n",
        "\n",
        "    $\\langle V \\rangle = \\frac{1}{2} m \\omega^2 \\int_{-\\infty}^{\\infty} x^2 |\\Psi_\\text{trial}|^2 dx$\n",
        "\n",
        "    Utilizzo:\n",
        "\n",
        "    $\\int_{-\\infty}^{\\infty} x^2 e^{-a x^2} dx = \\frac{\\sqrt{\\pi}}{2a^{3/2}}$\n",
        "\n",
        "    otteniamo:\n",
        "\n",
        "    $\\langle V \\rangle = \\frac{m \\omega^2}{4\\alpha}$\n",
        "\n",
        "    **Valore atteso dell'energia totale**\n",
        "\n",
        "    $\\therefore E_\\text{approx}(\\alpha) = \\frac{\\hbar^2 \\alpha}{2m} + \\frac{m \\omega^2}{4\\alpha}$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "(c) Utilizzare il principio variazionale per trovare l' $\\alpha$ e ottimale minimizzando l' $E_\\text{approx}(\\alpha)$.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    Ottimizzare l' $\\alpha$ e per ridurre al minimo il consumo energetico\n",
        "\n",
        "    **Fai la differenza**\n",
        "\n",
        "    $\\frac{d}{d\\alpha} \\left( \\frac{\\hbar^2 \\alpha}{2m} + \\frac{m \\omega^2}{4\\alpha} \\right) = 0$\n",
        "\n",
        "    Risolvere:\n",
        "\n",
        "    $\\frac{\\hbar^2}{2m} - \\frac{m \\omega^2}{4\\alpha^2} = 0$\n",
        "\n",
        "    $\\alpha_\\text{opt} = \\frac{m\\omega}{2\\hbar}$\n",
        "\n",
        "    Sostituendo $\\alpha_\\text{opt}$ in $E_\\text{approx}$ :\n",
        "\n",
        "    $\\therefore E_\\text{approx} = \\frac{\\hbar \\omega}{2}$\n",
        "\n",
        "    che corrisponde all'esatta energia di stato fondamentale dell'oscillatore armonico quantistico.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed2bd1ba-73ad-42d3-94ad-c1d4a7db0a6f",
      "metadata": {},
      "source": [
        "<span id=\"vqe-variational-quantum-eigensolver\" />\n",
        "\n",
        "### VQE (Risolvitore di autovalori quantistici variazionale)\n",
        "\n",
        "Il variational quantum eigensolver (VQE) è il metodo principale che utilizzeremo per esplorare il processo di $H+H = H_2$. Qui daremo un'occhiata a cos'è e come funziona il VQE. Ma prima soffermiamoci su una cosa molto importante attraverso la domanda di check-in.\n",
        "\n",
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Se abbiamo già tante strategie per i problemi di chimica, perché abbiamo bisogno di un computer quantistico? E qual è lo scopo dell'utilizzo congiunto di computer quantistici e classici?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    L'informatica quantistica ha la possibilità di rivoluzionare la chimica affrontando i problemi che i computer classici incontrano a causa della scalabilità esponenziale degli stati quantistici. Richard Feynman ha notoriamente osservato che per simulare la natura, le computazioni devono essere anche quantistiche \\[(rif. 1)].\n",
        "\n",
        "    Ad esempio, la simulazione della caffeina con il più semplice insieme di basi ( STO-3G ) richiederebbe $10^{48}$ bit, molto più grande del numero totale di stelle nell'universo osservabile ( $10^{24}$ ) \\[rif. 2]. Un computer quantistico può descrivere gli orbitali elettronici della caffeina con 160 qubit.\n",
        "\n",
        "    I computer quantistici elaborano naturalmente le interazioni quantistiche utilizzando la superposizione e l'entanglement, che rappresentano un modo promettente per consentire simulazioni molecolari accurate. Inoltre, possiamo combinare i vantaggi sia dei computer quantistici (simulazione di elettroni) sia dei computer classici (pre/post-processing dei dati, gestione dei processi algoritmici, ottimizzazione e così via). Si prevede che queste miglioreranno la scoperta di materiali, la progettazione di farmaci e la previsione di reazioni, riducendo i costosi esperimenti per tentativi ed errori. \\[rif. 3]\\[ref 4]\n",
        "\n",
        "    Se volete sapere perché i computer quantistici sono necessari per i problemi di chimica e perché utilizzare risorse di calcolo sia quantistiche che classiche, consultate i seguenti articoli:\n",
        "\n",
        "    * [Algoritmi emergenti di calcolo quantistico per la chimica quantistica](https://arxiv.org/abs/2109.02873)\n",
        "    * [La chimica oltre le soluzioni esatte su un supercomputer quantistico-centrico](https://arxiv.org/html/2405.05068v1)\n",
        "    * [Supercalcolo quantistico per la scienza dei materiali: Una prospettiva sulle sfide e le direzioni future](https://www.sciencedirect.com/science/article/pii/S0167739X24002012)\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n",
        "Torniamo ora alla VQE.\n",
        "\n",
        "Il VQE combina la potenza dei computer quantistici con quella dei computer classici, utilizzando fondamentalmente i principi variazionali per ottenere l'energia di stato fondamentale del sistema. Per comprendere la VQE, occorre innanzitutto suddividerla in tre parti:\n",
        "\n",
        "![Flusso di lavoro VQE](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/vqe/vqe.avif)\n",
        "\n",
        "<span id=\"quantum-observable-the-molecular-hamiltonian-energy-of-a-molecule\" />\n",
        "\n",
        "#### (Quantistico) Osservabile: l'Hamiltoniano molecolare (energia di una molecola)\n",
        "\n",
        "Nella VQE, l'hamiltoniana molecolare/atomica è un'osservabile, cioè possiamo misurarne il valore attraverso un esperimento. Il nostro obiettivo è trovare l'energia più bassa possibile (l'energia di stato fondamentale) della molecola. Per farlo, utilizziamo uno stato quantistico di prova, generato da un circuito quantistico parametrizzato (ansatz). Misuriamo l'osservabile e ottimizziamo lo stato quantistico fino a raggiungere l'energia più bassa possibile.\n",
        "\n",
        "Il set di basi utilizzato per l'hamiltoniana molecolare determina il numero di qubit necessari e influisce direttamente sulla precisione della VQE. La scelta del giusto set di basi è fondamentale per bilanciare efficienza e precisione. Per semplificare i calcoli senza modificare l'insieme di basi, possiamo utilizzare strategie come l'imposizione della simmetria e la riduzione dello spazio attivo. Molte molecole hanno forme simmetriche (come una farfalla o un fiocco di neve), il che significa che alcune parti si comportano allo stesso modo. Invece di calcolare tutto separatamente, possiamo concentrarci solo sulle parti uniche, risparmiando risorse quantistiche e sfruttando così la simmetria. Nella riduzione dello spazio attivo, consideriamo solo gli orbitali importanti, poiché non tutti gli elettroni hanno un impatto significativo sull'energia molecolare. Gli elettroni vicini al nucleo rimangono per lo più invariati, mentre altri influenzano il legame. Applicando questi metodi, possiamo rendere la VQE più efficiente, pur mantenendo l'accuratezza.\n",
        "\n",
        "Una volta ottenuta un'hamiltoniana molecolare utilizzando l'appropriato set di basi e le strategie di cui sopra, dobbiamo trasformare questa hamiltoniana in una adatta ai computer quantistici. La mappatura dei problemi con gli operatori di Pauli può essere piuttosto complicata. Questo è particolarmente vero nella chimica quantistica, che lavora con particelle indistinguibili (elettroni), poiché i qubit sono distinguibili. Non entreremo nei dettagli delle mappature in questa sede, ma vi rimandiamo alle seguenti risorse. Una discussione generale sulla mappatura di un problema in operatori quantistici si trova in [Quantum computing in practice](/learning/courses/quantum-computing-in-practice/index). Una discussione più dettagliata sulla mappatura dei problemi di chimica in operatori quantistici si trova in [Chimica quantistica con VQE](/learning/courses/quantum-chem-with-vqe/index).\n",
        "\n",
        "Per questo modulo, vi forniremo gli hamiltoniani appropriati (a un qubit) per $H$ e $H_2$ in modo da poterci concentrare sull'uso del computer quantistico. Queste Hamiltoniane a un qubit sono preparate utilizzando l'insieme di basi e la mappatura di Jordan-Wigner [STO-6G](https://en.wikipedia.org/wiki/STO-nG_basis_sets) e la [mappatura di Jordan-Wigner](https://en.wikipedia.org/wiki/Jordan%E2%80%93Wigner_transformation), che è la mappatura più diretta e con l'interpretazione fisica più semplice, poiché mappa l'occupazione di un orbitale di spin con l'occupazione di un qubit. Inoltre, abbiamo utilizzato una [tecnica di riduzione dei qubit utilizzando una simmetria dell'hamiltoniana](https://arxiv.org/abs/1701.08213), che sfrutta i modelli di comportamento delle occupazioni di spin per ridurre il numero di qubit. Per la molecola $H_2$ si assume che la distanza tra i due atomi di idrogeno sia `0.735` $\\mathring A$.\n",
        "\n",
        "<span id=\"quantum-ansatz-the-trial-wave-function-how-to-build-a-trivial-quantum-state-with-a-quantum-circuit\" />\n",
        "\n",
        "#### (Quantum) Ansatz: La funzione d'onda di prova (Come costruire uno stato quantistico banale con un circuito quantistico)\n",
        "\n",
        "Per il VQE, l'ansatz (plurale: ansätze) consiste in due componenti chiave. Il primo è la preparazione dello stato iniziale, che stabilisce lo stato del qubit applicando porte quantistiche senza alcun parametro variazionale. Il secondo componente è il circuito quantistico parametrizzato, uno speciale circuito quantistico con parametri regolabili, simili ai quadranti di una radio. Questi parametri saranno utilizzati per l'ultima parte - l'ottimizzatore classico - per aiutarci a raggiungere il miglior stato fondamentale possibile.\n",
        "\n",
        "Nella sezione dedicata al principio variazionale, abbiamo appreso che la qualità dello stato di prova influisce sulla qualità dei risultati dell'algoritmo variazionale. Ciò significa che la scelta di una buona ansatz è importante nella VQE. Ancora una volta, si tratta di un argomento ricco e complesso. Non tratteremo in questa sede i diversi tipi di ansatz e le loro origini. Se siete interessati a saperne di più sui circuiti quantistici parametrizzati e sugli ansatz, potete esplorare la lezione [Ansatz e forma variazionale](/learning/courses/variational-algorithm-design/ansaetze-and-variational-forms) del [corso Progettazione di algoritmi variazionali](/learning/courses/variational-algorithm-design/index), che fornisce spiegazioni dettagliate ed esempi di ansätze.\n",
        "\n",
        "Poiché in questo modulo utilizzeremo un'hamiltoniana a un qubit, abbiamo bisogno di un circuito quantistico parametrizzato a un qubit come ansatz. Nella sezione seguente vedremo tre tipi di ansätze a un qubit. Li confronteremo e discuteremo le considerazioni chiave per la scelta di un ansatz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a8fae507-41e6-4585-8e9d-b45db5055fb9",
      "metadata": {},
      "source": [
        "<span id=\"classical-optimizer-fine-tuning-the-quantum-circuit\" />\n",
        "\n",
        "#### Ottimizzatore (classico): messa a punto del circuito quantistico\n",
        "\n",
        "Una volta che il computer quantistico misura l'energia dell'osservabile dall'ansatz, i parametri dell'ansatz e il valore dell'energia vengono inviati all'ottimizzatore classico per la messa a punto. Questo processo di ottimizzazione viene eseguito su un computer classico, in genere utilizzando pacchetti scientifici generici come SciPy.\n",
        "\n",
        "L'ottimizzatore classico tratta l'energia misurata come una funzione di costo. Nei problemi di ottimizzazione, una funzione di costo (talvolta chiamata anche funzione obiettivo) è una funzione matematica che misura la \"bontà\" di una particolare soluzione. L'obiettivo dell'ottimizzatore è trovare l'insieme di parametri che minimizzi questa funzione di costo. Nel contesto della ricerca dell'energia di stato fondamentale di una molecola, l'energia stessa funge da funzione di costo: vogliamo trovare i parametri del nostro circuito quantistico (la nostra \"soluzione\") che producano l'energia più bassa possibile. L'ottimizzatore classico utilizza questo valore di energia misurato (il costo) e determina la successiva serie di parametri ottimizzati per l'ansatz quantistico. I parametri aggiornati vengono quindi inviati nuovamente al circuito quantistico e il processo si ripete. A ogni iterazione, l'ottimizzatore classico regola i parametri per cercare di ridurre l'energia (minimizzare la funzione di costo) fino a quando non viene soddisfatto un criterio di convergenza predefinito, garantendo idealmente che venga trovata l'energia più bassa possibile (corrispondente allo stato fondamentale della molecola per quella distanza di legame e quell'insieme di basi).\n",
        "\n",
        "Esistono molte strategie di ottimizzazione fornite da pacchetti scientifici come SciPy. Per saperne di più, consultate la lezione sui [cicli di ottimizzazione](/learning/courses/variational-algorithm-design/optimization-loops) del corso di [progettazione di algoritmi variazionali](/learning/courses/variational-algorithm-design/index). Qui utilizzeremo COBYLA (Constrained Optimization BY Linear Approximations), un algoritmo di ottimizzazione adatto a paesaggi energetici complicati. In particolare, COBYLA non tenta di calcolare il gradiente della funzione studiata; si tratta di un ottimizzatore senza gradiente. Immaginate di cercare la vetta più alta di una catena montuosa a occhi chiusi. Poiché non si può vedere l'intero paesaggio, si fanno piccoli passi in diverse direzioni, controllando se si sta salendo o scendendo. COBYLA funziona in modo simile: si muove nello spazio dei parametri, testando diversi valori, migliorando gradualmente il risultato fino a trovare il migliore.\n",
        "\n",
        "A questo punto si è pronti per eseguire un calcolo VQE. A tal fine, provate la domanda di check-in qui sotto, che riassume l'intero processo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8cdcfcc0-a6ca-4153-a11a-bd7b598f9b35",
      "metadata": {},
      "source": [
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Inserisci i termini corretti negli spazi vuoti per completare il riassunto del processo VQE, quindi clicca per verificare le tue risposte.\n",
        "\n",
        "Il VQE è un algoritmo quantistico variazionale, che combina la potenza del (1) \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\\\\code(0144) e del calcolo classico, utilizzato per trovare il (2) \\_\\_\\_\\_\\_\\_\\_\\\\\\\\\\code(0144) di una molecola. Il processo inizia con la definizione della (3) \\\\\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\_\\\\\\code(0144)}, che rappresenta l'energia totale del sistema e funge da osservabile nelle misure quantistiche. Next, we prepare an (4) \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_, a quantum circuit with adjustable parameters that represents the trial wave function of the molecule. Questi parametri sono ottimizzati con un algoritmo classico che regola iterativamente i parametri per minimizzare l'energia misurata. Nella discussione precedente abbiamo utilizzato l'ottimizzatore (6), che affina i parametri dell'ansatz senza bisogno di calcoli di derivazione. The process continues until we reach (7) \\_\\_\\_\\_\\_\\_\\_\\_\\_\\_, meaning we have found the lowest possible energy of the molecule.\n",
        "\n",
        "Banca delle parole:\n",
        "\n",
        "* ottimizzatore classico\n",
        "* energia dello stato fondamentale\n",
        "* efficiente dal punto di vista dell'hardware\n",
        "* ansatz\n",
        "* hamiltoniana molecolare\n",
        "* COBYLA\n",
        "* calcolo quantistico\n",
        "* convergenza\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    1 → calcolo quantistico\n",
        "\n",
        "    2 → energia dello stato fondamentale\n",
        "\n",
        "    3 → hamiltoniana molecolare\n",
        "\n",
        "    4 → ansatz\n",
        "\n",
        "    5 → ottimizzatore classico\n",
        "\n",
        "    6 → COBYLA\n",
        "\n",
        "    7 → convergenza\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64a090f7-1d18-4edb-bef3-4cea0f4cec2c",
      "metadata": {},
      "source": [
        "<span id=\"compute-the-ground-state-energy-of-a-hydrogen-atom-with-vqe\" />\n",
        "\n",
        "## Calcolare l'energia dello stato fondamentale di un atomo di idrogeno con VQE\n",
        "\n",
        "Utilizziamo ora quanto appreso per calcolare l'energia di stato fondamentale di un atomo di idrogeno. Nel corso del modulo si utilizzerà un framework per l'informatica quantistica noto come \"Qiskit patterns\", che suddivide i flussi di lavoro nelle seguenti fasi:\n",
        "\n",
        "* Fase 1: mappare gli input classici in un problema quantistico\n",
        "* Fase 2: Ottimizzazione del problema per l'esecuzione quantistica\n",
        "* Fase 3: Esecuzione tramite le primitive \" IBM Quantum \"\n",
        "* Fase 4: post-elaborazione e analisi classica\n",
        "\n",
        "![Modello Qiskit](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/vqe/patterns.svg)\n",
        "\n",
        "In genere seguiamo questi passaggi.\n",
        "\n",
        "Cominciamo installando alcuni pacchetti necessari, tra cui le primitive di IBM Quantum. Sceglieremo inoltre il computer quantistico meno carico tra quelli a nostra disposizione.\n",
        "\n",
        "Di seguito è riportato un codice per salvare le credenziali al primo utilizzo. Assicurarsi di eliminare queste informazioni dal blocco note dopo averlo salvato nel proprio ambiente, in modo che le credenziali non vengano accidentalmente condivise quando si condivide il blocco note. Per ulteriori informazioni, vedere [Configurazione dell'account IBM Cloud](/docs/guides/initialize-account) e [Inizializzazione del servizio in un ambiente non attendibile](/docs/guides/cloud-setup-untrusted).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "18d9c613-ee5f-4e38-bc83-57d25197b200",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "ibm_brisbane\n"
          ]
        }
      ],
      "source": [
        "# Load IBM Quantum Compute Service\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "\n",
        "# Load the Runtime primitive and session\n",
        "from qiskit_ibm_runtime import EstimatorV2 as Estimator\n",
        "\n",
        "# Syntax for first saving your token.  Delete these lines after saving your credentials.\n",
        "# QiskitRuntimeService.save_account(channel='ibm_quantum_platform',\n",
        "# instance = '<YOUR_IBM_INSTANCE_CRN>', token='<YOUR-API_KEY>', overwrite=True, set_as_default=True)\n",
        "# service = QiskitRuntimeService(channel='ibm_quantum_platform')\n",
        "\n",
        "# Load saved credentials\n",
        "service = QiskitRuntimeService()\n",
        "\n",
        "# Use the least busy backend, or uncomment the loading of a specific backend like \"ibm_brisbane\".\n",
        "backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)\n",
        "# backend = service.backend(\"ibm_brisbane\")\n",
        "print(backend.name)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0c4e0ac4-7f4c-4cab-b726-1d6fe1ca10ac",
      "metadata": {},
      "source": [
        "La cella sottostante consente di passare dall'uso del simulatore a quello dell'hardware reale nel corso del notebook. Si consiglia di eseguirlo subito:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "4c38f002-c6e6-4fe2-acf1-3cccf3d1f525",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Load the Aer simulator and generate a noise model based on the currently-selected backend.\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel\n",
        "\n",
        "# Alternatively, load a fake backend with generic properties and define a simulator.\n",
        "\n",
        "\n",
        "noise_model = NoiseModel.from_backend(backend)\n",
        "\n",
        "# Define a simulator using Aer, and use it in Sampler.\n",
        "backend_sim = AerSimulator(noise_model=noise_model)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "497acce3-f34c-43b3-a014-630702a3620b",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-the-problem-to-quantum-circuits-and-operators\" />\n",
        "\n",
        "### Fase 1: Mappare il problema su circuiti quantistici e operatori\n",
        "\n",
        "Iniziamo il nostro calcolo VQE definendo l'hamiltoniano della molecola di idrogeno ( $H_2$ ) a una distanza di legame specifica. Questo hamiltoniano rappresenta l'energia totale del sistema in termini di operatori di qubit, essendo stato ricavato e mappato dal sistema molecolare mediante una procedura standard: 1) utilizzando il set di basi di STO-6G (una specifica raccolta di funzioni matematiche impiegate per approssimare gli orbitali elettronici), 2) applicando la mappatura di Jordan-Wigner (una tecnica per tradurre gli operatori fermionici che descrivono gli elettroni in operatori di qubit), e 3) eseguendo la riduzione dei qubit utilizzando la parità dell'Hamiltoniano per semplificare il problema.\n",
        "\n",
        "Come abbiamo spiegato in precedenza, le energie di stato fondamentale calcolate dipendono fortemente dalla selezione del set di basi e dalla geometria molecolare (come la distanza di legame). Per questa specifica configurazione e dopo queste trasformazioni, l'hamiltoniana del qubit risultante è semplice:\n",
        "\n",
        "$\\hat{H} = -0.2355 I + 0.2355 Z$\n",
        "\n",
        "Qui, $I$ rappresenta l'operatore identità e $Z$ rappresenta l'operatore Pauli-Z, che agisce su un singolo qubit. I coefficienti sono derivati dagli integrali calcolati utilizzando il set di basi STO-6G a questa particolare distanza di legame con una trasformazione appropriata.\n",
        "\n",
        "Definita l'hamiltoniana, possiamo ora utilizzare il VQE per calcolare l'energia del suo stato fondamentale. È utile confrontare l'energia di stato fondamentale calcolata con i valori attesi. Per un singolo atomo di idrogeno isolato (H), l'energia di stato fondamentale è esattamente -0.5 Hartree (in assenza di effetti relativistici). Calcoliamo l'esatta energia di stato fondamentale della *nostra specifica hamiltoniana di qubit* definita sopra e confrontiamola con i valori noti.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "aaef7daa-6dbe-4a2e-abe2-3ad2a0baa860",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The exact ground state energy of the Hamiltonian is  -0.471 hartree\n"
          ]
        }
      ],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "import numpy as np\n",
        "\n",
        "# Qubit Hamiltonian of the hydrogen atom generated by using STO-3G basis set and parity mapping\n",
        "Hamiltonian = SparsePauliOp.from_list([(\"I\", -0.2355), (\"Z\", 0.2355)])\n",
        "\n",
        "# exact ground state energy of Hamiltonian\n",
        "\n",
        "A = np.array(Hamiltonian)\n",
        "eigenvalues, eigenvectors = np.linalg.eig(A)\n",
        "print(\n",
        "    \"The exact ground state energy of the Hamiltonian is \",\n",
        "    min(eigenvalues).real,\n",
        "    \"hartree\",\n",
        ")\n",
        "h = min(eigenvalues.real)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ec53e133-d8ab-4e5d-a44a-428fe8095aee",
      "metadata": {},
      "source": [
        "Quindi, abbiamo bisogno di un circuito quantistico parametrizzato, un ansatz, per preparare una funzione d'onda di prova $\\Psi_\\text{trial}$ per lo stato fondamentale. L'obiettivo è trovare i parametri $\\theta$ che minimizzano il valore di aspettativa dell'energia $\\langle\\psi(\\theta)|\\hat{H}|\\psi(\\theta)\\rangle$. La scelta dell'ansatz è cruciale perché determina l'insieme dei possibili stati quantistici che il nostro circuito può preparare. Un \"buon\" ansatz è quello sufficientemente flessibile da rappresentare uno stato molto vicino al vero stato fondamentale dell'hamiltoniana che stiamo studiando, ma non così complesso da richiedere troppi parametri o circuiti troppo profondi per gli attuali computer quantistici.\n",
        "\n",
        "Qui proveremo tre diverse ansätze a un qubit per vedere quale fornisce una migliore \"copertura\" dei possibili stati quantistici in cui può trovarsi un singolo qubit. La \"copertura\" si riferisce alla gamma di stati quantistici che il circuito ansatz può produrre variando i suoi parametri.\n",
        "\n",
        "Utilizzeremo tre ansätze basate su diverse combinazioni di porte rotazionali a singolo qubit:\n",
        "\n",
        "* Ansatz del cancello rotazionale a 1 asse: questo ansatz utilizza rotazioni intorno a un solo asse ( $R_x(\\theta)$ ). Sulla sfera di Bloch, ciò corrisponde a muoversi solo lungo un cerchio specifico. È il meno flessibile e copre un insieme limitato di Stati.\n",
        "* Due ansätze per cancelli rotazionali a 2 assi: queste ansätze combinano rotazioni intorno a due assi diversi ( $R_x(\\theta_1) R_z(\\theta_2)$ e $R_x(\\theta_1) R_z(\\theta_2) R_x(\\theta_3)$ ). Questo ci permette di raggiungere una porzione più ampia della sfera di Bloch, rispetto a una rotazione su un solo asse.\n",
        "\n",
        "Confrontando i risultati VQE ottenuti con queste tre ansätze, possiamo vedere come la flessibilità e la copertura dello spazio degli stati dell'ansatz influiscano sulla nostra capacità di trovare la vera energia di stato fondamentale della nostra hamiltoniana semplificata. Un surrogato più flessibile ha *il potenziale* per trovare un'approssimazione migliore, ma potrebbe anche essere più difficile per l'ottimizzatore classico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "653b257f-9975-4bd3-9c48-a046ec4a9fae",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<qiskit.circuit.instructionset.InstructionSet at 0x1059def80>"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.circuit import Parameter\n",
        "from qiskit.quantum_info import Statevector, DensityMatrix, Pauli\n",
        "\n",
        "theta = Parameter(\"θ\")\n",
        "phi = Parameter(\"φ\")\n",
        "lam = Parameter(\"λ\")\n",
        "\n",
        "ansatz1 = QuantumCircuit(1)\n",
        "ansatz1.rx(theta, 0)\n",
        "\n",
        "ansatz2 = QuantumCircuit(1)\n",
        "ansatz2.rx(theta, 0)\n",
        "ansatz2.rz(phi, 0)\n",
        "\n",
        "ansatz3 = QuantumCircuit(1)\n",
        "ansatz3.rx(theta, 0)\n",
        "ansatz3.rz(phi, 0)\n",
        "ansatz3.rx(lam, 0)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "26707a66-7098-4f57-93fc-eb1cb4c305f7",
      "metadata": {},
      "source": [
        "Ora, generiamo 5000 numeri casuali per ogni parametro e tracciamo la distribuzione degli stati quantici casuali, generati dalle tre ansätze con questi parametri casuali. Si può pensare a questi parametri come a rotazioni intorno a diversi assi su una superficie sferica. Per vedere la distribuzione dello stato quantistico, utilizzeremo [la Sfera di Bloch](https://en.wikipedia.org/wiki/Bloch_sphere), una sfera tridimensionale che mostra lo stato di un singolo qubit. Ogni punto della sfera rappresenta un possibile stato del qubit, dove i poli nord e sud sono come i classici \"0\" e \"1\", ma il qubit può anche trovarsi in qualsiasi punto intermedio, mostrando speciali proprietà quantistiche come la sovrapposizione. Innanzitutto, preparare le funzioni necessarie per tracciare la sfera di Bloch 3D e preparare 5000 parametri casuali.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "6e47463b-b0c2-44cd-b099-9f50f38ebd0e",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "\n",
        "def plot_bloch(bloch_vectors):\n",
        "    # Extract X, Y, Z coordinates for 3D projection\n",
        "    X_coords = bloch_vectors[:, 0]\n",
        "    Z_coords = bloch_vectors[:, 2]\n",
        "\n",
        "    # Compute Y coordinates from X and Z to approximate the full Bloch sphere projection\n",
        "    Y_coords = bloch_vectors[:, 1]\n",
        "\n",
        "    # Create 3D plot\n",
        "    fig = plt.figure(figsize=(8, 8))\n",
        "    ax = fig.add_subplot(111, projection=\"3d\")\n",
        "    ax.scatter(X_coords, Y_coords, Z_coords, color=\"blue\", alpha=0.6)\n",
        "\n",
        "    # Labels and title\n",
        "    ax.set_xlabel(\"X\")\n",
        "    ax.set_ylabel(\"Y\")\n",
        "    ax.set_zlabel(\"Z\")\n",
        "    ax.set_title(\"Parameterized 1-Qubit Circuit on 3D Bloch Sphere\")\n",
        "\n",
        "    # Set axis limits and make them equal\n",
        "    ax.set_xlim([-1, 1])\n",
        "    ax.set_ylim([-1, 1])\n",
        "    ax.set_zlim([-1, 1])\n",
        "\n",
        "    # Ensure equal aspect ratio for all axes\n",
        "    ax.set_box_aspect([1, 1, 1])  # Equal scaling for x, y, z axes\n",
        "\n",
        "    # Show grid\n",
        "    ax.grid(True)\n",
        "\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "num_samples = 5000  # Number of random states\n",
        "theta_vals = np.random.uniform(0, 2 * np.pi, num_samples)\n",
        "phi_vals = np.random.uniform(0, 2 * np.pi, num_samples)\n",
        "lam_vals = np.random.uniform(0, 2 * np.pi, num_samples)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "af24b916-fa42-4c4c-8e22-098adfb4fcd5",
      "metadata": {},
      "source": [
        "Vediamo come funziona il nostro primo ansatz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "6471a5d1-287a-4726-9e65-e23ef77ccf75",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/f7d886a0-d410-418d-a7a3-81a9134cbfd0-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# List to store Bloch Sphere XZ coordinates\n",
        "bloch_vectors = []\n",
        "\n",
        "# Generate quantum states and extract Bloch vectors\n",
        "for i in range(num_samples):\n",
        "    # Create a circuit and bind parameters\n",
        "    qc = ansatz1\n",
        "    bound_qc = qc.assign_parameters({theta: theta_vals[i]})  # , lam: lam_vals[i]})\n",
        "    state = Statevector.from_instruction(bound_qc)\n",
        "    rho = DensityMatrix(state)\n",
        "\n",
        "    X = rho.expectation_value(Pauli(\"X\")).real\n",
        "    Y = rho.expectation_value(Pauli(\"Y\")).real\n",
        "    Z = rho.expectation_value(Pauli(\"Z\")).real\n",
        "    bloch_vectors.append([X, Y, Z])  # Store X, Z components\n",
        "\n",
        "# Convert to a numpy array for plotting\n",
        "bloch_vectors = np.array(bloch_vectors)\n",
        "\n",
        "plot_bloch(bloch_vectors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9cb7d81e-8df5-4557-8452-98f794bdc5cd",
      "metadata": {},
      "source": [
        "Possiamo vedere che il nostro primo ansatz restituisce una forma ad anello di stati quantici distribuiti della sfera di Bloch. Questo ha senso, perché abbiamo dato all'ansatz un solo parametro rotazionale. Può quindi produrre solo stati ruotati attorno a un asse. Partendo dal punto $(0,0,1)$ e ruotando attorno a un asse si ottiene sempre un anello. Verifichiamo quindi il nostro secondo ansatz, che presenta due porte di rotazione ortogonali - `Rx` e `Rz`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "9cd86049-f652-4e76-a68a-2780cb7782db",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/607e4c0f-cb14-4036-901b-021cc8e11b79-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "bloch_vectors = []\n",
        "\n",
        "# Generate quantum states and extract Bloch vectors\n",
        "for i in range(num_samples):\n",
        "    # Create circuit and bind parameters\n",
        "    qc = ansatz2\n",
        "    bound_qc = qc.assign_parameters(\n",
        "        {theta: theta_vals[i], phi: phi_vals[i]}\n",
        "    )  # , lam: lam_vals[i]})\n",
        "    state = Statevector.from_instruction(bound_qc)\n",
        "    rho = DensityMatrix(state)\n",
        "\n",
        "    X = rho.expectation_value(Pauli(\"X\")).real\n",
        "    Y = rho.expectation_value(Pauli(\"Y\")).real\n",
        "    Z = rho.expectation_value(Pauli(\"Z\")).real\n",
        "    bloch_vectors.append([X, Y, Z])  # Store X, Z components\n",
        "\n",
        "# Convert to numpy array for plotting\n",
        "bloch_vectors = np.array(bloch_vectors)\n",
        "\n",
        "plot_bloch(bloch_vectors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e3fde737-d565-4200-a968-c6a6c78dfe30",
      "metadata": {},
      "source": [
        "Qui possiamo vedere che il nostro secondo ansatz copre una porzione più ampia della sfera di Bloch, ma notiamo che i punti sono più concentrati intorno ai poli e più sparsi intorno all'equatore. Ora è il momento di verificare il nostro ultimo ansatz.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "ec2e9a84-46b2-4143-8cde-d981e9edd3ce",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/21ff021a-7cfe-4ba9-a827-edac71c0871c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "bloch_vectors = []\n",
        "\n",
        "# Generate quantum states and extract Bloch vectors\n",
        "for i in range(num_samples):\n",
        "    # Create circuit and bind parameters\n",
        "    qc = ansatz3\n",
        "    bound_qc = qc.assign_parameters(\n",
        "        {theta: theta_vals[i], phi: phi_vals[i], lam: lam_vals[i]}\n",
        "    )\n",
        "    state = Statevector.from_instruction(bound_qc)\n",
        "    rho = DensityMatrix(state)\n",
        "\n",
        "    X = rho.expectation_value(Pauli(\"X\")).real\n",
        "    Y = rho.expectation_value(Pauli(\"Y\")).real\n",
        "    Z = rho.expectation_value(Pauli(\"Z\")).real\n",
        "    bloch_vectors.append([X, Y, Z])  # Store X, Z components\n",
        "\n",
        "# Convert to numpy array for plotting\n",
        "bloch_vectors = np.array(bloch_vectors)\n",
        "\n",
        "plot_bloch(bloch_vectors)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ca886db5-e7c7-4f5c-8594-50f79fbc61dd",
      "metadata": {},
      "source": [
        "Qui si possono vedere stati quantici più uniformemente distribuiti generati dal nostro ultimo ansatz.\n",
        "\n",
        "Come già detto, la cosa migliore da fare è acquisire conoscenze sullo stato fondamentale che si sta cercando e utilizzare un'ansatz adatta a sondare gli stati vicini a quello fondamentale. Per esempio, se sapessimo che il nostro stato fondamentale è vicino a un polo, potremmo scegliere l'ansatz 2. Per semplicità, ci atterremo all'ansatz 3, che sonda uniformemente l'intera sfera di Bloch.\n",
        "\n",
        "Ora che abbiamo scelto la nostra maschera, disegniamo il circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9f0c76d9-b4b2-46dc-a261-7c624c031177",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "This circuit has  3 parameters\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/20221e72-51dc-49d9-8a09-9b64de7f535f-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 12,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Pre-defined ansatz circuit and operator class for Hamiltonian\n",
        "\n",
        "ansatz = ansatz3\n",
        "\n",
        "num_params = ansatz.num_parameters\n",
        "print(\"This circuit has \", num_params, \"parameters\")\n",
        "\n",
        "ansatz.draw(\"mpl\", style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b7dc624b-37bc-4523-8532-e45c7826ce64",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-for-target-hardware\" />\n",
        "\n",
        "### Passaggio 2: Ottimizzazione per l'hardware di destinazione\n",
        "\n",
        "Quando si esegue un calcolo su un vero computer quantistico, non ci interessa solo la logica del circuito quantistico. Ci interessa anche sapere quali operazioni possono essere eseguite da quel particolare computer quantistico e in quale punto del computer quantistico si trovano i qubit che stiamo utilizzando. Sono proprio uno accanto all'altro? Sono molto distanti tra loro? Pertanto, il passo successivo è quello di riscrivere il nostro circuito utilizzando porte naturali per il computer quantistico che utilizzeremo e tenendo conto della disposizione dei qubit. Questo può essere fatto da `transpilation` - dopo questo processo, si può vedere il nostro semplice ansatz convertito in un diverso insieme di porte, e i nostri qubit astratti saranno mappati in qubit fisici su un vero computer quantistico.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "d4f50ff9-cdf4-483f-9bb8-5814fe5acc53",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backend: {config.backend_name}\n",
            "Native gates:  ['ecr', 'id', 'delay', 'measure', 'reset', 'rz', 'sx', 'x'] ,\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/b5e08232-d651-4df4-bcb7-533513144eff-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "config = backend.configuration()\n",
        "\n",
        "print(\"Backend: {config.backend_name}\")\n",
        "print(\"Native gates: \", config.supported_instructions, \",\")\n",
        "\n",
        "\n",
        "target = backend.target\n",
        "\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "\n",
        "ansatz_isa = pm.run(ansatz)\n",
        "\n",
        "ansatz_isa.draw(output=\"mpl\", idle_wires=False, style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "04feccf8-4591-4919-90ba-9b0d72e1b436",
      "metadata": {},
      "source": [
        "Si può notare che le porte `rx, rz` del nostro ansatz sono state convertite in una serie di porte `rz, sx` , che sono le porte native del nostro backend. Inoltre, si può notare che il nostro `q0` è ora mappato nel quinto qubit fisico. Dobbiamo anche mappare la nostra Hamiltoniana in base a queste modifiche, come nel codice seguente:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "2e105b4d-8c3c-4b1e-9652-9857ee9e5465",
      "metadata": {},
      "outputs": [],
      "source": [
        "Hamiltonian_isa = Hamiltonian.apply_layout(layout=ansatz_isa.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d3212dfa-da89-43d6-85ce-6200ecf9ca09",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-on-target-hardware\" />\n",
        "\n",
        "### Fase 3: Esecuzione sull'hardware di destinazione\n",
        "\n",
        "Ora è il momento di eseguire il nostro VQE su una vera QPU. A tal fine, è necessaria una funzione di costo per il processo di ottimizzazione, che valuti il valore di aspettativa dell'hamiltoniana con uno stato quantistico, generato dall'ansatz. Non ti preoccupare! Non è necessario codificare tutto da soli. Abbiamo preparato una funzione per questo scopo e tutto ciò che dovete fare è eseguire la cella sottostante.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "6b2178bd-6e86-4178-b82b-f7a6d7e95022",
      "metadata": {},
      "outputs": [],
      "source": [
        "def cost_func(params, ansatz, hamiltonian, estimator):\n",
        "    \"\"\"Return estimate of energy from estimator\n",
        "\n",
        "    Parameters:\n",
        "        params (ndarray): Array of ansatz parameters\n",
        "        ansatz (QuantumCircuit): Parameterized ansatz circuit\n",
        "        hamiltonian (SparsePauliOp): Operator representation of Hamiltonian\n",
        "        estimator (EstimatorV2): Estimator primitive instance\n",
        "        cost_history_dict: Dictionary for storing intermediate results\n",
        "\n",
        "    Returns:\n",
        "        float: Energy estimate\n",
        "    \"\"\"\n",
        "    pub = (ansatz, [hamiltonian], [params])\n",
        "    result = estimator.run(pubs=[pub]).result()\n",
        "    energy = result[0].data.evs[0]\n",
        "\n",
        "    cost_history_dict[\"iters\"] += 1\n",
        "    cost_history_dict[\"prev_vector\"] = params\n",
        "    cost_history_dict[\"cost_history\"].append(energy)\n",
        "    print(f\"Iters. done: {cost_history_dict['iters']} [Current cost: {energy}]\")\n",
        "\n",
        "    return energy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "83ed09be-d475-4d66-b55f-b25bbf3412c8",
      "metadata": {},
      "source": [
        "Infine, prepariamo i parametri iniziali per il nostro ansatz e il suo processo di ottimizzazione. È possibile utilizzare semplicemente tutti gli zeri o valori casuali. Abbiamo selezionato i parametri iniziali di seguito, ma sentitevi liberi di commentare o decommentare le righe nella cella per campionare i parametri in modo casuale, uniformemente da 0 a $2\\pi$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "b2a5dab1-f724-48df-b84b-20749ffd5261",
      "metadata": {},
      "outputs": [],
      "source": [
        "# x0 = np.random.uniform(0, 2*pi, 3)\n",
        "x0 = [1, 1, 0]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "5b48b37d-1f1e-4aef-9fd5-ba594db832b0",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Iters. done: 1 [Current cost: -0.3361517318448143]\n",
            "Iters. done: 2 [Current cost: -0.4682546422099432]\n",
            "Iters. done: 3 [Current cost: -0.38985802144149584]\n",
            "Iters. done: 4 [Current cost: -0.38319217316749354]\n",
            "Iters. done: 5 [Current cost: -0.4628720756579032]\n",
            "Iters. done: 6 [Current cost: -0.4683301936226905]\n",
            "Iters. done: 7 [Current cost: -0.45480498699294747]\n",
            "Iters. done: 8 [Current cost: -0.4690533242050814]\n",
            "Iters. done: 9 [Current cost: -0.465867415110354]\n",
            "Iters. done: 10 [Current cost: -0.4606882723137227]\n"
          ]
        }
      ],
      "source": [
        "# QPU Est. 2min for ibm_brisbane\n",
        "\n",
        "from scipy.optimize import minimize\n",
        "from qiskit_ibm_runtime import Batch\n",
        "\n",
        "batch = Batch(backend=backend)\n",
        "\n",
        "cost_history_dict = {\n",
        "    \"prev_vector\": None,\n",
        "    \"iters\": 0,\n",
        "    \"cost_history\": [],\n",
        "}\n",
        "estimator = Estimator(mode=batch)\n",
        "estimator.options.default_shots = 10000\n",
        "\n",
        "res = minimize(\n",
        "    cost_func,\n",
        "    x0,\n",
        "    args=(ansatz_isa, Hamiltonian_isa, estimator),\n",
        "    method=\"cobyla\",\n",
        "    options={\"maxiter\": 10, \"tol\": 0.01},\n",
        ")\n",
        "\n",
        "batch.close()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "124e15d2-ddce-4201-9f69-03c9e5612644",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The reference ground state energy is  (-0.471+0j)\n",
            "The computed ground state energy is  -0.4690533242050814\n"
          ]
        }
      ],
      "source": [
        "h_vqe = res.fun\n",
        "print(\"The reference ground state energy is \", min(eigenvalues))\n",
        "print(\"The computed ground state energy is \", h_vqe)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "79bce65c-d84e-4bed-bfe8-71f448290fd1",
      "metadata": {},
      "source": [
        "Congratulazioni. Avete appena terminato con successo il vostro primo esperimento di chimica quantistica. Si nota una differenza tra l'energia esatta dello stato fondamentale dell'hamiltoniana e la nostra, ma poiché abbiamo utilizzato una tecnica di attenuazione degli errori predefinita (che corregge gli errori di lettura), la differenza è minima. Questo è un ottimo inizio!\n",
        "\n",
        "Nota: è possibile ottenere un risultato migliore impostando un livello di mitigazione degli errori tramite [`resilience_level`](/docs/guides/error-mitigation-and-suppression-techniques). Il valore predefinito è 1; se si imposta un valore più alto, verrà impiegato più tempo di elaborazione della QPU, ma si potrebbe ottenere un risultato migliore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dae9eeb5-a2ac-4632-90cf-d2ac70182c7b",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process\" />\n",
        "\n",
        "### Fase 4: Post-elaborazione\n",
        "\n",
        "È ora di dare un'occhiata al funzionamento del nostro ottimizzatore classico. Eseguite la cella sottostante e osservate il modello di convergenza.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "b6b32cce-cbfa-4566-9d77-e511f54cc558",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/bee036cf-1255-4b4c-881b-cae6e903e09a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, ax = plt.subplots()\n",
        "x = np.linspace(0, 10, 10)\n",
        "\n",
        "# Define the constant function\n",
        "y_constant = np.full_like(x, h)\n",
        "ax.plot(\n",
        "    range(cost_history_dict[\"iters\"]), cost_history_dict[\"cost_history\"], label=\"VQE\"\n",
        ")\n",
        "ax.set_xlabel(\"Iterations\")\n",
        "ax.set_ylabel(\"Cost (Hartree)\")\n",
        "ax.plot(y_constant, label=\"Target\")\n",
        "plt.legend()\n",
        "plt.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "909ed768-080d-42b2-aae5-0fe6fa4f088b",
      "metadata": {},
      "source": [
        "Siamo partiti da un valore iniziale abbastanza buono, tale da ottenere un buon valore finale in soli 10 passaggi. Si possono notare picchi grandi e piccoli, e questa è la caratteristica tipica dell'ottimizzatore COBYLA: cerca nello spazio come se non vedesse il paesaggio e regola le dimensioni dei passi a ogni misurazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4b3fe46b-fb4e-48d4-a891-732231429926",
      "metadata": {},
      "source": [
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Qual è la sua osservazione? Quale parte del processo sopra descritto può essere migliorata per ottenere risultati più vicini ai valori teorici o all'esatta energia di stato fondamentale dell'hamiltoniana? Quali sono le cose da considerare a questo proposito?\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    La prima cosa da considerare è la modifica dell'insieme di basi utilizzate per calcolare l'hamiltoniana delle molecole. Come già detto, l'energia di stato fondamentale dell'atomo di H è -0.5 Hartree, come è noto, e la base STO-6G che abbiamo scelto non è sufficiente per ricavare con precisione questo valore.\n",
        "\n",
        "    La scelta di un tipo di base più complesso aumenta il numero di qubit utilizzati dall'hamiltoniana; di conseguenza, è necessario selezionare un'ansatz più complessa e adatta ai problemi di chimica.\n",
        "\n",
        "    Il prossimo aspetto da ottimizzare è la gestione del rumore nella QPU. Le tecniche di mitigazione degli errori più avanzate danno risultati migliori, ma possono richiedere più tempo per essere utilizzate. Considerate anche come il sito `shot_number` influisce sui risultati.\n",
        "\n",
        "    Infine, è possibile ottenere migliori prestazioni di convergenza provando diversi ottimizzatori.\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "52e936bb-a9af-4355-94a1-82393a25124e",
      "metadata": {},
      "source": [
        "<span id=\"compute-the-ground-state-energy-of-the-hydrogen-molecule-with-vqe\" />\n",
        "\n",
        "## Calcolare l'energia dello stato fondamentale della molecola di idrogeno con VQE\n",
        "\n",
        "Dopo aver esaminato l'intero processo di VQE utilizzando gli atomi di $H$, calcoleremo ora più rapidamente l'energia di stato fondamentale della molecola di $H_2$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bcd4b1e5-aea5-4509-af22-661937b4286d",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-the-problem-to-quantum-circuits-and-operators\" />\n",
        "\n",
        "### Fase 1: Mappare il problema su circuiti quantistici e operatori\n",
        "\n",
        "Qui vi forniamo anche un'hamiltoniana a un qubit che utilizza la base STO-6G e la trasformazione di Jordan-Wigner, con una riduzione del qubit utilizzando una simmetria dell'hamiltoniana. Si noti che abbiamo utilizzato una distanza atomica tra due atomi di idrogeno di `0.735` $\\mathring A$.\n",
        "\n",
        "A differenza del calcolo di un singolo atomo di idrogeno ( $H$ ), per calcolare lo stato fondamentale di una molecola di idrogeno( $H_2$ ), dobbiamo considerare anche la forza repulsiva che agisce tra i nuclei dei due atomi di idrogeno, oltre all'energia associata agli orbitali elettronici. In questo passaggio, daremo questo valore come costante e lo calcoleremo effettivamente nel problema del check-in. $\\hat{H} = -1.04886 I + -0.79674 Z + 0.18122 X$\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "3722816b-8710-4c61-9da9-a347556496e6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Electronic ground state energy (Hartree):  -1.8659468547627318\n",
            "Nuclear repulsion energy (Hartree):  0.71997\n",
            "Total ground state energy (Hartree):  -1.1459768547627318\n"
          ]
        }
      ],
      "source": [
        "h2_hamiltonian = SparsePauliOp.from_list(\n",
        "    [(\"I\", -1.04886087), (\"Z\", -0.7967368), (\"X\", 0.18121804)]\n",
        ")\n",
        "\n",
        "# exact ground state energy of hamiltonian\n",
        "nuclear_repulsion = 0.71997\n",
        "A = np.array(h2_hamiltonian)\n",
        "eigenvalues, eigenvectors = np.linalg.eig(A)\n",
        "print(\"Electronic ground state energy (Hartree): \", min(eigenvalues).real)\n",
        "print(\"Nuclear repulsion energy (Hartree): \", nuclear_repulsion)\n",
        "print(\n",
        "    \"Total ground state energy (Hartree): \", min(eigenvalues).real + nuclear_repulsion\n",
        ")\n",
        "h2 = min(eigenvalues).real + nuclear_repulsion"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7e275302-80ea-454b-83c1-d70b09b37442",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-for-target-hardware\" />\n",
        "\n",
        "### Passaggio 2: Ottimizzazione per l'hardware di destinazione\n",
        "\n",
        "Poiché il numero di qubit utilizzato dal precedente VQE e dall'Hamiltoniano è lo stesso del backend da utilizzare per l'esecuzione, utilizzeremo l'ansatz esistente e la sua forma ottimizzata.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "6a461ab3-fbd7-493a-aaad-e47d6bfd0079",
      "metadata": {},
      "outputs": [],
      "source": [
        "h2_hamiltonian_isa = h2_hamiltonian.apply_layout(layout=ansatz_isa.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da36cda3-94a8-4b2d-9e3d-3b5ac957c180",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-on-target-hardware\" />\n",
        "\n",
        "### Fase 3: Esecuzione sull'hardware di destinazione\n",
        "\n",
        "Ora è il momento di fare i calcoli sull'attuale QPU. Quasi tutto è uguale, ma utilizzeremo il punto iniziale appropriato per adattare l'hamiltoniana. Inoltre, in una parte iterativa, alcune impostazioni di `Estimator`, utilizzato per calcolare le aspettative dell'hamiltoniana per l'ansatz nella QPU, saranno impostate in modo leggermente diverso rispetto ai calcoli precedenti. Discuteremo ulteriormente questo cambiamento in una domanda di check-in.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "b5afe148-bfdc-4d77-895c-d58afc118d9e",
      "metadata": {},
      "outputs": [],
      "source": [
        "x0 = [2, 0, 0]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "id": "3fb2e079-1413-4fd2-a3db-da5d11fe58d5",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Iters. done: 1 [Current cost: -0.710621837568328]\n",
            "Iters. done: 2 [Current cost: -0.2603208441168329]\n",
            "Iters. done: 3 [Current cost: -0.25548711201326424]\n",
            "Iters. done: 4 [Current cost: -0.581129450619904]\n",
            "Iters. done: 5 [Current cost: -1.722920997605439]\n",
            "Iters. done: 6 [Current cost: -1.6633324849371915]\n",
            "Iters. done: 7 [Current cost: -1.8066989598929164]\n",
            "Iters. done: 8 [Current cost: -1.8051093803839542]\n",
            "Iters. done: 9 [Current cost: -1.802692217571555]\n",
            "Iters. done: 10 [Current cost: -1.8233585485263144]\n",
            "Iters. done: 11 [Current cost: -1.6904116652617205]\n",
            "Iters. done: 12 [Current cost: -1.8245120321245392]\n",
            "Iters. done: 13 [Current cost: -1.6837021361383608]\n",
            "Iters. done: 14 [Current cost: -1.8166632606115467]\n",
            "Iters. done: 15 [Current cost: -1.863446212658907]\n"
          ]
        }
      ],
      "source": [
        "# QPU time 4min for ibm_brisbane\n",
        "batch = Batch(backend=backend)\n",
        "\n",
        "cost_history_dict = {\n",
        "    \"prev_vector\": None,\n",
        "    \"iters\": 0,\n",
        "    \"cost_history\": [],\n",
        "}\n",
        "estimator = Estimator(mode=batch)\n",
        "estimator.options.default_shots = 10000\n",
        "\n",
        "res = minimize(\n",
        "    cost_func,\n",
        "    x0,\n",
        "    args=(ansatz_isa, h2_hamiltonian_isa, estimator),\n",
        "    method=\"cobyla\",\n",
        "    options={\"maxiter\": 15},\n",
        ")\n",
        "\n",
        "batch.close()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "id": "4edf1dbd-8363-4d0d-a4ca-2223719c7059",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The reference ground state energy is  -1.1459768547627318\n",
            "The computed ground state energy is  -1.143476212658907\n"
          ]
        }
      ],
      "source": [
        "h2_vqe = res.fun + nuclear_repulsion\n",
        "print(\n",
        "    \"The reference ground state energy is \", min(eigenvalues).real + nuclear_repulsion\n",
        ")\n",
        "print(\"The computed ground state energy is \", h2_vqe)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d30fbc5-8f52-4b84-b9a6-55b3432bbec5",
      "metadata": {},
      "source": [
        "Nonostante la VQE fornisca teoricamente un limite superiore alla vera energia di stato fondamentale, le implementazioni pratiche su hardware quantistico reale o simulato in modo rumoroso, così come le approssimazioni fatte nella preparazione dell'hamiltoniana (come gli insiemi di basi o la riduzione dei qubit), possono introdurre errori che a volte si traducono in un'energia misurata leggermente inferiore al valore teorico esatto o a un riferimento numerico specifico. Sebbene vi siano alcuni errori, i risultati sembrano essere soddisfacenti, soprattutto se si considera il numero ridotto di passaggi. Terminiamo ora il calcolo del VQE esaminando il funzionamento dell'ottimizzatore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5d5c88de-fcd1-4ef6-8055-01505d86d07e",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process\" />\n",
        "\n",
        "### Fase 4: Post-elaborazione\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 52,
      "id": "b20dda12-dad9-4585-9a56-31611aa00930",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/4b82b644-e030-4983-9fd3-83c3e9dbf307-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, ax = plt.subplots()\n",
        "x = np.linspace(0, 5, 15)\n",
        "\n",
        "# Define the constant function\n",
        "y_constant = np.full_like(x, min(eigenvalues))\n",
        "ax.plot(\n",
        "    range(cost_history_dict[\"iters\"]), cost_history_dict[\"cost_history\"], label=\"VQE\"\n",
        ")\n",
        "ax.set_xlabel(\"Iterations\")\n",
        "ax.set_ylabel(\"Cost (Hartree)\")\n",
        "ax.plot(y_constant, label=\"Target\")\n",
        "plt.legend()\n",
        "plt.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8eab4184-b5dd-494e-9e67-c209b3f835f3",
      "metadata": {},
      "source": [
        "<span id=\"check-your-understanding\" />\n",
        "\n",
        "#### Verifica la tua comprensione\n",
        "\n",
        "Calcoliamo l'energia di repulsione nucleare della molecola $H_2$, che abbiamo incluso come valore costante ( 0.71997 Hartree).\n",
        "\n",
        "![H2 molecola](https://quantum.cloud.ibm.com/learning/images/modules/computer-science/vqe/h2.avif)\n",
        "\n",
        "Utilizza [la legge di Coulomb](https://en.wikipedia.org/wiki/Coulomb%27s_law) e [l'unità atomica](https://en.wikipedia.org/wiki/Atomic_units) per assicurarti di ottenere il valore in `Hartree`.\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"Risposta\">\n",
        "    Poiché entrambi i nuclei di idrogeno sono carichi positivamente, si respingono a causa della forza elettrostatica. Questa repulsione è descritta dalla legge di Coulomb:\n",
        "\n",
        "    $E_{repulsive} = \\frac{e^2}{4\\pi\\epsilon_0R}$\n",
        "\n",
        "    dove $e$ è la carica del protone, $\\epsilon_0$ è la permittività del vuoto e $R$ è la distanza tra i due nuclei, misurata in metri o raggi di Bohr in unità di joule (J).\n",
        "\n",
        "    Per calcolare questa energia in Hartrees, dobbiamo convertire l'equazione precedente nel sistema delle unità atomiche (AU). In AU, $e^2 = 1$, $4\\pi\\epsilon_0=1$ e il raggio di Bohr ( $a_0$ ) è 1 e diventa la scala di lunghezza fondamentale in AU. Con queste semplificazioni, la legge di Coulomb si riduce a:\n",
        "\n",
        "    $E_{repulsion} = \\frac{1}{R}$\n",
        "\n",
        "    dove $R$ deve essere misurato in raggi di Bohr ( $a_0$ ).\n",
        "\n",
        "    Per convertire la separazione nucleare data in $\\r{A}$ in $a_0$, abbiamo bisogno di questa relazione di conversione:\n",
        "\n",
        "    $1\\r{A} = 1.88973 a_0$\n",
        "\n",
        "    quindi $0.735\\r{A}$ diventa $0.735 * 1.88973 = 1.38895 a_0$.\n",
        "\n",
        "    Pertanto, l'energia di repulsione nucleare di un dato $H_2$ è\n",
        "\n",
        "    $E_{repulsion} = \\frac{1}{R} = \\frac{1}{1.38895} = 0.71997 Hartree$\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f75e4a10-dfdc-4416-8087-a41c1eac039c",
      "metadata": {},
      "source": [
        "<span id=\"compute-reaction-energy-of-$h-+-h-=-h_2$\" />\n",
        "\n",
        "## Calcolare l'energia di reazione dell' $H + H = H_2$\n",
        "\n",
        "Ora utilizziamo ciò che abbiamo ottenuto! Avete usato il VQE, un eigensolver quantistico variazionale, per calcolare l'energia di stato fondamentale dell'atomo $H$ e della molecola $H_2$. Non resta che utilizzare i valori calcolati per ottenere l'energia di reazione del processo $H+H=H_2$.\n",
        "\n",
        "L'energia di reazione è la variazione di energia che si verifica quando le sostanze reagiscono per formare nuove sostanze. Immaginate di costruire qualcosa: a volte è necessario immettere energia (come impilare i blocchi), altre volte l'energia viene rilasciata (come una palla che rotola in discesa). In chimica, le reazioni assorbono energia (endotermiche) o la rilasciano (esotermiche).\n",
        "\n",
        "L'energia di reazione del processo $H+H = H_2$ può essere calcolata con la seguente formula:\n",
        "\n",
        "$E_{reaction} = E_{H_2} - (E_H + E_H)$\n",
        "\n",
        "Eseguendo la cella sottostante, vediamo questo aspetto visivamente. Qui utilizzeremo il valore esatto dello stato fondamentale di ciascuna hamiltoniana e confronteremo l'energia di reazione della soluzione esatta e i risultati del VQE.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "id": "aa8d1a29-fbfe-41bf-8ed7-947827d0f0e6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/87e1e660-ae81-43f4-adbe-a229b9d9c889-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Theoretical values\n",
        "E_H_theo = h.real\n",
        "E_H2_theo = h2\n",
        "\n",
        "# Experimental values\n",
        "E_H_exp = h_vqe\n",
        "E_H2_exp = h2_vqe\n",
        "\n",
        "# Calculate reaction energies\n",
        "E_reaction_theo = E_H2_theo - (2 * E_H_theo)\n",
        "E_reaction_exp = E_H2_exp - (2 * E_H_exp)\n",
        "\n",
        "# Set up the plot\n",
        "fig, ax = plt.subplots(figsize=(8, 6))\n",
        "ax.set_xlim(0, 3)\n",
        "ax.set_ylim(-1.16, -0.93)  # Adjust y-axis range to highlight differences\n",
        "ax.set_xticks([])\n",
        "ax.set_ylabel(\"Energy (Hartree)\")\n",
        "ax.set_title(\"H + H → H₂ Reaction Energy Diagram\")\n",
        "\n",
        "# Plot theoretical energy levels\n",
        "ax.hlines(\n",
        "    y=2 * E_H_theo, xmin=0.5, xmax=1.3, linewidth=2, color=\"r\", label=\"2H (Exact)\"\n",
        ")\n",
        "ax.hlines(y=E_H2_theo, xmin=1.3, xmax=2, linewidth=2, color=\"b\", label=\"H₂ (Exact)\")\n",
        "\n",
        "# Plot experimental energy levels\n",
        "ax.hlines(\n",
        "    y=2 * E_H_exp,\n",
        "    xmin=0.5,\n",
        "    xmax=1.5,\n",
        "    linewidth=2,\n",
        "    color=\"r\",\n",
        "    linestyle=\"dashed\",\n",
        "    label=\"2H (VQE)\",\n",
        ")\n",
        "ax.hlines(\n",
        "    y=E_H2_exp,\n",
        "    xmin=1.5,\n",
        "    xmax=2.5,\n",
        "    linewidth=2,\n",
        "    color=\"b\",\n",
        "    linestyle=\"dashed\",\n",
        "    label=\"H₂ (VQE)\",\n",
        ")\n",
        "\n",
        "# Add labels\n",
        "ax.text(\n",
        "    1,\n",
        "    2 * E_H_theo,\n",
        "    f\"2H: {2*E_H_theo:.4f}\",\n",
        "    verticalalignment=\"top\",\n",
        "    horizontalalignment=\"left\",\n",
        ")\n",
        "ax.text(\n",
        "    2,\n",
        "    E_H2_theo,\n",
        "    f\"H₂: {E_H2_theo:.4f}\",\n",
        "    verticalalignment=\"top\",\n",
        "    horizontalalignment=\"left\",\n",
        ")\n",
        "ax.text(\n",
        "    1,\n",
        "    2 * E_H_exp,\n",
        "    f\"2H_VQE: {2*E_H_exp:.4f}\",\n",
        "    verticalalignment=\"bottom\",\n",
        "    horizontalalignment=\"right\",\n",
        ")\n",
        "ax.text(\n",
        "    2,\n",
        "    E_H2_exp,\n",
        "    f\"H₂_VQE: {E_H2_exp:.4f}\",\n",
        "    verticalalignment=\"bottom\",\n",
        "    horizontalalignment=\"right\",\n",
        ")\n",
        "\n",
        "# Add arrows for reaction energy with ΔE label in the middle\n",
        "mid_y_theo = (2 * E_H_theo + E_H2_theo) / 2\n",
        "mid_y_exp = (2 * E_H_exp + E_H2_exp) / 2\n",
        "ax.annotate(\n",
        "    \"\",\n",
        "    xy=(1.3, E_H2_theo),\n",
        "    xytext=(1.3, 2 * E_H_theo),\n",
        "    arrowprops=dict(arrowstyle=\"<->\", color=\"g\"),\n",
        ")\n",
        "ax.text(\n",
        "    1.35, mid_y_theo, f\"ΔE: {E_reaction_theo:.4f}\", color=\"g\", verticalalignment=\"top\"\n",
        ")\n",
        "\n",
        "ax.annotate(\n",
        "    \"\",\n",
        "    xy=(1.5, E_H2_exp),\n",
        "    xytext=(1.5, 2 * E_H_exp),\n",
        "    arrowprops=dict(arrowstyle=\"<->\", color=\"g\", linestyle=\"dashed\"),\n",
        ")\n",
        "ax.text(\n",
        "    1.55,\n",
        "    mid_y_exp,\n",
        "    f\"ΔE_VQE: {E_reaction_exp:.4f}\",\n",
        "    color=\"g\",\n",
        "    verticalalignment=\"center\",\n",
        ")\n",
        "\n",
        "# Add legend\n",
        "ax.legend()\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1a87af7-0c69-42c5-a415-87c95752d678",
      "metadata": {},
      "source": [
        "Come mostrato nella figura, sebbene vi siano alcuni errori, l'energia esatta dello stato fondamentale degli hamiltoniani e l'energia di reazione calcolata utilizzando i risultati del VQE sono simili, vicini a -0.2 Hartree.\n",
        "\n",
        "Va notato che l'energia di reazione di questo processo ha un valore negativo, il che significa che l'energia viene rilasciata attraverso il processo e la molecola risultante ha un'energia inferiore a quella di due atomi singoli.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "006ded34-29e0-4702-a46a-0459ee49f416",
      "metadata": {},
      "source": [
        "6. Conclusione\n",
        "\n",
        "Riassumiamo ciò che abbiamo imparato finora.\n",
        "\n",
        "In primo luogo abbiamo esaminato due importanti tecniche di approssimazione necessarie per risolvere i problemi di chimica quantistica: il principio variazionale e le scelte degli insiemi di basi, entrambi fondamentali per la VQE. Abbiamo esplorato il principio variazionale a mano, calcolando l'energia di stato fondamentale dell'oscillatore armonico semplice.\n",
        "\n",
        "Abbiamo poi esplorato il VQE, un algoritmo molto utilizzato per calcolare l'energia di stato fondamentale di un sistema quantistico. Abbiamo eseguito un codice per calcolare le energie di stato fondamentale dell'idrogeno atomico ( $H$ ) e della molecola di idrogeno ( $H_2$ ). In particolare, abbiamo imparato che è necessario ottenere l'appropriata hamiltoniana molecolare per il sistema e trasformarla in una forma eseguibile su un computer quantistico. Abbiamo anche visto che l'ansatz, un circuito quantistico parametrizzato, è necessario per preparare gli stati quantistici di prova all'interno della VQE e abbiamo discusso l'importanza di scegliere un'adeguata struttura del circuito dell'ansatz. Abbiamo anche imparato che la VQE si basa su un processo di ottimizzazione iterativa che utilizza un computer classico, guidando il circuito quantistico alla ricerca dello stato di energia più basso, e abbiamo visto come il processo converge.\n",
        "\n",
        "Infine, abbiamo utilizzato le energie di stato fondamentale di $H$ e $H_2$ ottenute tramite VQE per calcolare l'energia di reazione del processo $H + H \\rightarrow H_2$.\n",
        "\n",
        "Il VQE è un potente algoritmo quantistico a breve termine, ma è importante essere consapevoli dei suoi limiti. Le prestazioni del VQE dipendono fortemente dalla scelta dell'ansatz: trovare un ansatz preparabile in modo efficiente che possa rappresentare accuratamente il vero stato fondamentale diventa impegnativo per le molecole più grandi e complesse. Inoltre, l'attuale hardware quantistico è suscettibile al rumore, che può influire sull'accuratezza dei risultati VQE, in particolare per i circuiti più profondi o per un numero maggiore di qubit. Nonostante queste sfide, il VQE è un algoritmo fondamentale e la ricerca in corso sta esplorando metodi variazionali più sofisticati e tecniche di mitigazione degli errori per spingere i confini di ciò che è possibile fare nella chimica quantistica su computer quantistici di prossima generazione. Ad esempio, sono in fase di sviluppo algoritmi come la Sample-based Quantum Diagonalization (SQD), che sfruttano i campioni ottenuti dai circuiti quantistici combinati con la diagonalizzazione classica in un sottospazio per migliorare la stima dell'energia e affrontare alcune delle limitazioni incontrate da VQE, in particolare per quanto riguarda l'efficienza di misura e la robustezza del rumore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "03c9dfc1-7367-4d71-9598-f02288c30ac0",
      "metadata": {},
      "source": [
        "<span id=\"review-and-questions\" />\n",
        "\n",
        "## Recensione e domande\n",
        "\n",
        "<span id=\"critical-concepts\" />\n",
        "\n",
        "### Concetti fondamentali:\n",
        "\n",
        "* L'algoritmo quantistico variazionale è un paradigma di calcolo in cui un computer classico e un computer quantistico lavorano insieme per risolvere un problema.\n",
        "* Nel VQE, partiamo da un'hamiltoniana del nostro sistema e la mappiamo su qubit da eseguire sul computer quantistico. Selezioniamo un circuito quantistico parametrizzato, un ansatz, ed effettuiamo misure ripetute, variando i parametri dell'ansatz, fino a raggiungere il valore energetico più basso. La ricerca nello spazio dei parametri viene effettuata con un ottimizzatore classico. Per ottenere buoni risultati, è necessario selezionare una buona maschera e un ottimizzatore appropriato.\n",
        "* L'energia di reazione è la variazione totale di energia in una reazione chimica, determinata dalla differenza tra l'energia dei reagenti e quella dei prodotti.\n",
        "\n",
        "<span id=\"true/false\" />\n",
        "\n",
        "### vero/falso\n",
        "\n",
        "1. Il principio variazionale afferma che il valore di aspettativa dell'energia per qualsiasi funzione d'onda di prova è sempre maggiore o uguale all'energia del vero stato fondamentale.\n",
        "2. Un insieme di basi è un insieme di funzioni utilizzate per approssimare le funzioni d'onda quantistiche.\n",
        "3. Il VQE è un algoritmo quantistico utilizzato per risolvere esattamente l'equazione di Schrödinger per una data hamiltoniana.\n",
        "4. Nella VQE, per preparare le funzioni d'onda di prova si utilizza un circuito quantistico parametrizzato (un ansatz).\n",
        "5. La scelta dell'ottimizzatore in VQE (ad esempio, COBYLA, SPSA o ADAM) non influisce sulla qualità del risultato.\n",
        "6. Il sito `Estimator` di Qiskit viene utilizzato per calcolare direttamente i valori di aspettativa degli hamiltoniani in VQE.\n",
        "\n",
        "<span id=\"multiple-choice-questions\" />\n",
        "\n",
        "### Domande a scelta multipla:\n",
        "\n",
        "1. Qual è lo scopo dell'hamiltoniana nella VQE?\n",
        "\n",
        "* A) Generare stati quantistici casuali\n",
        "* B) Determinare l'energia degli stati quantistici\n",
        "* C) Ottimizzare i circuiti quantistici\n",
        "* D) Per creare l'entanglement\n",
        "\n",
        "2. Qual è l'obiettivo principale dell'algoritmo VQE?\n",
        "\n",
        "* A) Trovare l'energia di stato fondamentale di un'hamiltoniana\n",
        "* B) Per creare l'entanglement tra i qubit\n",
        "* C) Eseguire la ricerca di Grover\n",
        "* D) Per rompere la crittografia RSA\n",
        "\n",
        "3. Quanti stati quantistici vengono generati in questo quaderno per confrontare l'ansatz?\n",
        "\n",
        "* A) 100\n",
        "* B) 1000\n",
        "* C) 5000\n",
        "* D) 10,000\n",
        "\n",
        "4. Perché è necessario un ottimizzatore classico in VQE?\n",
        "\n",
        "* A) Eseguire misure quantistiche\n",
        "* B) Aggiornare i parametri dell'ansatz per minimizzare l'energia\n",
        "* C) Per agganciare i qubit\n",
        "* D) Per generare casualità quantistica\n",
        "\n",
        "5. Perché l'ansatz è stato progettato per essere parametrizzato?\n",
        "\n",
        "* A) Consentire la preparazione dello stato quantistico\n",
        "* B) Consentire la ricerca di un ampio spazio di stati quantistici\n",
        "* C) Ridurre la complessità del circuito\n",
        "* D) Per misurare direttamente gli autovalori\n",
        "\n",
        "6. Quale delle seguenti è l'affermazione più corretta sulla scelta di una buona ansatz?\n",
        "\n",
        "* A) Un ansatz deve produrre stati uniformemente distribuiti sulla sfera di Bloch, altrimenti fallirà.\n",
        "* B) Un'ipotesi di calcolo deve essere adattata al vostro sistema per assicurarsi che possa generare stati vicini allo stato fondamentale.\n",
        "* C) Un'ansatz dovrebbe produrre stati casuali utilizzando i suoi parametri variazionali.\n",
        "* D) Un'ansatz migliore ha sempre più parametri variazionali.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f44807a-45f6-4e36-9d19-faa77f7c0c79",
      "metadata": {},
      "source": [
        "<span id=\"optional-appendix-optimizer-overhead-by-ansatz-complexity\" />\n",
        "\n",
        "## (Facoltativo) Appendice: Overhead dell'ottimizzatore in base alla complessità dell'approccio\n",
        "\n",
        "La VQE deve affrontare diverse sfide ben noteref \\[6], e le seguenti sono legate a quanto abbiamo appreso in precedenza.\n",
        "\n",
        "1. Sfide per la selezione dell'ansatz\n",
        "\n",
        "La scelta dell'ansatz variazionale corretto rappresenta una sfida intrinseca. Le soluzioni ispirate alla chimica (come l'UCCSD) forniscono un'accuratezza fisica ma richiedono circuiti profondi, mentre le soluzioni efficienti dal punto di vista dell'hardware hanno circuiti più superficiali ma possono mancare di interpretabilità fisica. Inoltre, molte ansätze introducono parametri variazionali eccessivi che contribuiscono poco a migliorare l'accuratezza ma aumentano significativamente la difficoltà di ottimizzazione.\n",
        "\n",
        "2. Difficoltà di ottimizzazione\n",
        "\n",
        "Il paesaggio di ottimizzazione delle VQE può presentare regioni in cui i gradienti svaniscono esponenzialmente (plateau sterili), rendendo difficile per gli ottimizzatori classici aggiornare i parametri variazionali in modo efficiente. A tal fine, i ricercatori hanno cercato di utilizzare diversi tipi di ottimizzatori - basati sul gradiente e privi di gradiente, ma entrambi presentano delle difficoltà. Gli ottimizzatori basati sul gradiente soffrono di plateau sterili, mentre i metodi senza gradiente richiedono un gran numero di valutazioni di funzioni.\n",
        "\n",
        "3. Spese generali dell'ottimizzatore\n",
        "\n",
        "Un'altra sfida ben nota è l'overhead dell'ottimizzatore, che è legato alla scala del problema. I circuiti quantistici richiesti per la VQE crescono in profondità e complessità con l'aumentare delle dimensioni del problema; questo tipicamente aumenta anche il numero di parametri da ottimizzare. Il processo di ottimizzazione diventa intrattabile all'aumentare del numero di parametri, con conseguente lentezza della convergenza e difficoltà nel trovare la soluzione ottimale.\n",
        "\n",
        "In questa sede esamineremo queste sfide utilizzando la VQE per una molecola $H_2$, con due diversi tipi di ansätze.\n",
        "\n",
        "(Nota: questa operazione può richiedere più tempo per la QPU, quindi, se non avete tempo a sufficienza, potete usare un simulatore)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "id": "96179a4e-33f9-43ff-a791-f5ac8114dd29",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.circuit import ParameterVector\n",
        "\n",
        "num_iter = 4\n",
        "alpha = ParameterVector(\"alpha\", 3)\n",
        "beta = ParameterVector(\"beta\", 3 * num_iter)\n",
        "\n",
        "# step1: Map problem to quantum circuits and operators\n",
        "hamiltonian = SparsePauliOp.from_list(\n",
        "    [(\"I\", -1.04886087), (\"Z\", -0.7967368), (\"X\", 0.18121804)]\n",
        ")\n",
        "\n",
        "ansatz_1 = ansatz3\n",
        "ansatz_2 = QuantumCircuit(1)\n",
        "for i in range(num_iter):\n",
        "    ansatz_2.rx(beta[i * 3 + 0], 0)\n",
        "    ansatz_2.rz(beta[i * 3 + 1], 0)\n",
        "    ansatz_2.rx(beta[i * 3 + 2], 0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "0eeb0370-0ec7-490e-962b-55f746c4e4a3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/faf0cf47-e48d-44aa-8119-af56c2ba94df-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 55,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ansatz_1.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 56,
      "id": "105b8557-861c-4fcc-a84e-82d382ad730f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/0edacb57-b03d-4850-8698-9b8d9f1338c4-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 56,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ansatz_2.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 57,
      "id": "2f3f5e3f-d41b-45af-a14f-d2f1ef506037",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Step 2: Optimize for target hardware\n",
        "\n",
        "target = backend.target\n",
        "pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "\n",
        "ansatz_isa_1 = pm.run(ansatz_1)\n",
        "ansatz_isa_2 = pm.run(ansatz_2)\n",
        "hamiltonian_isa_1 = hamiltonian.apply_layout(layout=ansatz_isa_1.layout)\n",
        "hamiltonian_isa_2 = hamiltonian.apply_layout(layout=ansatz_isa_2.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1433eb47-1fcc-4e2e-8738-2d2818be8824",
      "metadata": {},
      "source": [
        "Ora eseguiamo un VQE con un punto iniziale composto da tutti uno, con un massimo di 20 passi, e confrontiamo la convergenza di entrambe le esecuzioni.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 60,
      "id": "693444f0-5835-471f-b9f7-20b5158b3d45",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Iters. done: 1 [Current cost: -0.8782202668652658]\n",
            "Iters. done: 2 [Current cost: -0.43473160695469165]\n",
            "Iters. done: 3 [Current cost: -0.4076372093159749]\n",
            "Iters. done: 4 [Current cost: -1.3587839859772106]\n",
            "Iters. done: 5 [Current cost: -1.774529906754082]\n",
            "Iters. done: 6 [Current cost: -1.541934983115727]\n",
            "Iters. done: 7 [Current cost: -1.2732403113465345]\n",
            "Iters. done: 8 [Current cost: -1.820842221085785]\n",
            "Iters. done: 9 [Current cost: -1.8065762857059005]\n",
            "Iters. done: 10 [Current cost: -1.8126394095981146]\n",
            "Iters. done: 11 [Current cost: -1.8205831886180421]\n",
            "Iters. done: 12 [Current cost: -1.8086715778994924]\n",
            "Iters. done: 13 [Current cost: -1.8307676638629322]\n",
            "Iters. done: 14 [Current cost: -1.8177328827556327]\n",
            "Iters. done: 15 [Current cost: -1.8179426218088064]\n",
            "Iters. done: 16 [Current cost: -1.8109239667991088]\n",
            "Iters. done: 17 [Current cost: -1.824271872489647]\n",
            "Iters. done: 18 [Current cost: -1.813167587671394]\n",
            "Iters. done: 19 [Current cost: -1.824647343397313]\n",
            "Iters. done: 20 [Current cost: -1.8219785311686143]\n"
          ]
        }
      ],
      "source": [
        "# QPU time 3m 40s for ibm_brisbane\n",
        "# Step 3: Execute on target hardware\n",
        "\n",
        "from scipy.optimize import minimize\n",
        "\n",
        "x0 = np.ones(ansatz_1.num_parameters)\n",
        "\n",
        "batch = Batch(backend=backend)\n",
        "\n",
        "\n",
        "cost_history_dict = {\n",
        "    \"prev_vector\": None,\n",
        "    \"iters\": 0,\n",
        "    \"cost_history\": [],\n",
        "}\n",
        "estimator = Estimator(mode=batch)\n",
        "estimator.options.default_shots = 2048\n",
        "\n",
        "res = minimize(\n",
        "    cost_func,\n",
        "    x0,\n",
        "    args=(ansatz_isa_1, hamiltonian_isa_1, estimator),\n",
        "    method=\"cobyla\",\n",
        "    options={\"maxiter\": 20},\n",
        ")\n",
        "\n",
        "batch.close()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 61,
      "id": "6922c55c-6a4f-41d8-8aad-69fa432ddf14",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Save Cost_history as a new list\n",
        "ansatz_1_history = cost_history_dict[\"cost_history\"]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 62,
      "id": "5ba0affa-8641-4a15-9408-a99dff0c7f8c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Iters. done: 1 [Current cost: -0.738191173881188]\n",
            "Iters. done: 2 [Current cost: -0.42636037194506304]\n",
            "Iters. done: 3 [Current cost: -1.3503788613797374]\n",
            "Iters. done: 4 [Current cost: -0.9109204349776897]\n",
            "Iters. done: 5 [Current cost: -0.9060873157510835]\n",
            "Iters. done: 6 [Current cost: -0.7735065414083984]\n",
            "Iters. done: 7 [Current cost: -1.586889197437709]\n",
            "Iters. done: 8 [Current cost: -1.659215191584943]\n",
            "Iters. done: 9 [Current cost: -1.245445981794618]\n",
            "Iters. done: 10 [Current cost: -1.1608385766138023]\n",
            "Iters. done: 11 [Current cost: -1.1551733876027737]\n",
            "Iters. done: 12 [Current cost: -1.8143337768286332]\n",
            "Iters. done: 13 [Current cost: -1.2510951563756598]\n",
            "Iters. done: 14 [Current cost: -1.6918311531865413]\n",
            "Iters. done: 15 [Current cost: -1.8163783305531838]\n",
            "Iters. done: 16 [Current cost: -1.8434877732947152]\n",
            "Iters. done: 17 [Current cost: -1.8461898233304472]\n",
            "Iters. done: 18 [Current cost: -1.0346471214915485]\n",
            "Iters. done: 19 [Current cost: -1.8322518854150687]\n",
            "Iters. done: 20 [Current cost: -1.717144678705999]\n"
          ]
        }
      ],
      "source": [
        "# QPU time 3m 40s for ibm_brisbane\n",
        "\n",
        "x0 = np.ones(ansatz_2.num_parameters)\n",
        "\n",
        "batch = Batch(backend=backend)\n",
        "\n",
        "\n",
        "cost_history_dict = {\n",
        "    \"prev_vector\": None,\n",
        "    \"iters\": 0,\n",
        "    \"cost_history\": [],\n",
        "}\n",
        "estimator = Estimator(mode=batch)\n",
        "estimator.options.default_shots = 2048\n",
        "\n",
        "res = minimize(\n",
        "    cost_func,\n",
        "    x0,\n",
        "    args=(ansatz_isa_2, hamiltonian_isa_2, estimator),\n",
        "    method=\"cobyla\",\n",
        "    options={\"maxiter\": 20},\n",
        ")\n",
        "\n",
        "batch.close()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 63,
      "id": "7354f299-cab6-4d43-a0e3-38c6c19f0486",
      "metadata": {},
      "outputs": [],
      "source": [
        "ansatz_2_history = cost_history_dict[\"cost_history\"]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 64,
      "id": "2c6d2eb4-197b-4811-bb0b-377e445ff3a8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/learning/images/modules/computer-science/vqe/extracted-outputs/fb572c33-f3be-4ea8-959c-0891c9da947a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, ax = plt.subplots()\n",
        "\n",
        "# Define the constant function)\n",
        "ax.plot(\n",
        "    range(cost_history_dict[\"iters\"]),\n",
        "    ansatz_1_history,\n",
        "    label=\"Ansatz with 3 parameters\",\n",
        ")\n",
        "ax.plot(\n",
        "    range(cost_history_dict[\"iters\"]),\n",
        "    ansatz_2_history,\n",
        "    label=\"Ansatz with 12 parameters\",\n",
        ")\n",
        "ax.set_xlabel(\"Iterations\")\n",
        "ax.set_ylabel(\"Cost (Hartree)\")\n",
        "plt.legend()\n",
        "plt.draw()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b6a9fcdb-020f-429a-8b6e-17d67b0caaf9",
      "metadata": {},
      "source": [
        "Il grafico precedente dimostra chiaramente che il processo di ottimizzazione dell'ansatz con più variabili richiede più tempo per raggiungere una convergenza stabile.\n",
        "\n",
        "Piuttosto che affidarsi a semplici circuiti a un solo qubit e a un ansatz diretto, la complessità dell'ottimizzazione aumenta quando sono richiesti circuiti quantistici più grandi e ansätze strutturate più complesse. Ciò evidenzia una sfida ben nota nelle VQE: l'overhead dell'ottimizzatore.\n",
        "\n",
        "I ricercatori continuano a sviluppare varie metodologie avanzate che possono utilizzare i computer quantistici per risolvere problemi di chimica. È possibile accedere a una vasta gamma di materiali didattici all'indirizzo [IBM Quantum Learning](/learning).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f7c6d9a5-c2cc-4674-9d54-1f9809cfde51",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Riferimenti\n",
        "\n",
        "* \\[[rif 1](https://link.springer.com/article/10.1007/BF02650179) ] Richard P. Feynman, Simulating Physics with Computers, International Journal of Theoretical Physics, 1982.\n",
        "* [\\[rif. 2\\]](https://link.springer.com/chapter/10.1007/978-1-4614-8730-2_10) Marov, M.Y. (2015). La struttura dell'universo. In: I fondamenti dell'astrofisica moderna. Springer, New York, NY.\n",
        "* \\[[rif 3](https://www.ibm.com/quantum/blog/photoresists-quantum-chemistry-jsr) ] Come risolvere i difficili problemi di ingegneria chimica con l'informatica quantistica, IBM Research Blog, 2023.\n",
        "* \\[[rif. 4](https://ieeexplore.ieee.org/document/8585034) ] Y. Cao, J. Romero e A. Aspuru-Guzik, \"Potential of quantum computing for drug discovery\", in IBM Journal of Research and Development, vol. 62, n. 6, pp. 6:1-6:20, 1 Nov.-Dec. 2018\n",
        "* \\[[rif. 5](https://journals.aps.org/rmp/abstract/10.1103/RevModPhys.32.170) ] Stato attuale del calcolo della struttura molecolare, REv. Mod. Fisico. 32, 170, 1960\n",
        "* \\[[rif. 6](https://jmsh.springeropen.com/articles/10.1186/s41313-021-00032-6) ] Fedorov, D.A, Peng, B., Govind, N. e altri. Il metodo VQE: breve panoramica e sviluppi recenti. Teoria della Materia 6, 2 (2022)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "in_page_toc_max_heading_level": 2,
    "in_page_toc_min_heading_level": 2,
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    },
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "state": {},
        "version_major": 2,
        "version_minor": 0
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}