{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "8d86bf32",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Simulare l'evoluzione temporale del modello di Ising a campo trasversale\"\n",
        "description: \"Utilizzare Qiskit.jl per simulare l'evoluzione temporale del modello di Ising a campo trasversale su un hardware IBM Quantum\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore mktempdir Néel spdiagm Runge Kutta tspan saveat Neel maxdim tensornetworkstate println clims xlabel ylabel colorbar juliaup vcat nsteps ylims topright findfirst */}\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65550e2a",
      "metadata": {},
      "source": [
        "<span id=\"simulate-time-evolution-of-the-transverse-field-ising-model\" />\n",
        "\n",
        "# Simulare l'evoluzione temporale del modello di Ising a campo trasversale\n",
        "\n",
        "Stima *dell'utilizzo: 105 secondi su un processore Nighthawk r2 (NOTA: si tratta solo di una stima. (La durata potrebbe variare.)*\n",
        "\n",
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "1. Scopri come traspilare ed eseguire circuiti quantistici sull'hardware utilizzando Julia\n",
        "2. Scopri come elaborare i risultati delle misurazioni per calcolare i valori attesi\n",
        "3. Scopri come confrontare i risultati dell'hardware con quelli della simulazione classica per quantificare gli effetti combinati dell'errore di approssimazione di Trotter e del rumore dell'hardware\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "efde28b8",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Prima di iniziare questo tutorial, ti consigliamo di familiarizzare con i seguenti argomenti:\n",
        "\n",
        "* [Simulazione quantistica](/learning/courses/utility-scale-quantum-computing/quantum-simulation)\n",
        "\n",
        "* [Il modello di Ising a campo trasversale](https://en.wikipedia.org/wiki/Transverse-field_Ising_model)\n",
        "\n",
        "* [Introduzione alla transpilazione](/docs/guides/transpile#introduction-to-transpilation)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "727912c7",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "Julia è un linguaggio di programmazione dinamico progettato principalmente per il calcolo numerico e scientifico. Le sue elevate capacità di calcolo numerico lo rendono la scelta ideale per i flussi di lavoro di simulazione quantistica. In questo tutorial mostriamo come Julia venga utilizzata sia per le operazioni classiche di pre-elaborazione e post-elaborazione (ad esempio, la costruzione di hamiltoniani, l'esecuzione di risolutori di equazioni differenziali ordinarie (ODE) e il calcolo dei valori attesi), sia per l'orchestrazione di processi su hardware quantistico, eliminando la necessità di passare da un linguaggio o da un ambiente all'altro.\n",
        "\n",
        "Per interfacciarsi con l'hardware di IBM Quantum® da Julia, questo tutorial utilizza due pacchetti dell'ecosistema Qiskit: `Qiskit.jl` funge da wrapper per la libreria C di Qiskit e fornisce funzionalità di costruzione di circuiti e transpilazione in Julia; si `QiskitIBMRuntime.jl` collega all'hardware di IBM Quantum tramite il client del servizio IBM Quantum Compute, consentendo l'invio di job e il recupero dei risultati direttamente da Julia.\n",
        "\n",
        "In questo tutorial, consideriamo l’evoluzione trotterizzata del modello di Ising a campo trasversale su una catena di tipo “ 1D ” con interazioni tra vicini più prossimi:\n",
        "\n",
        "$$\n",
        "    H = \\sum_{\\langle i,j\\rangle}J_{ij}Z_iZ_j + \\sum_i h_i X_i\n",
        "$$\n",
        "\n",
        "Per implementare l'evoluzione temporale $e^{-iH\\tau}$, dividiamo l'intervallo di tempo $\\tau$ in $r$ passi e definiamo $\\Delta\\tau=\\tau/r$. La decomposizione di Trotter-Suzuki del secondo ordine fornisce quanto segue:\n",
        "\n",
        "$$\n",
        "    e^{-iH\\Delta\\tau}\\approx \\prod_i e^{-ih_i X_i\\Delta\\tau/2 } \\prod_{\\langle i,j\\rangle} e^{-iJ_{ij}Z_iZ_j\\Delta\\tau} \\prod_i e^{-ih_iX_i\\Delta\\tau/2}\n",
        "$$\n",
        "\n",
        "Per la realizzazione del circuito, ogni fase di Trotter viene implementata come una sequenza di rotazioni di un singolo qubit $R_x$ e di porte a due qubit $R_{ZZ}$. Il circuito inizia preparando lo stato di Néel $|0101\\cdots\\rangle$ utilizzando porte X su qubit alternati. Ogni fase successiva dell'algoritmo di Trotter prevede: (1) l'applicazione di un'operazione di tipo \" $R_x(h_i\\Delta\\tau)$ \" su ogni qubit, (2) l'applicazione di un'operazione di tipo \" $R_{ZZ}(2J_{ij}\\Delta\\tau)$ \" su ciascuna coppia adiacente lungo la catena, e (3) nuovamente l'applicazione di un'operazione di tipo \" $R_x(h_i\\Delta\\tau)$ \" su ogni qubit. La profondità totale del circuito cresce linearmente con il numero di passi di Trotter $r$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "020f72af",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisiti\n",
        "\n",
        "Si noti che questo tutorial richiede macOS o Linux. Qiskit.jl attualmente non è supportato su Windows (come indicato in [questa segnalazione aperta](https://github.com/Qiskit/Qiskit.jl/issues/15) ).\n",
        "\n",
        "Per iniziare, installa Julia seguendo le istruzioni riportate nella [pagina di download di](https://julialang.org/downloads/) Julia. Questo tutorial è stato realizzato con Julia 1.11; installalo con `juliaup add 1.11`.\n",
        "\n",
        "Successivamente, esegui il seguente comando in un terminale per installare il pacchetto Julia `IJulia` nell'ambiente globale, in modo da poter eseguire Julia all'interno del notebook Jupyter.\n",
        "\n",
        "```\n",
        "julia -e 'using Pkg; Pkg.add(\"IJulia\")'\n",
        "```\n",
        "\n",
        "Utilizziamo il gestore di pacchetti integrato in Julia per configurare l'ambiente di progetto. Esistono due modi per configurare l'ambiente.\n",
        "\n",
        "*Opzione 1* : ambiente temporaneo. È possibile eseguire la seguente cella di codice per configurare un ambiente temporaneo e installare i pacchetti necessari;\n",
        "\n",
        "*Opzione 2* : riprodurre l'esatto ambiente di test. Inseriscilo nella `download_toml_files = true` cella sottostante. La cella scaricherà e `Project.toml` `Manifest.toml` dal [repository](https://github.com/Qiskit/documentation/tree/main/docs/tutorials/assets/time-evolution/julia) della documentazione in una `env_tutorial/time-evolution/` cartella accanto a questo notebook, quindi attiverà quell’ambiente e installerà le versioni esatte dei pacchetti che sono state registrate. Il file di progetto descrive l'ambiente a livello generale; ad esempio, la sezione `[deps]` elenca tutte le dipendenze. Il file manifest specifica la versione esatta di ogni pacchetto (comprese le dipendenze indirette), rendendo così l'ambiente riproducibile. Per ulteriori dettagli, consultare la [documentazione di Julia](https://pkgdocs.julialang.org/v1/toml-files/).\n",
        "\n",
        "Nell'ambiente verranno installate le seguenti dipendenze.\n",
        "\n",
        "Per la costruzione e l'esecuzione di circuiti quantistici:\n",
        "\n",
        "* `Qiskit.jl`\n",
        "* `QiskitIBMRuntime.jl`\n",
        "\n",
        "Per la simulazione classica:\n",
        "\n",
        "* `OrdinaryDiffEq.jl`\n",
        "* `TensorNetworkQuantumSimulator.jl`\n",
        "\n",
        "Per la post-elaborazione dei risultati e la visualizzazione:\n",
        "\n",
        "* `StatsBase.jl`\n",
        "* `Plots.jl`\n",
        "\n",
        "Questo tutorial è stato testato con la versione `Qiskit.jl` 0.6.0 e la versione `QiskitIBMRuntime.jl` 0.3.1.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "1454cc50",
      "metadata": {},
      "outputs": [],
      "source": [
        "using Pkg\n",
        "using Downloads\n",
        "\n",
        "download_toml_files = false\n",
        "\n",
        "if !download_toml_files\n",
        "    # Option 1: Install the latest versions of the required packages into a temporary environment\n",
        "    Pkg.activate(mktempdir(); io=devnull)\n",
        "    Pkg.add([\n",
        "        PackageSpec(name=\"Qiskit\"),\n",
        "        PackageSpec(name=\"QiskitIBMRuntime\"),\n",
        "        PackageSpec(name=\"Python_jll\"),\n",
        "        PackageSpec(name=\"OrdinaryDiffEq\"),\n",
        "        PackageSpec(name=\"TensorNetworkQuantumSimulator\"),\n",
        "        PackageSpec(name=\"StatsBase\"),\n",
        "        PackageSpec(name=\"Plots\"),\n",
        "    ]; io=devnull)\n",
        "else\n",
        "    # Option 2: Install the exact tested versions pinned in the downloaded Project.toml and Manifest.toml\n",
        "    base_url = \"https://raw.githubusercontent.com/Qiskit/documentation/main/docs/tutorials/assets/time-evolution/julia\"\n",
        "    env_dir = joinpath(@__DIR__, \"env_tutorial\", \"time-evolution\")\n",
        "    mkpath(env_dir)\n",
        "    for file in (\"Project.toml\", \"Manifest.toml\")\n",
        "        Downloads.download(\"$base_url/$file\", joinpath(env_dir, file))\n",
        "    end\n",
        "\n",
        "    Pkg.activate(env_dir; io=devnull)\n",
        "    Pkg.instantiate(; io=devnull) # installs the exact versions recorded in Manifest.toml\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a13bff0e",
      "metadata": {},
      "source": [
        "A questo punto, abbiamo configurato l'ambiente di progetto Julia per eseguire il notebook. Per eseguire il flusso di lavoro su un'unità di elaborazione quantistica IBM, è necessario disporre di un account IBM Quantum e di un token API per istanziare il servizio da `qiskit-ibm-runtime`. Segui i passaggi della sezione \"Installazione e autenticazione\" nell'argomento \" [Esegui il tuo primo circuito su hardware](/docs/guides/hello-world)\" per generare il tuo token API e individuare l' CRN della tua istanza.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "613853a6",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "4ea0efac",
      "metadata": {},
      "outputs": [],
      "source": [
        "using Qiskit\n",
        "using Qiskit.Operations\n",
        "using QiskitIBMRuntime\n",
        "using StatsBase\n",
        "using OrdinaryDiffEq\n",
        "using SparseArrays\n",
        "using LinearAlgebra\n",
        "using TensorNetworkQuantumSimulator\n",
        "using Plots: plot, plot!, heatmap, @layout, mm"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5016929d",
      "metadata": {},
      "source": [
        "Definiamo inoltre la seguente funzione di utilità, che restituisce il valore del bit in una stringa di bit `v` nella posizione `i`. Ad esempio, con ( `v = 6` binario `110`),\n",
        "\n",
        "* `bit_at(6, 1)` restituisce `0`,\n",
        "* `bit_at(6, 2)` restituisce `1`,\n",
        "* `bit_at(6, 3)` restituisce `1`.\n",
        "\n",
        "Ciò segue la [convenzione little-endian](/docs/guides/bit-ordering) utilizzata in Qiskit: la posizione `i` è indicizzata a partire dal bit meno significativo (quello \"più a destra\").\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "143aecbe",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "bit_at"
            ]
          },
          "execution_count": 21,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "\"\"\"\n",
        "    bit_at(v::Integer, i::Integer) = (v >> (i-1)) & 1\n",
        "\n",
        "Return the value of the bit at position `i` in `v`.\n",
        "\"\"\"\n",
        "bit_at(v::Integer, i::Integer) = (v >> (i-1)) & 1"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a17088d",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Esempio di simulatore su piccola scala\n",
        "\n",
        "Consideriamo una catena di tipo “ 1D ” composta da qubit di tipo “ $N$ ”, descritta dal modello di Ising a campo trasversale sopra riportato. Per il sistema in esame, specifichiamo di seguito la dimensione del sistema `N`, la dimensione del passo di `δt` Trotter e il numero totale di passi di Trotter `r_max`. Il tempo totale di evoluzione è `δt * r_max`. Si noti che Julia supporta identificatori Unicode come `δt`;: nel notebook o nella REPL di Julia, digitare seguito `\\delta` dal tasto Tab per inserire `δ`. Per informazioni complete, consultare la [documentazione sull'input Unicode di Julia](https://docs.julialang.org/en/v1/manual/unicode-input/).\n",
        "\n",
        "<span id=\"exact-solution\" />\n",
        "\n",
        "### Soluzione esatta\n",
        "\n",
        "Per stabilire un punto di riferimento con cui confrontare i risultati ottenuti dall’hardware quantistico, illustriamo innanzitutto il flusso di lavoro della simulazione classica per un problema su piccola scala. Costruiamo l’Hamiltoniano di Ising come matrice sparsa, quindi otteniamo l’evoluzione temporale esatta integrando numericamente l’equazione di Schrödinger utilizzando `ODEProblem` da `OrdinaryDiffEq.jl`. Questo approccio cresce in modo esponenziale con il numero di qubit $N$. Richiede la memorizzazione dell'intero vettore di stato a $2^N$ dimensioni. Per $N=20$ lo spazio di Hilbert ha già oltre un milione di dimensioni, il che lo rende poco pratico per sistemi più grandi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "aa6b4db3",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "1048576×1048576 SparseMatrixCSC{ComplexF64, Int64} with 22020096 stored entries:\n",
              "⎡⣿⣿⣾⢦⡀⠳⣄⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤\n",
              "⎢⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⡀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠙⢦⡈⠳⣼⣿⣿⡆⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠙⢦⡀⠀⠀⠁⠀⠈⠈⠉⣿⣿⣾⢦⡀⠳⣄⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡈⠳⣼⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⎥\n",
              "⎢⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡟⢦⡈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠙⢦⠈⠳⡿⣿⣿⣀⡀⡀⠀⢀⠀⠀⠈⠳⣄⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠸⣿⣿⡟⢦⡈⠳⣄⠀⠀⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠈⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠐⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⎥\n",
              "⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⎥\n",
              "⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠙⢦⠈⠳⡿⣿⣿⎦"
            ]
          },
          "execution_count": 22,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "N = 20\n",
        "δt = 0.05 # Trotter step size\n",
        "r_max = 10 # total number of Trotter steps\n",
        "\n",
        "h = fill(1.0, N)\n",
        "J = fill(1.0, N-1)\n",
        "\n",
        "# Build the Ising Hamiltonian as a sparse 2^n × 2^n matrix\n",
        "function build_ising_hamiltonian(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int)\n",
        "    dim = 2^n\n",
        "\n",
        "    # diagonal ZZ terms\n",
        "    diag_terms = zeros(Float64, dim)\n",
        "    for i in 1:n-1\n",
        "        for b in 0:dim-1\n",
        "            bi = bit_at(b, i) # bit at position i\n",
        "            bi_next = bit_at(b, i+1) # bit at position i + 1\n",
        "            diag_terms[b+1] += J[i] * (1-2bi) * (1-2bi_next)\n",
        "        end\n",
        "    end\n",
        "    H = spdiagm(0 => complex(diag_terms))\n",
        "\n",
        "    # off-diagonal local X terms\n",
        "    for i in 1:n\n",
        "        mask = 1 << (i-1)\n",
        "        cols = [xor(b, mask) + 1 for b in 0:dim-1]\n",
        "        H += h[i] * sparse(1:dim, cols, ones(ComplexF64, dim), dim, dim)\n",
        "    end\n",
        "    return H\n",
        "end\n",
        "\n",
        "H_ising = build_ising_hamiltonian(h, J, N)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bbe38420",
      "metadata": {},
      "source": [
        "Definiamo il lato destro dell’equazione di Schrödinger nella forma “in-place” `schrodinger!(dψ, ψ, H, t)`, che calcola l’ $d\\psi/dt = -iH\\psi$ e tramite una moltiplicazione matrice-vettore sparsa. Abbiamo quindi impostato un'equazione con `ODEProblem` lo stato di Néel come condizione iniziale e l'abbiamo risolta nell'intervallo di tempo $[0, r \\cdot \\delta t]$, salvando lo stato ad ogni passo temporale $\\delta t$. Il risolutore utilizzato è `Tsit5()`, un metodo Runge-Kutta esplicito standard di quarto/quinto ordine adatto a problemi non rigidi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "959f77e9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "retcode: Success\n",
              "Interpolation: 1st order linear\n",
              "t: 11-element Vector{Float64}:\n",
              " 0.0\n",
              " 0.05\n",
              " 0.1\n",
              " 0.15\n",
              " 0.2\n",
              " 0.25\n",
              " 0.3\n",
              " 0.35\n",
              " 0.4\n",
              " 0.45\n",
              " 0.5\n",
              "u: 11-element Vector{Vector{ComplexF64}}:\n",
              " [0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im  …  0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im]\n",
              " [-7.612535273941705e-14 + 2.5737401150886516e-29im, 7.616818423232204e-14 - 1.6944517510822304e-12im, -8.565903349044157e-17 + 3.2368190823993794e-15im, -7.61682079584434e-14 - 1.078515326911689e-15im, 1.523363843119942e-13 - 1.6912125988569567e-12im, 3.5867824743714624e-11 + 5.083352813839524e-12im, -7.608249752495255e-14 - 4.317561743447096e-15im, 7.616820798431766e-14 - 1.6944516451719627e-12im, -8.570258514925147e-17 + 3.2384113624188233e-15im, -7.606534127496737e-14 - 5.398093819616815e-15im  …  -7.591102113501652e-14 - 7.55501666902211e-15im, 1.5233640806402347e-13 - 1.691212597789024e-12im, -4.283149391646935e-17 + 3.2390475588024777e-15im, -7.61253567131055e-14 - 4.319154099332238e-15im, 1.28498500555455e-16 + 4.773388002812907e-18im, -8.570258413862945e-17 + 3.2384113624488572e-15im, -7.612534879063769e-14 - 3.2390464923709225e-15im, 1.5233638826330585e-13 - 1.6912127047672224e-12im, -4.283149290692347e-17 + 3.237455101084436e-15im, -7.612535273941595e-14 - 1.8338992189508272e-31im]\n",
              " [-8.680854672628291e-11 - 3.427254579020333e-25im, 8.709110140864061e-11 - 8.699033104505336e-10im, -5.649235901782656e-13 + 8.601572275260816e-12im, -8.709221846767839e-11 - 2.859732693854631e-12im, 1.7418295344900034e-10 - 8.612608133903897e-10im, 8.41248317362033e-9 + 2.6096671235273274e-9im, -8.652487588621059e-11 - 1.1500395899126693e-11im, 8.709222358844944e-11 - 8.699014655672987e-10im, -5.669821718119223e-13 + 8.629638578162805e-12im, -8.641073444337035e-11 - 1.4395664161950265e-11im  …  -8.538755211456945e-11 - 2.0113079067784756e-11im, 1.7418407565004836e-10 - 8.612606969068827e-10im, -2.825548799186401e-13 + 8.640787361594646e-12im, -8.680873675466659e-11 - 1.152847038256832e-11im, 8.478635815548866e-13 + 8.382372443494577e-14im, -5.669819735592012e-13 + 8.629638588149376e-12im, -8.680836165042751e-11 - 8.640671375786309e-12im, 1.7418313902498964e-10 - 8.61262658275938e-10im, -2.82554682353394e-13 + 8.612701741862262e-12im, -8.680854672628374e-11 - 2.811149115586865e-25im]\n",
              " [-4.2861942928308275e-9 - 4.7678984538985196e-24im, 4.318192995176613e-9 - 2.8269371315624182e-8im, -6.395173522788358e-11 + 6.447233357075582e-10im, -4.318468070877377e-9 - 2.1364257686596578e-10im, 8.636571975410676e-9 - 2.7617721500979347e-8im, 1.7289660717976908e-7 + 8.480086489483005e-8im, -4.253920910159066e-9 - 8.649865429907717e-10im, 4.318470318624579e-9 - 2.826906169988642e-8im, -6.446071076018044e-11 + 6.495016281145607e-10im, -4.240844230135151e-9 - 1.0846764703342651e-9im  …  -4.124170626784771e-9 - 1.5115818225612714e-9im, 8.636849310525618e-9 - 2.7617681592276622e-8im, -3.199878870600196e-11 + 6.513863923787342e-10im, -4.2862418335791054e-9 - 8.697676141630365e-10im, 9.60478168658149e-11 + 1.4206678132640617e-11im, -6.446062402203318e-11 + 6.495016332435747e-10im, -4.286148929585427e-9 - 6.513467397401749e-10im, 8.636617557977306e-9 - 2.7618031117900408e-8im, -3.1998702345812497e-11 + 6.466014984949206e-10im, -4.286194292830829e-9 + 4.870909626742163e-24im]\n",
              " [-6.079348924748445e-8 + 6.709717322272516e-24im, 6.162943323939352e-8 - 2.9592110353110093e-7im, -1.6696556398383817e-9 + 1.2400459826013194e-8im, -6.164292787255694e-8 - 4.088131560944853e-9im, 1.2326810978249792e-7 - 2.832733452226872e-7im, 1.253532043962043e-6 + 8.875042569125039e-7im, -5.994408800994472e-8 - 1.6725258647088304e-8im, 6.164314112979357e-8 - 2.9591021577421156e-7im, -1.6948387141108349e-9 + 1.2572865374243683e-8im, -5.959595610442699e-8 - 2.1031419496566967e-8im  …  -5.651357562833814e-8 - 2.9192023780832225e-8im, 1.2328181992163385e-7 - 2.832706038202527e-7im, -8.359520177503641e-10 + 1.2640019589288568e-8im, -6.079590106381668e-8 - 1.689785040734888e-8im, 2.5106376181130386e-9 + 5.084778396177926e-10im, -1.6948306165735295e-9 + 1.2572866042727832e-8im, -6.079128573701664e-8 - 1.2637312624047932e-8im, 1.2327033399936622e-7 - 2.8328423313378375e-7im, -8.359439919088748e-10 + 1.2467163914771088e-8im, -6.079348924748447e-8 + 1.0331673240130025e-23im]\n",
              " [-4.193552407608965e-7 - 1.840220498495959e-23im, 4.2862657866184504e-7 - 1.605297190776032e-6im, -1.8502762435969943e-8 + 1.0856276308591437e-7im, -4.288690355597115e-7 - 3.554458373697522e-8im, 8.574220964863143e-7 - 1.4932465582703908e-6im, 4.849187224415274e-6 + 4.812233679729101e-6im, -4.098425461298137e-7 - 1.4745065418194435e-7im, 4.288753395920834e-7 - 1.6051467128773302e-6im, -1.896035285782807e-8 + 1.1102835700521827e-7im, -4.0588320553359593e-7 - 1.8611022850670223e-7im  …  -3.711838427223188e-7 - 2.569391306045151e-7im, 8.57670971098675e-7 - 1.4931825847645462e-6im, -9.271568570757063e-9 + 1.1197289579952942e-7im, -4.194005371452928e-7 - 1.4992045110994755e-7im, 2.787088237718031e-8 + 7.194933454521717e-9im, -1.8960118645415406e-8 + 1.1102838165568638e-7im, -4.193161709409199e-7 - 1.1191024844294344e-7im, 8.574617742181924e-7 - 1.4933970418540967e-6im, -9.271337900947239e-9 + 1.0949690324053836e-7im, -4.193552407608964e-7 + 1.6524848989693985e-22im]\n",
              " [-1.7823579016922897e-6 - 1.087640585040274e-21im, 1.840838369770109e-6 - 5.569854913729441e-6im, -1.1658899308162594e-7 + 5.632670364049069e-7im, -1.843111911436975e-6 - 1.8277839588324167e-7im, 3.6832950716080163e-6 - 4.979757871286645e-6im, 1.1823232043972897e-5 + 1.668138856075027e-5im, -1.7216192586061922e-6 - 7.718155525067347e-7im, 1.8432004842500001e-6 - 5.56872697042903e-6im, -1.209408931510706e-7 + 5.825677201706816e-7im, -1.6958321412209815e-6 - 9.789484717969276e-7im  …  -1.4727630743780977e-6 - 1.3421173522554028e-6im, 3.6856596566327488e-6 - 4.9790105233659736e-6im, -5.848359517965976e-8 + 5.898080282235565e-7im, -1.7828068643880377e-6 - 7.911632992433939e-7im, 1.760517112427114e-7 + 5.557422909554899e-8im, -1.209376913040035e-7 + 5.825681268981909e-7im, -1.7819976541714096e-6 - 5.890838036292954e-7im, 3.6836637838442504e-6 - 4.980885908360778e-6im, -5.848046807781978e-8 + 5.703874412047737e-7im, -1.7823579016922876e-6 + 1.4997773495573015e-22im]\n",
              " [-5.2719847869895295e-6 + 8.772351797140282e-21im, 5.515796864069832e-6 - 1.3769890958036113e-5im, -4.854583030811284e-7 + 1.984102714841576e-6im, -5.52914007682216e-6 - 6.365426377065764e-7im, 1.1041341496086288e-5 - 1.1652942788167796e-5im, 1.9151485012220565e-5 + 4.1169949873337434e-5im, -5.0149560721693026e-6 - 2.748560607544284e-6im, 5.529875137426081e-6 - 1.3764484293927507e-5im, -5.114444270677655e-7 + 2.0817439894898367e-6im, -4.903067916522327e-6 - 3.508133202204623e-6im  …  -3.950715241692067e-6 - 4.767013437270117e-6im, 1.1055449702075777e-5 - 1.1647607717886785e-5im, -2.4383689790869913e-7 + 2.1174362562762014e-6im, -5.274804158840664e-6 - 2.846520575369006e-6im, 7.355815857851513e-7 + 2.7656731682993024e-7im, -5.114187341304747e-7 + 2.0817477909234415e-6im, -5.2699143882793144e-6 - 2.112332735357189e-6im, 1.1043481329588082e-5 - 1.1658350328429614e-5im, -2.438120770802614e-7 + 2.018953103020384e-6im, -5.271984786989539e-6 + 6.138686657683503e-21im]\n",
              " [-1.1617825704147756e-5 + 1.0312565819538484e-21im, 1.2349337633136464e-5 - 2.5744303624623147e-5im, -1.454293291805138e-6 + 5.123664098726546e-6im, -1.2403650700016836e-5 - 1.6202581807656819e-6im, 2.473960492472325e-5 - 2.0155561130978555e-5im, 1.9144096039811722e-5 + 7.6742969783544e-5im, -1.0832694804431494e-5 - 7.1930875309620025e-6im, 1.2407719644833958e-5 - 2.572612053989065e-5im, -1.56229208977908e-6 + 5.474515092672054e-6im, -1.0480130191816845e-5 - 9.254685626031852e-6im  …  -7.537537188446326e-6 - 1.2435206581306264e-5im, 2.4798220145598703e-5 - 2.0129513964044365e-5im, -7.316420138432232e-7 + 5.598791194770797e-6im, -1.1630271281791021e-5 - 7.545396775206743e-6im, 2.2142322134173096e-6 + 9.742099793903422e-7im, -1.5621555243617513e-6 + 5.474538012007301e-6im, -1.1609612670484607e-5 - 5.574263610620754e-6im, 2.4748196626943606e-5 - 2.0173749496261283e-5im, -7.315119289888009e-7 + 5.243914596674148e-6im, -1.1617825704147746e-5 + 2.0077178062353968e-21im]\n",
              " [-1.9812558848038747e-5 - 9.187798066332235e-22im, 2.147271081932555e-5 - 3.757217609507472e-5im, -3.2943974661872274e-6 + 1.0138423201098428e-5im, -2.163583224409355e-5 - 3.1479593611068202e-6im, 4.3072956212126986e-5 - 2.6216146005671797e-5im, 4.770433131249609e-6 + 0.00011143181451766211im, -1.7992013464296718e-5 - 1.4466566267165694e-5im, 2.1652013724844488e-5 - 3.752687773861608e-5im, -3.6269134427165884e-6 + 1.1086241359275966e-5im, -1.7142579310500252e-5 - 1.8804566182208357e-5im  …  -1.0216935244391546e-5 - 2.491043927093904e-5im, 4.325353440439837e-5 - 2.6122963951736878e-5im, -1.6606359562313976e-6 + 1.140941830342574e-5im, -1.9853711381944784e-5 - 1.541920404485947e-5im, 5.050007317756615e-6 + 2.569541043839736e-6im, -3.6263960453836696e-6 + 1.1086337766500889e-5im, -1.97886585017898e-5 - 1.1323323936752868e-5im, 4.309833408283902e-5 - 2.6261466566058982e-5im, -1.6601519712867477e-6 + 1.044756873253024e-5im, -1.981255884803875e-5 + 7.339651656276922e-20im]\n",
              " [-2.6605994524636198e-5 + 1.1289591858171765e-19im, 2.9532145628332138e-5 - 4.333877076489454e-5im, -5.793688435837011e-6 + 1.572023652812328e-5im, -2.9906819721527362e-5 - 4.767746092973603e-6im, 5.9370303731772103e-5 - 2.51584831603933e-5im, -2.1873141629015348e-5 + 0.00012741720406900944im, -2.3313208885217603e-5 - 2.288272975975095e-5im, 2.995521974547466e-5 - 4.325274969426522e-5im, -6.580507052690018e-6 + 1.770923159800008e-5im, -2.1702668886267368e-5 - 3.0142728729320425e-5im  …  -8.927183090969304e-6 - 3.921197964153728e-5im, 5.979856139686108e-5 - 2.4903653161242186e-5im, -2.9274853045629046e-6 + 1.8356702741523666e-5im, -2.6711940545518465e-5 - 2.488368314305582e-5im, 8.967724203429812e-6 + 5.238269352768273e-6im, -6.579046665900031e-6 + 1.770952822000892e-5im, -2.6553193423229552e-5 - 1.812661956307733e-5im, 5.942741740138485e-5 - 2.5244572524328294e-5im, -2.9261511036959703e-6 + 1.6330572728121542e-5im, -2.660599452463617e-5 - 3.440870688152987e-20im]"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# initial state |0101...01⟩\n",
        "ψ0 = zeros(ComplexF64, 2^N)\n",
        "neel_index = sum(1 << (i-1) for i in 1:2:N)\n",
        "ψ0[neel_index + 1] = 1.0\n",
        "\n",
        "function schrodinger!(dψ::AbstractVector, ψ::AbstractVector, H::AbstractMatrix, t::Real)\n",
        "    mul!(dψ, H, ψ)\n",
        "    dψ .*= -im\n",
        "end\n",
        "\n",
        "tspan = (0.0, r_max * δt)\n",
        "prob = ODEProblem(schrodinger!, ψ0, tspan, H_ising)\n",
        "sol = solve(prob, Tsit5(), saveat=δt)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8054932d",
      "metadata": {},
      "source": [
        "Dalla soluzione, che descrive il vettore di stato $\\psi(t)$, possiamo ricavare la magnetizzazione per sito, espressa come valori attesi del qubit singolo $\\langle Z\\rangle$ in funzione del tempo. Confrontiamo questi risultati con quelli ottenuti dai circuiti trotterizzati.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "27b209db",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11×20 Matrix{Float64}:\n",
              " -1.0       1.0       -1.0       1.0       …  1.0       -1.0       1.0\n",
              " -0.995021  0.995034  -0.995034  0.995034     0.995034  -0.995034  0.995021\n",
              " -0.980189  0.980386  -0.980386  0.980386     0.980386  -0.980386  0.980189\n",
              " -0.955994  0.956968  -0.956968  0.956968     0.956968  -0.956968  0.955994\n",
              " -0.922667  0.925652  -0.925653  0.925653     0.925653  -0.925652  0.922667\n",
              " -0.881106  0.888117  -0.88812   0.88812   …  0.88812   -0.888117  0.881106\n",
              " -0.832251  0.846116  -0.846129  0.846129     0.846129  -0.846116  0.832251\n",
              " -0.776957  0.801257  -0.801298  0.801298     0.801298  -0.801257  0.776957\n",
              " -0.715858  0.754749  -0.75486   0.75486      0.75486   -0.754749  0.715858\n",
              " -0.649744  0.707628  -0.707895  0.707895     0.707895  -0.707628  0.649744\n",
              " -0.580117  0.661272  -0.661841  0.661842  …  0.661841  -0.661272  0.580117"
            ]
          },
          "execution_count": 24,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# get a single-qubit expectation value ⟨Z_qubit⟩ from a full state vector, weighting ±1 by |amplitude|²\n",
        "function z_expval_from_state(ψ::AbstractVector{<:Complex}, qubit::Int, n::Int)\n",
        "    s = 0.0\n",
        "    for b in 0:2^n-1\n",
        "        bit = bit_at(b, qubit)\n",
        "        s += (1 - 2bit) * abs2(ψ[b+1])\n",
        "    end\n",
        "    return s\n",
        "end\n",
        "\n",
        "classical_magnetizations = [z_expval_from_state(sol.u[r+1], q, N)\n",
        "                             for r in 0:r_max, q in 1:N]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c3568ea2",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulation-of-the-trotterized-circuits\" />\n",
        "\n",
        "### Simulazione su piccola scala dei circuiti di Trotter\n",
        "\n",
        "Di seguito illustriamo la simulazione classica dei circuiti privi di rumore utilizzando metodi basati su reti tensoriali supportati da `TensorNetworkQuantumSimulator.jl`, in modo da poter verificare la validità della nostra costruzione dei circuiti. Questi metodi forniscono un punto di riferimento per il confronto con i risultati ottenuti dall'hardware quantistico.\n",
        "\n",
        "Per prima cosa definiamo il reticolo come un grafo a catena di 1D, utilizzando `named_grid((N,))`, dove ogni vertice è una tupla `(i,)`. Specifichiamo quindi le porte del circuito come un elenco di tuple `(gate_name, qubit_indices, gate_parameter)`, che funge da formato di input per il simulatore di reti tensoriali.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "47e7b661",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "make_trotter_circuit_tn (generic function with 1 method)"
            ]
          },
          "execution_count": 25,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# 1D chain graph — vertices are named (1,), (2,), ..., (N,)\n",
        "g = named_grid((N,))\n",
        "\n",
        "# Gates to prepare Néel state |0101…⟩, X on every other site\n",
        "neel_state_gates(n::Int) = [(\"X\", [(i,)]) for i in 1:2:n]\n",
        "\n",
        "# Gates for one second-order Trotter step of size δt\n",
        "trotter_step_gates(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real) = vcat(\n",
        "    [(\"Rx\",  [(i,)],         h[i] * δt) for i in 1:n],\n",
        "    [(\"Rzz\", [(i,), (i+1,)], 2 * J[i] * δt) for i in 1:n-1],\n",
        "    [(\"Rx\",  [(i,)],         h[i] * δt) for i in 1:n])\n",
        "\n",
        "# Make a list of gates: Néel state preparation followed by n_trotter_steps Trotter steps\n",
        "function make_trotter_circuit_tn(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real,\n",
        "                                 n_trotter_steps::Int)\n",
        "    circuit = []\n",
        "\n",
        "    # Neel state initialization\n",
        "    append!(circuit, neel_state_gates(n))\n",
        "\n",
        "    for _ in 1:n_trotter_steps\n",
        "        append!(circuit, trotter_step_gates(h, J, n, δt))\n",
        "    end\n",
        "\n",
        "    return circuit\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2425014b",
      "metadata": {},
      "source": [
        "Utilizziamo l'algoritmo di propagazione delle credenze per la contrazione della rete tensoriale. Questo metodo è efficiente per circuiti con entanglement limitato, ma la sua precisione diminuisce man mano che l'entanglement aumenta con la profondità del circuito. I parametri `cutoff` e `maxdim` determinano il compromesso tra precisione e costo computazionale. Allo stesso modo, calcoliamo la magnetizzazione in ogni sito per poterla confrontare in seguito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "5e3ea2c3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "fidelity at Trotter step 0 was 1.0\n",
            "fidelity at Trotter step 1 was 1.0\n",
            "fidelity at Trotter step 2 was 1.0\n",
            "fidelity at Trotter step 3 was 0.9999999999999679\n",
            "fidelity at Trotter step 4 was 0.9999999999976941\n",
            "fidelity at Trotter step 5 was 0.9999999999476229\n",
            "fidelity at Trotter step 6 was 0.9999999993741544\n",
            "fidelity at Trotter step 7 was 0.9999999993647009\n",
            "fidelity at Trotter step 8 was 0.9999999992323603\n",
            "fidelity at Trotter step 9 was 0.9999999980892764\n",
            "fidelity at Trotter step 10 was 0.9999999980892698\n"
          ]
        }
      ],
      "source": [
        "apply_kwargs = (; maxdim=32, cutoff=1e-10, normalize_tensors=true)\n",
        "tn_magnetizations = zeros(r_max+1, N)\n",
        "\n",
        "# |↑↑…↑⟩ product state, wrapped in a belief propagation cache\n",
        "tn_initial_state(g::NamedGraph) = BeliefPropagationCache(\n",
        "    tensornetworkstate(ComplexF32, v -> \"↑\", g, \"S=1/2\"))\n",
        "\n",
        "# Apply a gate list to a TN state; returns the evolved state and the\n",
        "# truncation fidelity, such as ∏(1 - ε) over all gate applications\n",
        "function apply_gates_to_tn_state(circuit::Vector, ψ_bpc::BeliefPropagationCache; apply_kwargs::NamedTuple)\n",
        "    ψ_bpc, errs = apply_gates(circuit, ψ_bpc; apply_kwargs)\n",
        "    return ψ_bpc, prod(1.0 .- errs)\n",
        "end\n",
        "\n",
        "# ⟨Z_q⟩ on every site of a tensor-network state\n",
        "z_expvals_from_tn_state(ψ_bpc::BeliefPropagationCache, n::Int) =\n",
        "    [real(expect(ψ_bpc, [(\"Z\", [(q,)])])[1]) for q in 1:n]\n",
        "\n",
        "for r in 0:r_max\n",
        "    circuit = make_trotter_circuit_tn(h, J, N, δt, r)\n",
        "    ψ_bpc, fidelity = apply_gates_to_tn_state(circuit, tn_initial_state(g); apply_kwargs)\n",
        "    println(\"fidelity at Trotter step $(r) was $(fidelity)\")\n",
        "    tn_magnetizations[r+1, :] = z_expvals_from_tn_state(ψ_bpc, N)\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aeb0878b",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### Fase 1: Mappare gli input classici su un problema quantistico\n",
        "\n",
        "Ora costruiamo il circuito di evoluzione temporale \"trotterizzato\" utilizzando `Qiskit.jl`. Il circuito rispecchia la versione della rete tensoriale: inizializza lo stato di Néel, applica un $r$ i passi di Trotter costituiti da porte $R_x$ e $R_{ZZ}$ e, infine, misura tutti i qubit nella base Z.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "6b680069",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "QuantumCircuit with 20 qubits, 20 clbits\n",
              "  instructions: 89"
            ]
          },
          "execution_count": 27,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "function make_trotter_circuit(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real, n_trotter_steps::Int)\n",
        "    qc = QuantumCircuit(n, n)\n",
        "\n",
        "    # Neel state initialization\n",
        "    for i in 1:2:n\n",
        "        x!(qc, i)\n",
        "    end\n",
        "\n",
        "    # Trotter evolution\n",
        "    for _ in 1:n_trotter_steps\n",
        "        for i in 1:n\n",
        "            rx!(qc, h[i] * δt, i)\n",
        "        end\n",
        "\n",
        "        for i in 1:n-1\n",
        "            rzz!(qc, 2* J[i] * δt, i, i+1)\n",
        "        end\n",
        "\n",
        "        for i in 1:n\n",
        "            rx!(qc, h[i] * δt, i)\n",
        "        end\n",
        "    end\n",
        "\n",
        "    # measure in Z basis\n",
        "    for i in 1:n\n",
        "        measure!(qc, i, i)\n",
        "    end\n",
        "    return qc\n",
        "end\n",
        "\n",
        "\n",
        "qc = make_trotter_circuit(h, J, N, δt, 1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7a7e4d8f",
      "metadata": {},
      "source": [
        "Abbiamo stilato un elenco di circuiti per gli stadi da 0 a 10 di Trotter, corrispondenti ai tempi di evoluzione $\\tau = 0, \\delta t, 2\\delta t, \\ldots, 10\\delta t$.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "704485a7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QuantumCircuit}:\n",
              " QuantumCircuit(20, 20; 30 instructions)\n",
              " QuantumCircuit(20, 20; 89 instructions)\n",
              " QuantumCircuit(20, 20; 148 instructions)\n",
              " QuantumCircuit(20, 20; 207 instructions)\n",
              " QuantumCircuit(20, 20; 266 instructions)\n",
              " QuantumCircuit(20, 20; 325 instructions)\n",
              " QuantumCircuit(20, 20; 384 instructions)\n",
              " QuantumCircuit(20, 20; 443 instructions)\n",
              " QuantumCircuit(20, 20; 502 instructions)\n",
              " QuantumCircuit(20, 20; 561 instructions)\n",
              " QuantumCircuit(20, 20; 620 instructions)"
            ]
          },
          "execution_count": 28,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# prepare a list of circuits with different Trotter steps\n",
        "qc_list = [make_trotter_circuit(h, J, N, δt, r) for r in 0:r_max]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3b7100d9",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Fase 2: Ottimizzare il problema per l'esecuzione su hardware quantistico\n",
        "\n",
        "Per poter funzionare su hardware quantistico, i circuiti devono prima essere transpilati. Ciò comprende i seguenti passaggi: selezionare un insieme di qubit fisici su cui mappare il circuito, ricompilare i gate nel set di istruzioni nativo del backend e ottimizzare la profondità del circuito risultante. Utilizziamo `least_busy()` per selezionare automaticamente il backend disponibile meno carico, `target_from_backend()` per recuperare il suo insieme di gate nativi e la connettività dei qubit, e `transpile()` per eseguire la compilazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a63ecabe",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "backend.name = \"ibm_phoenix\"\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "\"ibm_phoenix\""
            ]
          },
          "execution_count": 95,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "service = Service()\n",
        "search_results = backend_search(service)\n",
        "backend = least_busy(search_results)\n",
        "@show backend.name"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "14205fe7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Target with 120 qubits\n",
              "  instructions: 8"
            ]
          },
          "execution_count": 37,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "target = target_from_backend(backend, service)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "8828900e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QuantumCircuit}:\n",
              " QuantumCircuit(120, 20; 30 instructions)\n",
              " QuantumCircuit(120, 20; 211 instructions)\n",
              " QuantumCircuit(120, 20; 344 instructions)\n",
              " QuantumCircuit(120, 20; 475 instructions)\n",
              " QuantumCircuit(120, 20; 606 instructions)\n",
              " QuantumCircuit(120, 20; 737 instructions)\n",
              " QuantumCircuit(120, 20; 868 instructions)\n",
              " QuantumCircuit(120, 20; 999 instructions)\n",
              " QuantumCircuit(120, 20; 1130 instructions)\n",
              " QuantumCircuit(120, 20; 1261 instructions)\n",
              " QuantumCircuit(120, 20; 1392 instructions)"
            ]
          },
          "execution_count": 38,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "tqc_list = [transpile(qc, target)[1] for qc in qc_list]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "501e8dc5",
      "metadata": {},
      "source": [
        "Dopo la transpilazione, esaminiamo due proprietà dei circuiti compilati. `get_circuit_layout()` restituisce l'insieme degli indici dei qubit fisici selezionati per il circuito. `two_qubit_depth()` calcola la profondità delle porte a due qubit — ovvero la lunghezza della catena più lunga di operazioni a due qubit presenti nel circuito — che costituisce un indicatore utile dell'accumulo di rumore sull'hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "bf4c910c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Set{Int64} with 20 elements:\n",
              "  35\n",
              "  110\n",
              "  58\n",
              "  12\n",
              "  24\n",
              "  37\n",
              "  23\n",
              "  22\n",
              "  47\n",
              "  69\n",
              "  36\n",
              "  80\n",
              "  109\n",
              "  90\n",
              "  57\n",
              "  34\n",
              "  13\n",
              "  59\n",
              "  70\n",
              "  100"
            ]
          },
          "execution_count": 39,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "function get_circuit_layout(tqc::QuantumCircuit)\n",
        "    return Set(q for inst in tqc.data for q in inst.qubits)\n",
        "end\n",
        "\n",
        "get_circuit_layout(tqc_list[2])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1f5318ec",
      "metadata": {},
      "source": [
        "Di seguito riportiamo il numero di gate a due qubit e la profondità del circuito in corrispondenza di ciascun passo di Trotter; come previsto, entrambi crescono linearmente con il numero di passi. Si noti che il gate “ $R_\\mathrm{ZZ}$ ” frazionario non è ancora disponibile tramite l’API C (vedere [qiskit-ibm-runtime-c#29](https://github.com/Qiskit/qiskit-ibm-runtime-c/issues/29) ). Di conseguenza, ciascuna di esse `RZZGate` viene transpilata in due porte a due qubit anziché in una sola, il che fa aumentare il numero di porte a due qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "id": "5fa03372",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "r=0: 2q gate count=0, 2q gate depth=0\n",
            "r=1: 2q gate count=38, 2q gate depth=38\n",
            "r=2: 2q gate count=76, 2q gate depth=42\n",
            "r=3: 2q gate count=114, 2q gate depth=46\n",
            "r=4: 2q gate count=152, 2q gate depth=50\n",
            "r=5: 2q gate count=190, 2q gate depth=54\n",
            "r=6: 2q gate count=228, 2q gate depth=58\n",
            "r=7: 2q gate count=266, 2q gate depth=62\n",
            "r=8: 2q gate count=304, 2q gate depth=66\n",
            "r=9: 2q gate count=342, 2q gate depth=70\n",
            "r=10: 2q gate count=380, 2q gate depth=74\n"
          ]
        }
      ],
      "source": [
        "two_qubit_count(qc::QuantumCircuit) = count(inst -> length(inst.qubits) == 2, qc.data)\n",
        "\n",
        "function two_qubit_depth(qc::QuantumCircuit)\n",
        "    qubit_depth = Dict{Int,Int}()\n",
        "    for inst in qc.data\n",
        "        length(inst.qubits) == 2 || continue   # skip non-two-qubit gates\n",
        "        d = maximum(get(qubit_depth, q, 0) for q in inst.qubits)\n",
        "        for q in inst.qubits\n",
        "            qubit_depth[q] = d + 1\n",
        "        end\n",
        "    end\n",
        "    return isempty(qubit_depth) ? 0 : maximum(values(qubit_depth))\n",
        "end\n",
        "\n",
        "for (i, tqc) in enumerate(tqc_list)\n",
        "    println(\"r=$(i-1): 2q gate count=$(two_qubit_count(tqc)), 2q gate depth=$(two_qubit_depth(tqc))\")\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "37aabbc8",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Fase 3: Eseguire il comando utilizzando Qiskit primitives\n",
        "\n",
        "Ora possiamo inviare i circuiti transpilati al backend come `Sampler` lavori con specificato `shots` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "id": "7bcdead5",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QiskitIBMRuntime.Job}:\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000003427f72f0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d1b4e0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2dcb180)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b1e24840)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b266e640)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d1b690)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c17980)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b315b9b0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c16090)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c195f0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d33f70)"
            ]
          },
          "execution_count": 41,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "shots = 1024\n",
        "job_list = [run_sampler_job(service, backend, tqc, shots) for tqc in tqc_list]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "id": "9bca942a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job 1: Completed\n",
            "Job 2: Completed\n",
            "Job 3: Completed\n",
            "Job 4: Completed\n",
            "Job 5: Completed\n",
            "Job 6: Completed\n",
            "Job 7: Completed\n",
            "Job 8: Completed\n",
            "Job 9: Completed\n",
            "Job 10: Completed\n",
            "Job 11: Completed\n"
          ]
        }
      ],
      "source": [
        "for (i, job) in enumerate(job_list)\n",
        "    status = get_job_status(job, service)\n",
        "    println(\"Job $i: \", status)\n",
        "end"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f1429d09",
      "metadata": {},
      "source": [
        "Man mano che i lavori vengono completati, possiamo recuperare i relativi risultati. Si noti che la funzione `get_sampler_job_results` rimarrà in attesa fino al completamento del processo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "id": "e8f81320",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11-element Vector{QiskitIBMRuntime.Samples}:\n",
              " [[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0]  …  [1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]\n",
              " [[0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]]\n",
              " [[0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1], [0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]  …  [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1]]\n",
              " [[1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]  …  [1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0], [1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0]]"
            ]
          },
          "execution_count": 47,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "all_samples = [get_sampler_job_results(job, service) for job in job_list]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64b46f68",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Fase 4: Elaborazione finale e restituzione del risultato nel formato classico desiderato\n",
        "\n",
        "A partire dai campioni di stringhe di bit ottenuti dall’hardware quantistico, calcoliamo la magnetizzazione per sito (i valori attesi dell’ $\\langle Z\\rangle$ e del singolo qubit) calcolando la media di $(-1)^{b_i}$ su tutti gli shot, dove $b_i$ è il bit misurato per il qubit $i$. Tracciamo quindi la magnetizzazione sotto forma di mappa termica sui qubit e sui passi di Trotter, confrontando fianco a fianco i tre metodi: simulazione classica esatta, simulazione con rete tensoriale priva di rumore ed esecuzione su hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "id": "e7ee3928",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "11×20 Matrix{Float64}:\n",
              " -0.996094  0.998047  -0.990234  0.998047  …  1.0       -0.988281  0.994141\n",
              " -0.925781  0.980469  -0.878906  0.96875      0.970703  -0.976562  0.988281\n",
              " -0.916016  0.992188  -0.837891  0.957031     0.962891  -0.966797  0.953125\n",
              " -0.884766  0.970703  -0.824219  0.90625      0.908203  -0.939453  0.892578\n",
              " -0.835938  0.96875   -0.814453  0.884766     0.931641  -0.902344  0.908203\n",
              " -0.777344  0.958984  -0.78125   0.871094  …  0.935547  -0.876953  0.939453\n",
              " -0.732422  0.933594  -0.767578  0.8125       0.884766  -0.835938  0.890625\n",
              " -0.650391  0.916016  -0.771484  0.771484     0.853516  -0.773438  0.837891\n",
              " -0.537109  0.902344  -0.705078  0.742188     0.875     -0.662109  0.833984\n",
              " -0.472656  0.923828  -0.652344  0.728516     0.839844  -0.695312  0.808594\n",
              " -0.4375    0.923828  -0.613281  0.681641  …  0.890625  -0.658203  0.8125"
            ]
          },
          "execution_count": 48,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Compute expectation values\n",
        "# 0 -> 1, 1 -> -1\n",
        "z_expval(samples, i) = mean((-1)^s[i] for s in samples)\n",
        "\n",
        "magnetizations = [z_expval(all_samples[i], q) for i in 1:length(all_samples), q in 1:N]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a1b2c3d4",
      "metadata": {},
      "source": [
        "I tre grafici riportati di seguito mostrano la magnetizzazione del sito $\\langle Z_i \\rangle$ in funzione dell’indice del qubit (asse x) e del passo di Trotter (asse y). All’indirizzo $\\delta t = 0.05$ con $r_{\\max} = 10$, il tempo totale di evoluzione è $\\tau = r_{\\max} \\cdot \\delta t = 0.5$, sufficientemente breve da impedire che il modello antiferromagnetico iniziale si sia ancora dissolto: tutti e tre i metodi mostrano un modello fortemente alternato. I risultati ottenuti con il metodo classico e con la rete tensoriale sono ora in stretta concordanza, a conferma del fatto che l'errore di Trotter è minimo con questa dimensione del passo. I risultati relativi all'hardware sono sostanzialmente in linea con quelli degli altri due, anche se alcuni qubit si discostano dalle simulazioni più di altri, il che riflette le variazioni nella qualità dei qubit all'interno del backend. Queste deviazioni aumentano nei passi successivi di Trotter man mano che la profondità del circuito aumenta.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "id": "cc5496cc",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/time-evolution/extracted-outputs/cc5496cc-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 49,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# plot magnetization as a function of time\n",
        "l = @layout [a{0.3w} b{0.3w} c{0.44w}]\n",
        "plot(\n",
        "    heatmap(classical_magnetizations, title=\"Classical\", clims=(-1,1), color=:RdBu,\n",
        "        xlabel=\"Qubit\", ylabel=\"Trotter steps\", colorbar=false),\n",
        "    heatmap(tn_magnetizations, title=\"Tensor Network\", clims=(-1,1), color=:RdBu,\n",
        "        xlabel=\"Qubit\", ylabel=\"Trotter steps\", colorbar=false),\n",
        "    heatmap(magnetizations, title=\"Hardware\", clims=(-1,1), color=:RdBu,\n",
        "        xlabel=\"Qubit\", ylabel=\"Trotter steps\", colorbar=true),\n",
        "    layout=l, size=(900,300),\n",
        "    bottom_margin=5mm, left_margin=5mm, right_margin=6mm\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e9fc890",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\n",
        "\n",
        "<span id=\"steps-1–4-in-a-single-workflow\" />\n",
        "\n",
        "### Passaggi da 1 a 4 in un unico flusso di lavoro\n",
        "\n",
        "Ora combiniamo tutte e quattro le fasi sopra descritte in un unico flusso di lavoro, su una scala che va oltre i limiti della simulazione classica esatta. Anziché determinare la magnetizzazione sito per sito, monitoriamo un unico parametro scalare dell’ordine antiferromagnetico, ovvero la magnetizzazione sfalsata:\n",
        "\n",
        "$$\n",
        "    \\langle M_s \\rangle = \\frac{1}{N} \\sum_{i=1}^{N} (-1)^i \\langle Z_i \\rangle\n",
        "$$\n",
        "\n",
        "Il segno alternato nella somma rende visibile il segnale. Per lo stato iniziale di Néel $|0101\\ldots01\\rangle$ ogni termine contribuisce con $+1$, quindi $\\langle M_s\\rangle = 1$, mentre la media semplice $\\frac{1}{N}\\sum_i \\langle Z_i\\rangle$ si annulla identicamente per tutti i $t$. Poiché il campo trasversale sconvolge il modello alternato, $\\langle M_s\\rangle$ decade verso $0$, quindi la magnetizzazione sfalsata ci indica quanta parte dell’ordine iniziale sopravvive durante l’evoluzione temporale.\n",
        "\n",
        "Utilizziamo questo esempio anche per osservare in che modo la dimensione del passo di Trotter influisca sulla precisione. Fissiamo il tempo totale di evoluzione $T = 1.5$ e variamo il numero di passi di Trotter $r \\in \\{3, 6, 12\\}$, in modo che $\\delta t = T/r$. Sull’hardware, sono in gioco due fonti di errore: un valore più basso di $\\delta t$ riduce l’errore di Trotter, ma richiede un numero proporzionalmente maggiore di porte a due qubit, che accumulano più rumore hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "id": "f840b38f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "  δt = 0.5  →  4 time points, deepest circuit = 3 Trotter steps, 2q count = 594, 2q depth = 206\n",
            "  δt = 0.25  →  7 time points, deepest circuit = 6 Trotter steps, 2q count = 1188, 2q depth = 218\n",
            "  δt = 0.125  →  13 time points, deepest circuit = 12 Trotter steps, 2q count = 2376, 2q depth = 242\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "24-element Vector{QiskitIBMRuntime.Job}:\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1a750)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0a270)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c21a50)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e05120)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c2a6e0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0d140)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0b640)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1a200)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b312eb90)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c22060)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b31ea4e0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1ccf0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c2af50)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b312ffb0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b0e89700)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e15070)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e10d40)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004af5a5d10)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e12500)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e14870)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e10730)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b237ca60)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2c713a0)\n",
              " QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e13e90)"
            ]
          },
          "execution_count": 50,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "# Map classical inputs to a quantum problem.\n",
        "N_large = 100\n",
        "g_large = named_grid((N_large,))\n",
        "h_large = fill(1.0, N_large)       # transverse field on every site\n",
        "J_large = fill(1.0, N_large - 1)   # nearest-neighbor ZZ couplings on the chain\n",
        "\n",
        "T_total = 1.5                    # fixed total evolution time\n",
        "r_list = [3, 6, 12]       # varying Trotter steps; δt = T_total/r\n",
        "sweep = [(r, k) for r in r_list for k in 0:r]\n",
        "\n",
        "qc_list_large = [make_trotter_circuit(h_large, J_large, N_large, T_total/r, k)\n",
        "                 for (r, k) in sweep]\n",
        "\n",
        "# -------------------------Step 2-------------------------\n",
        "# Optimize the problem for quantum hardware execution.\n",
        "tqc_list_large = [transpile(qc, target)[1] for qc in qc_list_large]\n",
        "# Print the 2q gate count and depth of the deepest circuit at each δt\",\n",
        "for r in r_list\n",
        "    i = findfirst(==((r, r)), sweep)   # the k = r circuit reaches the full T_total\n",
        "    println(\"  δt = $(round(T_total/r, digits=4))  →  $(r+1) time points, \",\n",
        "        \"deepest circuit = $(r) Trotter steps, \",\n",
        "        \"2q count = $(two_qubit_count(tqc_list_large[i])), \",\n",
        "        \"2q depth = $(two_qubit_depth(tqc_list_large[i]))\")\n",
        "end\n",
        "\n",
        "# -------------------------Step 3-------------------------\n",
        "# Execute using Qiskit primitives.\n",
        "shots_large = 4096\n",
        "\n",
        "job_list_large = [run_sampler_job(service, backend, tqc, shots_large)\n",
        "                  for tqc in tqc_list_large]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 53,
      "id": "10004db8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job 1 (δt=0.5, k=0): Completed\n",
            "Job 2 (δt=0.5, k=1): Completed\n",
            "Job 3 (δt=0.5, k=2): Completed\n",
            "Job 4 (δt=0.5, k=3): Completed\n",
            "Job 5 (δt=0.25, k=0): Completed\n",
            "Job 6 (δt=0.25, k=1): Completed\n",
            "Job 7 (δt=0.25, k=2): Completed\n",
            "Job 8 (δt=0.25, k=3): Completed\n",
            "Job 9 (δt=0.25, k=4): Completed\n",
            "Job 10 (δt=0.25, k=5): Completed\n",
            "Job 11 (δt=0.25, k=6): Completed\n",
            "Job 12 (δt=0.125, k=0): Completed\n",
            "Job 13 (δt=0.125, k=1): Completed\n",
            "Job 14 (δt=0.125, k=2): Completed\n",
            "Job 15 (δt=0.125, k=3): Completed\n",
            "Job 16 (δt=0.125, k=4): Completed\n",
            "Job 17 (δt=0.125, k=5): Completed\n",
            "Job 18 (δt=0.125, k=6): Completed\n",
            "Job 19 (δt=0.125, k=7): Completed\n",
            "Job 20 (δt=0.125, k=8): Completed\n",
            "Job 21 (δt=0.125, k=9): Completed\n",
            "Job 22 (δt=0.125, k=10): Completed\n",
            "Job 23 (δt=0.125, k=11): Completed\n",
            "Job 24 (δt=0.125, k=12): Completed\n"
          ]
        }
      ],
      "source": [
        "# Run this cell to check the job status\n",
        "# Run the following cell for post-processing after all jobs complete\n",
        "for (i, job) in enumerate(job_list_large)\n",
        "    r, k = sweep[i]\n",
        "    println(\"Job $i (δt=$(round(T_total/r, digits=4)), k=$k): \",\n",
        "            get_job_status(job, service))\n",
        "end"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 54,
      "id": "a3646d14",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "24-element Vector{Float64}:\n",
              " 0.9880371093750097\n",
              " 0.697231445312484\n",
              " 0.44886718750000276\n",
              " 0.29333496093750067\n",
              " 0.9882324218750095\n",
              " 0.8109619140625324\n",
              " 0.6457958984374791\n",
              " 0.4808593749999992\n",
              " 0.36695312500000055\n",
              " 0.2963671875000012\n",
              " 0.23831542968750055\n",
              " 0.9884472656250093\n",
              " 0.8566455078125492\n",
              " 0.7951171875000282\n",
              " 0.7050732421874865\n",
              " 0.610673828124981\n",
              " 0.529980468749995\n",
              " 0.4656054687500018\n",
              " 0.4149316406250024\n",
              " 0.3799462890625011\n",
              " 0.35178710937500185\n",
              " 0.3185888671875003\n",
              " 0.31287597656250016\n",
              " 0.29621093750000077"
            ]
          },
          "execution_count": 54,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# -------------------------Step 4-------------------------\n",
        "# Post-process and return the result in the desired classical format.\n",
        "# Run this cell after all jobs are completed\n",
        "\n",
        "# ⟨M_s⟩ = (1/N) Σ (-1)^i ⟨Z_i⟩, averaged over hardware shots\n",
        "function staggered_magnetization(samples::AbstractVector{<:AbstractVector}, n::Int)\n",
        "    s = 0.0\n",
        "    for sample in samples\n",
        "        s += sum((-1)^i * (1 - 2 * sample[i]) for i in 1:n) / n\n",
        "    end\n",
        "    return s / length(samples)\n",
        "end\n",
        "\n",
        "all_samples_large = [get_sampler_job_results(job, service) for job in job_list_large]\n",
        "mags_hardware = [staggered_magnetization(s, N_large) for s in all_samples_large]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89c310c8",
      "metadata": {},
      "source": [
        "All'indirizzo $N=100$ non è possibile ottenere la soluzione esatta fornita dal risolutore di equazioni differenziali ordinarie utilizzato in precedenza, poiché il solo vettore di stato richiederebbe amp $2^{100} \\approx 10^{30}$. Utilizziamo invece come riferimento una simulazione con rete tensoriale priva di rumore della stessa catena di 1D con un passo di Trotter molto più fine ( $r = 96$, $\\delta t \\approx 0.016$ ), in cui l’errore di Trotter è trascurabile rispetto a qualsiasi simulazione $\\delta t$ che eseguiamo su hardware. Questo riferimento è di per sé approssimativo: il suo errore principale è ora il troncamento della dimensione delle legami di cui si è parlato in precedenza, riportato come “fedeltà di troncamento” per ciascuna esecuzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 55,
      "id": "fbcc23a1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Tensor-network reference:\n",
            "  δt=0.01562, 96 steps: truncation fidelity ≈ 1.0\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5], Float32[1.0, 0.9695576, 0.8870231, 0.77430177, 0.6560442, 0.5499543, 0.46285573, 0.39298016, 0.33518773, 0.28527063, 0.24147007, 0.20369667, 0.17206171])"
            ]
          },
          "execution_count": 55,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "apply_kwargs = (; maxdim=64, cutoff=1e-10, normalize_tensors=true)\n",
        "\n",
        "# Calculate the staggered magnetization given a tensor network state\n",
        "staggered_magnetization(ψ_bpc::BeliefPropagationCache, n::Int) =\n",
        "    sum((-1)^q * m for (q, m) in enumerate(z_expvals_from_tn_state(ψ_bpc, n))) / n\n",
        "\n",
        "# Evolve a TN state and record the staggered magnetization at each step.\n",
        "function compute_staggered_magnetization_tn(δt::Real, nsteps::Int; record_every::Int = 1)\n",
        "    init_gates = neel_state_gates(N_large)\n",
        "    step_gates = trotter_step_gates(h_large, J_large, N_large, δt)\n",
        "\n",
        "    ψ_bpc, fid = apply_gates_to_tn_state(init_gates, tn_initial_state(g_large); apply_kwargs)\n",
        "    times = [0.0]\n",
        "    mags = [staggered_magnetization(ψ_bpc, N_large)]\n",
        "    for k in 1:nsteps\n",
        "        ψ_bpc, fid_step = apply_gates_to_tn_state(step_gates, ψ_bpc; apply_kwargs)\n",
        "        fid *= fid_step\n",
        "        if k % record_every == 0\n",
        "            push!(times, k * δt)\n",
        "            push!(mags, staggered_magnetization(ψ_bpc, N_large))\n",
        "        end\n",
        "    end\n",
        "    println(\"  δt=$(round(δt, digits=5)), $(nsteps) steps: truncation fidelity ≈ $(round(fid, digits=5))\")\n",
        "    (times, mags)\n",
        "end\n",
        "\n",
        "# If the fidelity drifts from 1, raise `maxdim` in `apply_kwargs`.\n",
        "# Under current setting, the tensor network simulation takes ~ 3 minutes on a laptop.\n",
        "println(\"Tensor-network reference:\")\n",
        "r_ref = 96\n",
        "times_ref, mags_ref = compute_staggered_magnetization_tn(T_total / r_ref, r_ref; record_every = r_ref ÷ 12)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "af333ad4",
      "metadata": {},
      "source": [
        "Il grafico riportato di seguito mostra l'andamento della magnetizzazione sfalsata nel tempo per le tre dimensioni del passo di Trotter, rispetto al riferimento della rete tensoriale priva di rumore (linea nera tratteggiata).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 56,
      "id": "94cd122e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/time-evolution/extracted-outputs/94cd122e-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 56,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "plt = plot(xlabel = \"Time\", ylabel = \"Staggered magnetization\",\n",
        "    title = \"N = $(N_large) on $(backend.name), T = $(T_total)\",\n",
        "    legend = :topright, ylims = (-0.05, 1.05), size = (820, 480),\n",
        "    bottom_margin = 5mm, left_margin = 5mm)\n",
        "\n",
        "# Plot tensor network reference\n",
        "plot!(plt, times_ref, mags_ref, lw = 2, ls = :dash, color = :black,\n",
        "      label = \"tensor network, δt → 0\")\n",
        "\n",
        "# Plot hardware result per Trotter step size\n",
        "for (r, stop) in zip(r_list, cumsum(r_list .+ 1))\n",
        "    plot!(plt, range(0, T_total, length = r + 1), mags_hardware[(stop - r):stop],\n",
        "          marker = :circle, markersize = 4, lw = 2,\n",
        "          label = \"hardware, δt = $(round(T_total / r, digits = 4))\")\n",
        "end\n",
        "plt"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "603fd062",
      "metadata": {},
      "source": [
        "Nel complesso, lo step di Trotter più grossolano $\\delta t = 0.5$ (punti arancioni) mostra la deviazione maggiore rispetto al riferimento della rete tensoriale, presumibilmente a causa di un contributo significativo dell’errore di Trotter. Al passo più fine $\\delta t = 0.25$ (punti verdi), i risultati dell'hardware sono più in linea con quelli di riferimento. All’incremento minimo $\\delta t = 0.125$ (punti viola), l’errore di Trotter è minimo, ma la concordanza è inferiore rispetto a quella osservata all’ $\\delta t = 0.25$. Con un incremento pari alla metà, ogni punto temporale richiede il doppio delle porte a due qubit e il rumore aggiuntivo supera la riduzione dell’errore di Trotter. La scelta di un circuito \" $\\delta t$ \" per un circuito \"Trotterized\" su hardware rappresenta quindi un compromesso tra l'errore di Trotter e il rumore accumulato dalle porte aggiuntive.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8defe5bb",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "Potresti trovare interessante il seguente materiale:\n",
        "\n",
        "* [Formule multiprodotto per ridurre l'errore di Trotter](/docs/tutorials/multi-product-formula)\n",
        "\n",
        "* [Corso sull'integrazione tra informatica quantistica e calcolo ad alte prestazioni](/learning/courses/integrating-quantum-and-high-performance-computing)\n",
        "\n",
        "* [Qiskit.jl](https://github.com/Qiskit/Qiskit.jl) e [QiskitIBMRuntime.jl](https://github.com/Qiskit/QiskitIBMRuntime.jl) su GitHub\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Julia 1.11",
      "language": "julia",
      "name": "julia-1.11"
    },
    "language_info": {
      "file_extension": ".jl",
      "mimetype": "application/julia",
      "name": "julia",
      "version": "1.11.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}