{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Avvio rapido\"\n",
        "description: \"Guida rapida all'ultima versione dell'Operator Backpropagation (OBP)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quick-start\" />\n",
        "\n",
        "# Avvio rapido\n",
        "\n",
        "Questa guida illustra un esempio minimale funzionante del pacchetto `qiskit-addon-obp` . Utilizziamo la retropropagazione degli operatori (OBP) per ridurre la profondità di un circuito quantistico, incorporando i gate finali nell'osservabile.\n",
        "\n",
        "Il valore atteso $\\langle O \\rangle = \\langle \\psi | U^\\dagger O U | \\psi \\rangle$ rimane invariato se si rimuove dal circuito $U$ un blocco finale di porte logiche e si coniuga l'osservabile in modo classico in base a tale blocco. OBP applica questo approccio ripetutamente, valutando una parte del circuito in modo classico, in modo che sull'hardware venga eseguito solo un circuito più semplice. Il prezzo da pagare è che ogni gate assorbito può espandere l'osservabile in un numero maggiore di termini di Pauli, quindi la profondità risparmiata deve essere valutata alla luce dell'aumento dell'osservabile.\n",
        "\n",
        "Per vedere alcuni esempi su come creare flussi di lavoro realistici con questo strumento ed eseguirli su hardware quantistico, consulta i tutorial disponibili sull’ IBM Quantum Platform ( [Tutorial OBP](/docs/tutorials/operator-back-propagation) ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "inputs-header",
      "metadata": {},
      "source": [
        "<span id=\"prepare-inputs-for-obp\" />\n",
        "\n",
        "## Preparare i dati di input per l'OBP\n",
        "\n",
        "OBP accetta come input un elenco di sezioni di circuito e un osservabile. Esegue la retropropagazione delle sezioni una alla volta, partendo dalla fine del circuito verso l'osservabile, riducendo la profondità del circuito a costo di termini di Pauli aggiuntivi nell'osservabile. In questa sede generiamo un circuito di evoluzione temporale per un modello di Heisenberg a 10 qubit e lo suddividiamo per tipo di gate.\n",
        "\n",
        "Di seguito riportiamo il circuito originale, seguito dallo stesso circuito riorganizzato con delle barriere che segnano i confini delle sezioni: ciascuna sezione è un’unità che può essere assorbita nell’osservabile in un unico passo di retropropagazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "setup-inputs",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit depth: 18\n",
            "Number of slices: 18\n",
            "Observable terms: 1\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.synthesis import LieTrotter\n",
        "from qiskit.transpiler import CouplingMap\n",
        "from qiskit_addon_utils.problem_generators import (\n",
        "    generate_time_evolution_circuit,\n",
        "    generate_xyz_hamiltonian,\n",
        ")\n",
        "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n",
        "\n",
        "# Generate a circuit to reduce\n",
        "coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)\n",
        "reduced_coupling_map = coupling_map.reduce(\n",
        "    [0, 13, 1, 14, 10, 16, 5, 12, 8, 18]\n",
        ")\n",
        "\n",
        "hamiltonian = generate_xyz_hamiltonian(\n",
        "    reduced_coupling_map,\n",
        "    coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),\n",
        "    ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),\n",
        ")\n",
        "\n",
        "circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian,\n",
        "    time=0.2,\n",
        "    synthesis=LieTrotter(reps=2),\n",
        ")\n",
        "\n",
        "# Slice the circuit and define an observable\n",
        "slices = slice_by_gate_types(circuit)\n",
        "observable = SparsePauliOp(\"IIIIIIIIIZ\")\n",
        "\n",
        "print(f\"Original circuit depth: {circuit.depth()}\")\n",
        "print(f\"Number of slices: {len(slices)}\")\n",
        "print(f\"Observable terms: {len(observable)}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "46efdcfc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/46efdcfc-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Sliced circuit (recombined with barriers for visualization)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/46efdcfc-3.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Recombine the slices with barriers to make the slice boundaries visible\n",
        "sliced_circuit = combine_slices(slices, include_barriers=True)\n",
        "\n",
        "print(\"Original circuit:\")\n",
        "display(circuit.draw(\"mpl\", scale=0.6, fold=-1))\n",
        "print(\"Sliced circuit (recombined with barriers for visualization)\")\n",
        "sliced_circuit.draw(\"mpl\", scale=0.6, fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "obp-header",
      "metadata": {},
      "source": [
        "<span id=\"reduce-circuit-depth-with-obp\" />\n",
        "\n",
        "## Ridurre la profondità del circuito con OBP\n",
        "\n",
        "Chiamiamo `backpropagate` per incorporare le sezioni nell'osservabile. La funzione restituisce l'osservabile espanso, le sezioni del circuito che non sono state propagate e i metadati relativi alla procedura.\n",
        "\n",
        "Se non controllata, la grandezza osservabile può crescere verso termini di Pauli di ordine $2^n$. Questo limite `operator_budget` pone un tetto massimo a tale crescita: in questo caso ammettiamo al massimo 8 gruppi commutanti a livello di qubit, il che determina approssimativamente il numero di tentativi necessari per valutare l’osservabile sulla QPU. La retropropagazione si interrompe non appena l'assorbimento della fetta successiva supererebbe il budget, ed è esattamente ciò che accade nell'esempio riportato di seguito: solo 7 delle 18 fette vengono assorbite prima che l'osservabile riempia tutti e 8 i gruppi di commutazione e la procedura si interrompa.\n",
        "\n",
        "Per approfondire l'analisi del circuito, `backpropagate` è possibile utilizzare l'argomento \" `truncation_error_budget` keyword\" di per eliminare i termini di Pauli con coefficienti piccoli dall'osservabile man mano che questo cresce. Ciò attenua la crescita dell'osservabile in cambio di un errore proporzionale all'ampiezza dei termini di Pauli che vengono troncati. I due budget sono complementari e possono essere utilizzati insieme: `operator_budget` regola l' *entità* della grandezza osservabile, mentre `truncation_error_budget` regola l' *errore* derivante dall'esclusione di alcuni termini.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "run-obp",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 7 of 18 slices.\n",
            "Reduced circuit depth: 11 (was 18)\n",
            "Observable grew from 1 to 18 Pauli terms.\n",
            "Filled 8 of 8 commuting groups, exhausting the budget.\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "\n",
        "max_qwc_groups = 8\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    observable,\n",
        "    slices,\n",
        "    operator_budget=OperatorBudget(max_qwc_groups=max_qwc_groups),\n",
        ")\n",
        "\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "num_groups = len(bp_obs.group_commuting(qubit_wise=True))\n",
        "\n",
        "print(\n",
        "    f\"Backpropagated {metadata.num_backpropagated_slices} of {len(slices)} slices.\"\n",
        ")\n",
        "print(\n",
        "    f\"Reduced circuit depth: {reduced_circuit.depth()} (was {circuit.depth()})\"\n",
        ")\n",
        "print(f\"Observable grew from {len(observable)} to {len(bp_obs)} Pauli terms.\")\n",
        "print(\n",
        "    f\"Filled {num_groups} of {max_qwc_groups} commuting groups, exhausting the budget.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b006625e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/b006625e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reduced_circuit.draw(\"mpl\", scale=0.6, fold=-1)"
      ]
    },
    {
      "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
}