{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Troncare i termini di Pauli durante la retropropagazione\"\n",
        "description: \"Troncare i termini di Pauli durante la retropropagazione per l'ultima versione della retropropagazione degli operatori (OBP)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "11cf5076-2777-499c-be50-531e305bf011",
      "metadata": {},
      "source": [
        "<span id=\"truncate-pauli-terms-during-backpropagation\" />\n",
        "\n",
        "# Troncare i termini di Pauli durante la retropropagazione\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1453472-3506-4008-b432-f8ef9ccbf3ee",
      "metadata": {},
      "source": [
        "Questa guida spiega come configurare il meccanismo di troncamento del termine di Pauli fornito dal modulo \" [qiskit\\_addon\\_obp.utils.truncating](/docs/api/qiskit-addon-obp/utils-truncating#module-qiskit_addon_obp.utils.truncating) \".\n",
        "\n",
        "La retropropagazione degli operatori (OBP) può essere utilizzata per ridurre la profondità dei circuiti quantistici, a costo di un osservabile più complesso. Per ottenere risultati significativi dall'OBP, di solito è necessario troncare i termini della variabile osservabile per evitare che diventi troppo estesa. Un modo per consentire una retropropagazione più profonda all'interno del circuito, evitando al contempo che l'operatore diventi troppo grande, consiste nel troncare i termini con coefficienti piccoli, anziché aggiungerli all'operatore. Il troncamento dei termini può comportare una riduzione del numero di circuiti quantistici da eseguire, ma ciò comporta un errore nel calcolo del valore atteso finale, proporzionale all'ampiezza dei coefficienti dei termini troncati.\n",
        "\n",
        "Il metodo [di retropropagazione](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) accetta un parametro opzionale denominato [`TruncationErrorBudget`](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget), che configura il troncamento dei termini di Pauli a basso peso per ciascun osservabile dopo il completamento con esito positivo della retropropagazione di ogni slice.\n",
        "Il numero di termini troncati dipende da vari parametri di configurazione specificati dall'utente.\n",
        "Al momento è disponibile una sola strategia di troncamento: il metodo [truncate\\_binary\\_search](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search).\n",
        "Dato un osservabile e un determinato *budget*, il sistema effettuerà una ricerca binaria tra i termini di Pauli e i coefficienti all’interno di tale osservabile per individuare la soglia ottimale,\n",
        "in modo tale che la somma dei coefficienti troncati sia massima ma inferiore al budget.\n",
        "\n",
        "**Nota** : per impostazione predefinita, per valutare e limitare l'errore di troncamento viene utilizzata la norma L1; tuttavia, l'impostazione `p_norm` consente di specificare la norma Lp da utilizzare.\n",
        "Per ulteriori informazioni su come utilizzare tale impostazione, consultare la guida “[Utilizzo di diverse norme Lp per il troncamento del termine di Pauli](/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm) ”.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aaf5f046-6d5e-4b6f-add7-b60604edef30",
      "metadata": {},
      "source": [
        "Gli esempi seguenti illustrano diversi modi per creare un \" [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) \" utilizzando la funzione \" [setup\\_budget](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget)\" allegata.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c83c0741-4a9e-4f89-92a9-4756ef133130",
      "metadata": {},
      "source": [
        "<span id=\"construct-an-example-circuit\" />\n",
        "\n",
        "## Costruire un circuito di esempio\n",
        "\n",
        "La presente guida utilizza le seguenti sezioni di circuito:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "297fc3b0-d3b4-432d-96cd-7e175b7e6a52",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import rustworkx.generators\n",
        "from qiskit.synthesis import LieTrotter\n",
        "from qiskit_addon_utils.problem_generators import (\n",
        "    PauliOrderStrategy,\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 linear chain of 10 qubits\n",
        "linear_chain = rustworkx.generators.path_graph(10)\n",
        "\n",
        "# Use an arbitrary XY model\n",
        "hamiltonian = generate_xyz_hamiltonian(\n",
        "    linear_chain,\n",
        "    coupling_constants=(0.05, 0.02, 0.0),\n",
        "    ext_magnetic_field=(0.02, 0.08, 0.0),\n",
        "    pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,\n",
        ")\n",
        "# Evolve for some time\n",
        "circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian, synthesis=LieTrotter(reps=3), time=2.0\n",
        ")\n",
        "# slice the circuit by gate type\n",
        "slices = slice_by_gate_types(circuit)\n",
        "\n",
        "# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit\n",
        "combine_slices(slices, include_barriers=True).draw(\"mpl\", fold=50, scale=0.6)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89ce4fa9-06ba-4009-8594-d1b85f5e719f",
      "metadata": {},
      "source": [
        "Prenderemo in esame un unico osservabile semplice:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "b168d34c-f147-4524-9af9-264f56440a55",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "obs = SparsePauliOp(\"IIIIIZIIII\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "723dcf4e-1dab-4c26-a8e4-cd30b1564189",
      "metadata": {},
      "source": [
        "<span id=\"the-simplest-case-a-fixed-truncation-budget-for-each-slice\" />\n",
        "\n",
        "## Il caso più semplice: un limite di troncamento fisso per ogni slice\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e04e5c4-1e6b-4720-9563-becbb9edc4d7",
      "metadata": {},
      "source": [
        "Il budget disponibile per il troncamento dei termini di Pauli può variare in ogni fase della retropropagazione.\n",
        "Per capire come funziona, esaminiamo innanzitutto il caso più semplice, ovvero quello di un budget di troncamento fisso specificato dall'utente.\n",
        "\n",
        "Il modo più semplice per specificare il limite di troncamento è utilizzare l'argomento `max_error_per_slice` .\n",
        "Infatti, è proprio quello che viene illustrato nel tutorial “[Riduzione della profondità dei circuiti con la retropropagazione degli operatori](/docs/tutorials/operator-back-propagation) ”. Impostando il valore su `max_error_per_slice` `float` , a ciascuna fetta viene assegnato un budget pari a tale valore.\n",
        "Nell'esempio riportato di seguito, abbiamo impostato questo valore su, il che `0.001` garantisce un errore di troncamento implicito pari al massimo a `0.018`, nel caso in cui venissero sottoposte a retropropagazione tutte le 18 sezioni.\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  Si noti che l'eventuale margine di errore residuo dopo la retropropagazione di una sezione e il troncamento dei termini con coefficienti piccoli verrà sempre aggiunto al margine di errore della sezione successiva.\n",
        "</div>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "5f20a9b9-c3c4-43c4-9f13-fdf451958081",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp.utils.truncating import setup_budget\n",
        "\n",
        "truncation_error_budget = setup_budget(max_error_per_slice=0.001)\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "c3b8b9ef-7d5c-43dd-8cb9-90756d4753b7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 11 circuit slices.\n",
            "New observable contains 29 terms and 10 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "\n",
        "op_budget = OperatorBudget(max_qwc_groups=10)\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4a772af6-edc6-4e53-81c7-53f3b72bf58a",
      "metadata": {},
      "source": [
        "Successivamente utilizziamo l'istanza [OBPMetadata](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata) e gli strumenti forniti dal modulo [di visualizzazione](/docs/api/qiskit-addon-obp/utils-visualization#module-qiskit_addon_obp.utils.visualization) per visualizzare il processo di retropropagazione.\n",
        "\n",
        "* **Il grafico in alto a sinistra** mostra che disponiamo di un budget sufficiente per iniziare a troncare i termini osservabili dopo la retropropagazione della terza porzione. A partire dalla terza porzione, sappiamo che troncamo almeno un termine da ciascuna porzione su cui eseguiamo la retropropagazione, poiché dopo ogni porzione si verifica un certo errore di troncamento.\n",
        "* **Il grafico in alto a destra** mostra che il margine di errore aumenta fino a `.003` per la terza porzione. Si osserva un brusco calo del budget residuo, il che significa che alcuni termini sono stati troncati dai dati osservabili. Ciò è in linea con quanto abbiamo dedotto dal grafico in alto a sinistra.\n",
        "* **Il grafico in basso a sinistra** mostra che, man mano che eliminiamo i termini dalla nostra grandezza osservabile, il nostro errore complessivo accumulato cresce in modo monotono. Questo grafico conferma inoltre che nessun termine è stato troncato fino a quando non è stata eseguita la retropropagazione della terza porzione.\n",
        "* **Il grafico in basso a destra** mostra che il numero di gruppi di Pauli in commutazione nel nostro osservabile è cresciuto fino al limite specificato di `10`. Questo grafico mostra inoltre come l'applicazione della retropropagazione a un ulteriore strato farebbe sì che la nostra grandezza osservabile superasse il limite specificato, come si può notare dall'intersezione delle linee nera e rossa.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8ff436fa-3b52-4387-a9f4-d8d9df13c480",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\">\n",
        "  Si noti che in tutti questi grafici l'asse x indica le sezioni sottoposte a retropropagazione; tuttavia, poiché l'OBP opera alla fine del circuito, `slice 1` è l'ultima sezione in assoluto, `slice 2` è quella precedente e così via.\n",
        "</div>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "68f118df-6718-49b2-ba80-c37ef461f71a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from matplotlib import pyplot as plt\n",
        "from qiskit_addon_obp.utils.visualization import (\n",
        "    plot_accumulated_error,\n",
        "    plot_left_over_error_budget,\n",
        "    plot_num_qwc_groups,\n",
        "    plot_slice_errors,\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "523f5a81-afbc-4eee-9d66-05cc3b4a781a",
      "metadata": {},
      "source": [
        "<span id=\"specify-slice-budget-explicitly\" />\n",
        "\n",
        "## Specificare esplicitamente il budget per le slice\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "80f9337d-ea37-454c-ae0b-b101259568c9",
      "metadata": {},
      "source": [
        "Se sai come assegnare un budget a ciascuna slice in modo da ottimizzare le prestazioni della retropropagazione, potresti voler assegnare esplicitamente un budget a ciascuna slice. A scopo dimostrativo, assegneremo un budget pari a zero alle prime tre sezioni e utilizzeremo un budget di per `.001` sezione per le sezioni rimanenti.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "17ce7232-7e3a-44e4-8081-1b1b27430bad",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "# Zero out the first 3 slices' budgets\n",
        "max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_per_slice=max_error_per_slice\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "7cd711a7-1aea-4b45-a46a-63d771230969",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 11 circuit slices.\n",
            "New observable contains 32 terms and 10 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3e853723-454b-4c69-b9c5-180eb1b2e6c2",
      "metadata": {},
      "source": [
        "La rimozione del budget dai primi tre livelli ha fatto sì che nessun termine venisse troncato fino a dopo il quarto livello, come si può verificare in tre di questi grafici. Ciò che potrebbe risultare un po’ sorprendente è che, sebbene alla quarta fascia non fosse stato trasferito alcun budget residuo, un periodo è stato comunque troncato utilizzando il budget `.001` stanziato. Ciò risulta evidente dal grafico **in** alto a sinistra, ma si può osservare anche in quello **in alto a destra**, poiché la curva del budget residuo si appiattisce tra la sezione tre e la sezione quattro. Un altro dettaglio degno di nota è che, a partire da questo punto, almeno un termine è stato troncato, proprio come nell’esempio precedente.\n",
        "\n",
        "Il punto fondamentale è che, sebbene nel secondo esempio si sia verificato un errore di troncamento minore a causa dell’azzeramento dei budget di alcune sezioni, siamo riusciti a eseguire la retropropagazione sullo stesso numero di sezioni e il nostro osservabile contiene lo stesso numero di gruppi di Pauli commutativi. Possiamo confermare che il limite del nostro errore è minore nel secondo esempio osservando il grafico **in basso a sinistra**.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "b7167f80-5d29-4c32-be97-cba733f18b1d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f03a92ee-e9a0-4707-b23a-ebdc3c9f35a3",
      "metadata": {},
      "source": [
        "<span id=\"specify-the-budget-cyclically\" />\n",
        "\n",
        "## Specificare il bilancio su base ciclica\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62f0ea1f-eaec-408d-99df-25a8647c1eb9",
      "metadata": {},
      "source": [
        "Se si dispone di un circuito con uno schema ripetibile, come ad esempio un circuito di Trotter, potrebbe essere opportuno specificare un budget per quel sottoinsieme ripetuto di sezioni e fare in modo che tale budget venga utilizzato per tutte le successive ripetizioni di quelle sezioni.\n",
        "\n",
        "Più precisamente, il circuito di esempio che stiamo utilizzando presenta sei sezioni che si ripetono tre volte, per un totale di 18 sezioni. Assegneremo arbitrariamente un budget pari a zero agli strati `RYY` a singolo qubit e, e un budget `.003` pari a a ciascuno degli `RXX` strati. Vedremo come, specificando il budget come sequenza di tipo “ length-6 ”, il budget venga applicato ciclicamente a tutte le 18 sezioni.\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  Si noti ancora una volta che le sezioni vengono sottoposte a retropropagazione in ordine inverso (cioè partendo dalla fine). Pertanto, la prima voce del nostro bilancio ciclico viene effettivamente utilizzata per l'ultima tranche, la seconda voce per la tranche precedente e così via.\n",
        "</div>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4f37a576-5a61-4589-a896-8304056d0e19",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "# Specify a length-6 per-slice budget.\n",
        "# This will be cycled over three times to be applied to the 18 slices\n",
        "max_error_per_slice = [0.0] * 4 + [0.003] * 2\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_per_slice=max_error_per_slice\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "7f78cfe4-13e4-472c-b6cc-9d1e867cbd9c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 13 circuit slices.\n",
            "New observable contains 49 terms and 14 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "op_budget = OperatorBudget(max_qwc_groups=20)\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3511206b-2972-41ba-b981-6c20ccb7712b",
      "metadata": {},
      "source": [
        "Come si può vedere nelle immagini **in** alto a sinistra e **in basso a sinistra**, non è stato effettuato alcun troncamento sulle prime quattro sezioni, poiché a queste non era stato assegnato alcun margine di errore. Le sezioni cinque e sei hanno visto alcuni dei loro termini ridotti man mano che si rendevano disponibili i fondi.\n",
        "\n",
        "Come si può vedere nell'immagine **in alto a sinistra**, dopo la retropropagazione della fetta numero sette si è verificato un errore relativamente consistente, nonostante a quella fetta non fosse stato assegnato alcun budget. Questo perché è stato utilizzato solo circa del `.002` budget totale `.006` stanziato per le sezioni cinque e sei, quindi la parte restante è stata trasferita alla sezione sette e in gran parte spesa, come si vede nell'immagine in **alto a destra**.\n",
        "\n",
        "Le immagini **in** alto a sinistra e **in alto a destra** mostrano che la piccola quota di budget residua viene esaurita tra le sezioni 8 e 10 e che, come previsto, nella sezione 11 diventa disponibile nuovo budget. Le immagini **in** alto a sinistra, **in alto a destra** e **in basso a sinistra** mostrano tutte il comportamento ciclico dell'argomento `max_error_per_slice` quando la sua lunghezza è inferiore al numero di sezioni. Questo andamento ciclico sarebbe proseguito per tutte le sezioni, ma il criterio di arresto `max_qwc_groups` è stato raggiunto dopo la retropropagazione di 13 sezioni, come si vede nell'immagine in **basso a destra**.\n",
        "\n",
        "È interessante notare inoltre che il numero dei gruppi di Pauli diminuisce effettivamente dopo che, alla fetta 11, diventa disponibile la seconda tranche di bilancio. Ciò è dovuto al fatto che c'erano gruppi con coefficienti piccoli che non potevano essere troncati finché non si rendeva disponibile una maggiore quantità di budget dopo la retropropagazione della slice 11, quindi si sono accumulati nell'osservabile per diverse iterazioni. Questo caso evidenzia inoltre che `max_qwc_groups` deve essere *superato* affinché l'algoritmo termini.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "4c36768e-03e7-4e1b-81b3-59b6e9eab43e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "269536c5-8ba4-48da-8e99-6b8ce3a52bd0",
      "metadata": {},
      "source": [
        "<span id=\"cap-the-total-error\" />\n",
        "\n",
        "## Limitare l'errore totale\n",
        "\n",
        "Oltre a specificare il margine di errore per ogni fetta, è possibile specificare il valore massimo di errore che si può incorrere a causa del troncamento. Una volta raggiunto tale limite, non verrà più eseguito alcun troncamento; tuttavia, la retropropagazione proseguirà finché l'osservabile non diventerà troppo grande e non verrà soddisfatto uno dei criteri di arresto.\n",
        "\n",
        "Successivamente ripetiamo l'esperimento sopra descritto, impostando un limite massimo per `max_error_total` in modo tale che il budget di errore venga esaurito dopo la retropropagazione della settima porzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "8ef6a100-fb4b-4b66-be6e-a094db2c29d4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "# Specify a length-6 per-slice budget.\n",
        "# This will be cycled over 3 times to be applied to the 18 slices\n",
        "max_error_per_slice = [0.0] * 4 + [0.003] * 2\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_per_slice=max_error_per_slice, max_error_total=0.006\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "c58bccd5-aa40-482f-acaf-4ef50d420053",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 10 circuit slices.\n",
            "New observable contains 67 terms and 20 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dadb77a5-00d7-427d-921b-4a005f5ab36f",
      "metadata": {},
      "source": [
        "Come previsto, il nostro errore di troncamento raggiunge il valore massimo `.006` indicato nel grafico **in basso a sinistra**. È da notare che in questa esecuzione non siamo riusciti a eseguire la retropropagazione della slice 11. Questo perché non disponevamo di un budget sufficiente per troncare i termini e il numero di gruppi di Pauli pendolari ha superato il limite di `20`, come si vede nell'immagine **in** basso a destra.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "69c9069a-9038-4319-8dc6-37a1be973ebf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2049d223-1b91-4042-ba98-a86f7a59483c",
      "metadata": {},
      "source": [
        "Potrebbe essere opportuno limitarsi a fissare un tetto massimo al bilancio complessivo senza `max_error_total` specificare ulteriori dettagli `max_error_per_slice`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "fac59f4f-3528-49c1-9d4d-9be98d2c7d78",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "truncation_error_budget = setup_budget(max_error_total=0.018)\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20c55e75-f619-4ac9-aecd-29491867ab82",
      "metadata": {},
      "source": [
        "Il risultato della cella sopra potrebbe risultare leggermente sorprendente, poiché è `per_slice_budget` impostato su `max_error_total`. Ciò indica che l'intero budget disponibile verrà consumato **senza ritegno**.\n",
        "Si può ragionare in questo modo: l’intero budget è a disposizione di ciascuna fetta (poiché si esegue un ciclo su `per_slice_budget`). Tuttavia, qualsiasi importo già utilizzato verrà detratto dal budget disponibile in quel determinato momento dell'algoritmo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "5d65a057-eb5e-4308-b84d-82bfbea06989",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 9 circuit slices.\n",
            "New observable contains 25 terms and 9 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "op_budget = OperatorBudget(max_qwc_groups=10)\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a77ceb1e-bc6d-462d-aebe-ec478d312233",
      "metadata": {},
      "source": [
        "L'immagine **in alto a destra** mostra come l'intero budget di errore `.018` venga messo a disposizione della prima sezione. Fino alla terza porzione non viene effettuato alcun taglio, quindi il budget residuo rimane costante. Il budget diminuisce in modo monotono, poiché l'intero budget viene messo a disposizione di ciascuna porzione sottoposta a retropropagazione fino al suo esaurimento.\n",
        "\n",
        "È interessante notare che questo esperimento ha prodotto due sezioni sottoposte a retropropagazione in meno rispetto al primo esperimento contenuto in questo notebook, che è pressoché identico. Ciò dimostra che, per alcuni problemi, distribuire il budget in modo uniforme potrebbe essere la soluzione ottimale. Per altri problemi, consentire alle slice di utilizzare avidamente l'intero budget potrebbe garantire prestazioni migliori.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "51d36d7e-0bd5-4388-bbb8-6831bf1f02e6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8859d36c-258f-441f-a7cf-3997b9f6e8ff",
      "metadata": {},
      "source": [
        "<span id=\"cap-the-number-of-backpropagated-slices-and-the-total-error-together\" />\n",
        "\n",
        "## Limitare contemporaneamente il numero di sezioni sottoposte a retropropagazione e l'errore totale\n",
        "\n",
        "Se non si desidera distribuire il budget di errore lungo l'intero circuito, ma non si vuole nemmeno esaurirlo in modo eccessivo, è possibile specificare il numero di slice su cui si intende eseguire la retropropagazione (`num_slices`), insieme al budget di errore totale (`max_error_total`). In questo modo il budget di errore verrà distribuito uniformemente (secondo `p_norm`) tra le slice di input.\n",
        "\n",
        "In questo caso, limiteremo a il numero di sezioni su cui potremmo eseguire la retropropagazione `12`e manterremo lo stesso budget di errore totale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "474cf708-5447-4984-873c-5f62f81a402d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "num_slices = 12\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_total=0.018, num_slices=num_slices, p_norm=1\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ac2fb58-db97-4c9e-9a96-fce97b52e7e2",
      "metadata": {},
      "source": [
        "Ora proveremo a eseguire la retropropagazione delle 12 sezioni per le quali abbiamo stanziato una quota nel passaggio precedente. Per farlo, basta inserire le ultime 12 sezioni nel nostro circuito. Con `p_norm=1`, ciascuna delle 12 fette dispone di un budget pari a `0.018 / num_slices = 0.0015`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "6efc92bc-3433-4ea6-a683-684205201f4f",
      "metadata": {},
      "outputs": [],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices[-num_slices:],\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b15c2a20-19c3-417c-b32d-173c10f13852",
      "metadata": {},
      "source": [
        "Poiché abbiamo passato un sottoinsieme delle nostre sezioni (nel codice `slices[-num_slices:]`) a `backpropagate`, dobbiamo combinare le sezioni rimaste dopo la retropropagazione con quelle che non sono mai state inviate alla retropropagazione (nel codice ) `slices[:-num_slices]`.\n",
        "\n",
        "Una volta unite tutte le fette rimanenti, possiamo utilizzare [la funzione `combine\\_slices`](/docs/api/qiskit-addon-utils/slicing#combine_slices) per generare l'[QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) a profondità ridotta. Successivamente, analizziamo il numero di sezioni sottoposte a retropropagazione rispetto all'entità raggiunta dal nostro osservabile.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "7c42256f-e7f2-40ed-999e-6a51fd9351c4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 12 circuit slices.\n",
            "New observable contains 29 terms and 9 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "# Recombine the slices remaining after backprop with the rest of the original circuit\n",
        "reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)\n",
        "\n",
        "print(f\"Backpropagated {num_slices - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae2d383c-c11c-457e-856f-85602f2df306",
      "metadata": {},
      "source": [
        "I grafici mostrano che siamo riusciti a eseguire con successo la retropropagazione su tutte e 12 le sezioni, mantenendo la nostra osservabile all’interno di 10 gruppi di Pauli commutanti. Possiamo inoltre notare che l'utilizzo di insieme `num_slices` a porta `max_error_total` a una ripartizione dei budget tra le fasce e che il budget non utilizzato viene nuovamente trasferito alla fascia successiva. Ciò risulta particolarmente evidente nel grafico **in alto a destra**, poiché il budget viene sia utilizzato che reintegrato durante tutta la fase di retropropagazione.\n",
        "\n",
        "È degno di nota il fatto che questo metodo di distribuzione dell'errore abbia prodotto i risultati migliori (un numero maggiore di sezioni sottoposte a retropropagazione), rispetto al primo esperimento di questo notebook e all'esempio immediatamente precedente. In tutti questi esempi è stato assegnato un “budget di `.018` errore”, ma la retropropagazione ha dato risultati diversi a seconda di come tale budget è stato distribuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "b0bf5050-2928-41a1-9c3d-c9d79e918f6f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5c554774-2986-43a0-bdcd-45eaeb81ef5a",
      "metadata": {},
      "source": [
        "<span id=\"work-with-multiple-observables\" />\n",
        "\n",
        "## Lavorare con più osservabili\n",
        "\n",
        "Il metodo `qiskit_addon_obp.backpropagate` consente di passare una sequenza di osservabili. Ciò semplifica il flusso di lavoro quando si ha a che fare con più grandezze osservabili di destinazione.\n",
        "\n",
        "Lo sottolineiamo espressamente in questa sede per illustrarvi come la strategia di troncamento gestisce un caso del genere. Ai fini di questo esempio, aggiungiamo un osservabile in più a quello che abbiamo utilizzato finora:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "0857ea4c-82c1-4641-a268-1a9ba44a312b",
      "metadata": {},
      "outputs": [],
      "source": [
        "obs = [SparsePauliOp(\"IIIIIZIIII\"), SparsePauliOp(\"IIIIIXIIII\")]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba3ac81c-7395-4287-907a-742a1751cbd0",
      "metadata": {},
      "source": [
        "Infine, ripetiamo il primo esperimento di questo tutorial., ma con due grandezze osservabili in cui eseguire la retropropagazione del circuito.\n",
        "\n",
        "In questo esempio, ciò non influisce sul numero di sezioni che potrebbero essere sottoposte a retropropagazione. Tuttavia, possiamo notare che i due osservabili hanno dato origine a un numero diverso di termini di Pauli e di gruppi commutativi.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "10c50f14-5bbd-427d-ae77-24389498a1e4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "truncation_error_budget = setup_budget(max_error_per_slice=0.001)\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "862edaec-fc2c-488b-875d-23841ecc80b3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 11 circuit slices.\n",
            "The new first observable contains 29 terms and 10 commuting groups.\n",
            "The new second observable contains 23 terms and 8 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")\n",
        "print(\n",
        "    f\"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "467c614d-6bac-4826-a42b-39937b9d0c72",
      "metadata": {},
      "source": [
        "I grafici riportati di seguito aiutano a illustrare come l'algoritmo di retropropagazione gestisca più osservabili.\n",
        "\n",
        "In primo luogo, i grafici in **alto a sinistra**, **in alto a destra** e in **basso a sinistra** mostrano che il budget per il troncamento dei termini viene impostato individualmente per ciascuna variabile osservabile. In altre parole, entrambe le grandezze osservabili sono in grado di troncare i termini ipotizzando un errore pari a per `0.001` ogni slice sottoposta a retropropagazione.\n",
        "Data la diversa natura delle grandezze osservabili, ciò comporta un consumo diverso del budget. In questo esempio, si nota una notevole sovrapposizione, cosa che non sempre si verifica.\n",
        "\n",
        "Il grafico **in basso a destra** mostra che `max_qwc_groups` tiene conto di *tutte* le grandezze osservabili. Ciò significa che i termini di tutte le grandezze osservabili vengono raggruppati per ottenere un numero finale di gruppi commutanti a livello di qubit, che viene poi confrontato con `max_qwc_paulis`. Lo stesso vale per la soglia `max_paulis` (di cui non si parla in questo quaderno), che consente di impostare un limite al numero di termini di Pauli su tutti gli osservabili.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 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
}