{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Avvio rapido\"\n",
        "description: \"Guida rapida all'ultima versione di Pauli Propagation\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a9320fa8-31c5-4248-96b0-3549a13dda6f",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Avvio rapido\"\n",
        "description: \"Guida rapida al pacchetto di componenti aggiuntivi pauli-prop per Qiskit\"\n",
        "---\n",
        "\n",
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Avvio rapido\n",
        "\n",
        "In questa guida utilizziamo il pacchetto `pauli-prop` per simulare in modo classico le dinamiche temporali di un modello di Ising “kicked” a 10 qubit su una catena di spin di tipo “ 1D ”.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3b5bf7dc-1cd8-41d3-b8ce-cc75a66fed8d",
      "metadata": {},
      "source": [
        "<span id=\"prepare-the-inputs-for-pauli-propagation\" />\n",
        "\n",
        "## Preparare i dati di input per la propagazione di Pauli\n",
        "\n",
        "L'Hamiltoniano considerato è:\n",
        "\n",
        "$H = -J\\sum\\limits_{\\langle i,j \\rangle} Z_iZ_j + h\\sum\\limits_iX_i$\n",
        "\n",
        "dove $J>0$ descrive l'accoppiamento tra gli spin più vicini, $i<j$, e $h$ è il campo trasversale globale. Una decomposizione di Trotter di primo ordine dell’operatore evoluto nel tempo verrà implementata come circuito quantistico, $U$, in $20$ passi di Trotter. La costante di accoppiamento, $J$, sarà fissata a $J=-\\frac{\\pi}{2}$, mentre $h$ sarà fissata a $\\frac{\\pi}{6}$. Le interazioni $ZZ$ saranno implementate utilizzando i gate di Clifford ( $CX$, $Sdg$, $\\sqrt{Y}$ ).\n",
        "\n",
        "Implementiamo l’evoluzione temporale “trotterizzata” come circuito quantistico e utilizziamo l’ $\\frac{\\pi}{6}$ e per le rotazioni non-Clifford attorno all’asse x. Quanto più questi angoli si discostano dagli angoli di Clifford (ad esempio, $\\theta=n\\frac{\\pi}{2}, n \\in \\mathbb{Z}$ ), tanto più difficile sarà simulare il sistema con i metodi di propagazione di Pauli.\n",
        "\n",
        "Per la scelta dell’osservabile, consideriamo la magnetizzazione media di un singolo sito, $\\frac{1}{N} \\sum_{i=1}^{N} \\langle z_i \\rangle$, dove $N$ è il numero di spin.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "15d5eccf-eef0-4435-b67a-98a8038c400e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/pauli-prop/guides/quickstart/extracted-outputs/15d5eccf-eef0-4435-b67a-98a8038c400e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler import CouplingMap\n",
        "\n",
        "num_qubits = 10\n",
        "coupling_map = CouplingMap.from_line(num_qubits, bidirectional=False)\n",
        "\n",
        "# Num Trotter steps\n",
        "num_steps = 20\n",
        "theta_rx = np.pi / 6\n",
        "\n",
        "# Average single-site magnetization\n",
        "observable = (\n",
        "    SparsePauliOp(\n",
        "        [\n",
        "            \"I\" * iq + \"Z\" + \"I\" * (num_qubits - iq - 1)\n",
        "            for iq in range(num_qubits)\n",
        "        ]\n",
        "    )\n",
        "    / num_qubits\n",
        ")\n",
        "\n",
        "# Create the Trotter circuit\n",
        "num_qubits = 10\n",
        "num_steps = 20\n",
        "theta_rx = np.pi / 6\n",
        "circuit = QuantumCircuit(num_qubits)\n",
        "edges = CouplingMap.from_line(num_qubits, bidirectional=False).get_edges()\n",
        "for _ in range(num_steps):\n",
        "    circuit.rx(theta_rx, [i for i in range(num_qubits)])\n",
        "    for edge in edges:\n",
        "        circuit.sdg(edge)\n",
        "        circuit.ry(np.pi / 2, edge[1])\n",
        "        circuit.cx(edge[0], edge[1])\n",
        "        circuit.ry(-np.pi / 2, edge[1])\n",
        "circuit.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c05fa0f8-605e-4172-8880-0de89360f27d",
      "metadata": {},
      "source": [
        "<span id=\"simulate-the-time-evolution-of-the-system-with-pauli-propagation\" />\n",
        "\n",
        "## Simulare l'evoluzione temporale del sistema con la propagazione di Pauli\n",
        "\n",
        "Una volta definiti il circuito ( $U$ ) e l’osservabile ( $O$ ), possiamo simulare facilmente il sistema in pochi passaggi:\n",
        "\n",
        "* Separare $U$ nelle sue parti relative a Clifford, $C$, e non relative a Clifford, $P$, in modo tale che $U=PC$ utilizzando `evolve_through_cliffords`\n",
        "* Applicare la funzione \" $O$ \" alla funzione \" $P$ \", ottenendo una nuova funzione, \" $O^\\prime$ \", utilizzando `pauli_prop.propagate_through_circuit`\n",
        "* Evolvere l' $O^\\prime$ e lungo la parte Clifford del circuito utilizzando il supporto per l'evoluzione Clifford integrato in Qiskit\n",
        "* Approssimare il valore atteso come $\\langle0|O^\\prime|0\\rangle \\approx \\langle0|U^\\dagger OU|0\\rangle$ sommando i coefficienti in $O^\\prime$ associati ai termini di Pauli completamente diagonali (termini di Pauli contenenti o `I` `Z` su tutti i qubit). Ricordate che si tratta di un'approssimazione, poiché abbiamo troncato i termini provenienti dall' $O^\\prime$ e mentre li propagavamo attraverso la parte non-Clifford del circuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "c8a27469-ab72-4d50-9544-d446af847da5",
      "metadata": {},
      "outputs": [],
      "source": [
        "import time\n",
        "\n",
        "from pauli_prop import evolve_through_cliffords, propagate_through_circuit\n",
        "\n",
        "cliff, non_cliff = evolve_through_cliffords(circuit)\n",
        "\n",
        "max_terms_list = [10**i for i in range(8)]\n",
        "approx_evs = []\n",
        "durations = []\n",
        "for max_terms in max_terms_list:\n",
        "    st = time.perf_counter()\n",
        "    evolved_obs = propagate_through_circuit(\n",
        "        observable, non_cliff, max_terms=max_terms, atol=1e-12, frame=\"h\"\n",
        "    )[0]\n",
        "    evolved_obs.paulis = evolved_obs.paulis.evolve(cliff, frame=\"h\")\n",
        "    durations.append(time.perf_counter() - st)\n",
        "    approx_evs.append(\n",
        "        float(evolved_obs.coeffs[~evolved_obs.paulis.x.any(axis=1)].sum())\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7fb3e72f-20fb-4fd0-a301-2fcc6c0ddad5",
      "metadata": {},
      "source": [
        "Man mano che si eseguono calcoli più complessi, le approssimazioni del valore atteso diventano più precise. In questo esempio, saturiamo l'intero spazio di Pauli a circa $4^{10}\\approx10^6$, il che si riflette nell'appiattimento della curva tra gli ultimi due punti.\n",
        "\n",
        "Sebbene il grafico riportato di seguito mostri una convergenza monotona, le simulazioni di propagazione di Pauli generalmente non convergono in modo monotono. Non è raro osservare un andamento “irregolare” in questo tipo di grafici.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "f12410aa-ef3b-4ee6-ab35-f7e9001a92b8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0.5, 1.0, 'Simulating 20-step 1D Ising Model')"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/pauli-prop/guides/quickstart/extracted-outputs/f12410aa-ef3b-4ee6-ab35-f7e9001a92b8-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "from qiskit_aer import AerSimulator\n",
        "\n",
        "sim_circ = circuit.copy()\n",
        "sim_circ.save_statevector()\n",
        "backend = AerSimulator(method=\"statevector\")\n",
        "psi = backend.run(sim_circ).result().data()[\"statevector\"]\n",
        "exact_ev = psi.expectation_value(observable)\n",
        "\n",
        "ax1 = plt.gca()\n",
        "ax1.plot(max_terms_list, approx_evs, marker=\"o\", label=\"Approximate\")\n",
        "ax1.axhline(exact_ev, linestyle=\"--\", color=\"green\", label=\"Exact\")\n",
        "ax1.set_xscale(\"log\")\n",
        "ax1.set_xlabel(\"# terms kept\")\n",
        "ax1.set_ylabel(r\"$\\frac{1}{N} \\sum_{i=1}^{N} \\langle z_i \\rangle$\")\n",
        "\n",
        "ax2 = ax1.twinx()\n",
        "ax2.plot(\n",
        "    max_terms_list, durations, marker=\".\", label=\"Runtime\", color=\"orange\"\n",
        ")\n",
        "ax2.set_ylabel(\"Runtime (s)\", color=\"orange\")\n",
        "ax2.set_yscale(\"log\")\n",
        "\n",
        "handles1, labels1 = ax1.get_legend_handles_labels()\n",
        "handles2, labels2 = ax2.get_legend_handles_labels()\n",
        "ax1.legend(handles1 + handles2, labels1 + labels2, loc=\"lower right\")\n",
        "\n",
        "plt.title(f\"Simulating {num_steps}-step 1D Ising Model\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}