{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d2c31ae8",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Taglio a filo per la stima dei valori attesi\"\n",
        "description: \"Utilizzare il taglio dei fili per suddividere i circuiti in tanti sottocircuiti più piccoli.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore edgecolor Cutqc forall fontsize */}\n",
        "\n",
        "<span id=\"wire-cutting-for-expectation-values-estimation\" />\n",
        "\n",
        "# Taglio a filo per la stima dei valori attesi\n",
        "\n",
        "*Stima del tempo di esecuzione: 22 secondi su un processore Heron (NOTA: si tratta solo di una stima). (La durata potrebbe variare.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bf80006",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Dopo aver seguito questo tutorial, gli utenti dovrebbero aver compreso:\n",
        "\n",
        "* Come utilizzare [`qiskit-addon-cutting`](https://github.com/Qiskit/qiskit-addon-cutting) per suddividere un circuito di grandi dimensioni in sottocircuiti più piccoli, riducendo così l'effetto del rumore\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Consigliamo agli utenti di acquisire familiarità con l'argomento seguente prima di seguire questo tutorial:\n",
        "\n",
        "* Utilizzando la primitiva [Sampler](/docs/api/qiskit-ibm-runtime/sampler-v2), impiegata in questo flusso di lavoro\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "Il termine \"circuito a maglie\" è un concetto generico che racchiude vari metodi di suddivisione di un circuito in più sottocircuiti più piccoli, composti da un numero inferiore di porte logiche o qubit. Ciascuno dei sottocircuiti può essere eseguito in modo indipendente, e il risultato finale si ottiene mediante una classica post-elaborazione dei risultati di ciascun sottocircuito. Questa tecnica è disponibile [nell'add-on \"Circuit cutting\" di Qiskit](https://qiskit.github.io/qiskit-addon-cutting/index.html); per una spiegazione dettagliata della tecnica, consultare la [documentazione](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html) e [gli](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) altri materiali introduttivi.\n",
        "\n",
        "Questo tutorial si concentra su un metodo denominato \" **taglio del filo** \", in cui il circuito viene suddiviso lungo il filo [\\[1\\], \\[2\\]](#references). Si noti che la suddivisione è semplice nei circuiti classici, poiché il risultato nel punto di suddivisione può essere determinato in modo deterministico ed è pari a 0 o 1. Tuttavia, lo stato del qubit nel momento del taglio è, in generale, uno stato misto. Pertanto, ogni sottocircuito deve essere misurato più volte in basi diverse (di solito una base tomograficamente completa, come la base di Pauli [\\[3\\], \\[4\\]](#references) ) e preparato di conseguenza nel proprio stato proprio. La figura seguente (per gentile concessione [di](#references) \\[7]) mostra un esempio di suddivisione di uno stato GHZ a quattro qubit in tre sottocircuiti. Qui $M_j$ indica un insieme di basi (di solito Pauli X, Y e Z), mentre $P_i$ indica un insieme di stati propri (di solito $|0\\rangle$, $|1\\rangle$, $|+\\rangle$ e $|+i\\rangle$ ).\n",
        "\n",
        "![wc-1.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/0ce8857b-7f5f-400e-8536-6a496c724d50.avif)\n",
        "![wc-2.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/cbce4455-4794-4c81-8630-3e3993e1b29f.avif)\n",
        "\n",
        "Poiché ogni sottocircuito ha un numero inferiore di qubit e di porte logiche, ci si aspetta che siano meno sensibili al rumore. Questo tutorial illustra un esempio in cui è possibile utilizzare questo metodo per eliminare efficacemente il rumore nel sistema.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisiti\n",
        "\n",
        "Prima di iniziare questa esercitazione, assicuratevi di aver installato quanto segue:\n",
        "\n",
        "* Qiskit SDK v2.0 o versioni successive, con supporto [alla visualizzazione](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.22 o successivamente ( `pip install qiskit-ibm-runtime` )\n",
        "* Disattivazione del circuito - Componente aggiuntivo Qiskit v0.10.0 o versioni successive (`pip install qiskit-addon-cutting`)\n",
        "* Qiskit addon utils 0.3 o versioni successive (`pip install qiskit-addon-utils`)\n",
        "* Qiskit Aer (`pip install qiskit-aer` )\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit\n",
        "from qiskit.quantum_info import PauliList, SparsePauliOp\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit.result import sampled_expectation_value\n",
        "\n",
        "from qiskit_addon_cutting.instructions import CutWire\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_wires,\n",
        "    expand_observables,\n",
        "    partition_problem,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "431a5bd2-e6ed-471b-ad9e-c4edd27784a8",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Esempio di simulatore su piccola scala\n",
        "\n",
        "Questo tutorial implementa un [modello Qiskit](/docs/guides/intro-to-patterns) per simulare un circuito di localizzazione a molti corpi (MBL) 1D e. Il circuito MBL è un circuito efficiente dal punto di vista hardware ed è parametrizzato da due parametri $\\theta$ e $\\vec{\\phi}$. Quando $\\theta$ è impostato su $0$ e lo stato iniziale è preparato in $|0\\rangle$ per tutti i qubit, il valore atteso ideale di $\\langle Z_i \\rangle$ è $+1$ per ogni sito di qubit $i$ indipendentemente dai valori di $\\vec{\\phi}$. Maggiori dettagli su questo circuito sono disponibili in questo [articolo](https://www.nature.com/articles/s41467-025-57623-x).\n",
        "\n",
        "Si noti che in un simulatore privo di rumore, il valore atteso ottenuto con e senza interruzione del circuito sarà lo stesso.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "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",
        "<span id=\"construct-the-1d-mbl-circuit\" />\n",
        "\n",
        "#### Costruire il circuito MBL \" 1D \"\n",
        "\n",
        "In primo luogo, presentiamo una funzione per la costruzione del circuito MBL ( 1D ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "d2aa988a",
      "metadata": {},
      "outputs": [],
      "source": [
        "class MBLChainCircuit(QuantumCircuit):\n",
        "    def __init__(\n",
        "        self, num_qubits: int, depth: int, use_cut: bool = False\n",
        "    ) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainCircuit<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "        evolution = MBLChainEvolution(num_qubits, depth, use_cut)\n",
        "        self.compose(evolution, inplace=True)\n",
        "\n",
        "\n",
        "class MBLChainEvolution(QuantumCircuit):\n",
        "    def __init__(self, num_qubits: int, depth: int, use_cut) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainEvolution<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "\n",
        "        theta = Parameter(\"θ\")\n",
        "        phis = ParameterVector(\"φ\", num_qubits)\n",
        "\n",
        "        for layer in range(depth):\n",
        "            layer_parity = layer % 2\n",
        "            # print(\"layer parity\", layer_parity)\n",
        "            for qubit in range(layer_parity, num_qubits - 1, 2):\n",
        "                # print(qubit)\n",
        "                self.cz(qubit, qubit + 1)\n",
        "                self.u(theta, 0, np.pi, qubit)\n",
        "                self.u(theta, 0, np.pi, qubit + 1)\n",
        "                if (\n",
        "                    use_cut\n",
        "                    and layer_parity == 0\n",
        "                    and (\n",
        "                        qubit == num_qubits // 2 - 1\n",
        "                        or qubit == num_qubits // 2\n",
        "                    )\n",
        "                ):\n",
        "                    self.append(CutWire(), [num_qubits // 2])\n",
        "                if use_cut and layer < depth - 1 and layer_parity == 1:\n",
        "                    if qubit == num_qubits // 2:\n",
        "                        self.append(CutWire(), [qubit])\n",
        "            for qubit in range(num_qubits):\n",
        "                self.p(phis[qubit], qubit)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a3debf65-06df-4277-933e-14b6f6170756",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/a3debf65-06df-4277-933e-14b6f6170756-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_qubits = 10\n",
        "depth = 2\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "mbl.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ef13f367",
      "metadata": {},
      "source": [
        "Calcoliamo il valore atteso medio $O = \\frac{1}{n} \\sum_i Z_i$ su tutti i qubit per $\\theta = 0$. Poiché il valore atteso ideale di $\\langle Z_i \\rangle = 1$ $\\forall$ $i$, anche il valore atteso ideale di $O$ è $1$. I parametri $\\phi$ sono scelti a caso.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b0f1e5fd",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a81f0346",
      "metadata": {},
      "source": [
        "Per suddividere il circuito, è necessario inserire delle annotazioni con la dicitura \" **CutWire** \" nei punti desiderati. Per questo tutorial, abbiamo scelto una partizione equidistante. Il circuito MBL è progettato in modo tale che l'impostazione `use_cut=True` nella funzione inserisca correttamente l'annotazione dopo $\\frac{n}{2}$ qubit, dove $n$ è il numero di qubit nel circuito originale. Abbiamo inoltre assegnato al circuito i parametri generati in modo casuale.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "5208e0a8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/5208e0a8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_cut.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Fase 2: Ottimizzazione del problema per l'esecuzione su hardware quantistico\n",
        "\n",
        "<span id=\"cut-the-circuit-into-smaller-subcircuits\" />\n",
        "\n",
        "#### Taglia il circuito in sottocircuiti più piccoli\n",
        "\n",
        "Ora suddividiamo il circuito in due sottocircuiti più piccoli utilizzando [`qiskit-addon-cutting`](https://qiskit.github.io/qiskit-addon-cutting/). `qiskit-addon-cutting` aggiunge un gate virtuale `Move` per dividere il punto di taglio del cavo regolando opportunamente il numero di qubit. Ora creiamo il circuito con questo gate virtuale. Poiché c'è un filo tagliato, il numero di qubit associati aumenterà di 1.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "1834cb22",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1834cb22-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_move = cut_wires(mbl_cut)\n",
        "mbl_move.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1df16e6b",
      "metadata": {},
      "source": [
        "<span id=\"construct-and-expand-the-observable\" />\n",
        "\n",
        "#### Costruire ed espandere l'osservabile\n",
        "\n",
        "L'osservabile, come definito in precedenza, sarà la media dell' $Z$ e su ciascun qubit. Tuttavia, una volta inserito il gate virtuale `Move` , il numero effettivo di qubit nel circuito aumenta. Anche l'osservabile deve essere esteso di conseguenza per tenere conto di questa variazione nel numero di qubit. Si noti che l'osservabile agisce sempre in modo banale (come in $I$ ) sul qubit aggiuntivo inserito per il gate virtuale `Move` .\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "3074b173",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',\n",
              "           'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',\n",
              "           'IIIIIIIIZI', 'IIIIIIIIIZ'])"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "observable"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32b7081b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',\n",
              "           'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',\n",
              "           'IIIIIIIIIZI', 'IIIIIIIIIIZ'])"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "new_obs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ad0886b",
      "metadata": {},
      "source": [
        "Ora il circuito può essere suddiviso lungo il `Move` gate e si ottengono i sottocircuiti, nonché il sottoosservabile, ovvero la porzione dell'osservabile originale associata a ciascun sottocircuito.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "c8894a06",
      "metadata": {},
      "outputs": [],
      "source": [
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f47ab33",
      "metadata": {},
      "source": [
        "Qui riportiamo i due sottocircuiti:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1b0e779f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1b0e779f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "3c802f28",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/3c802f28-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[1].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e4c03fb",
      "metadata": {},
      "source": [
        "Per estendere l'osservabile utilizzando l'operazione `Move` è necessaria una `PauliList` struttura dati. Per ricostruire il valore atteso del circuito originale, abbiamo bisogno dell'osservabile nel `SparsePauliOp` formato.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "7dc51220",
      "metadata": {},
      "outputs": [],
      "source": [
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b5a5746",
      "metadata": {},
      "source": [
        "Come già detto, per ogni taglio il circuito a monte deve essere misurato in una base di Pauli, mentre il circuito a valle deve essere preparato nello stato proprio di tale base. La funzione `generate_cutting_experiments` crea tutti i circuiti necessari e i coefficienti associati a ciascun circuito richiesti per la ricostruzione. Per ulteriori dettagli, consultare [questo documento](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.125.150504).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "648519f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "43f58cfb",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuits-onto-the-backend\" />\n",
        "\n",
        "#### Compilare i circuiti sul backend\n",
        "\n",
        "Per il primo esempio, che riguarda esclusivamente la simulazione, trasponiamo il circuito nell'insieme di porte di base del backend:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "29d71cd3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=133\n",
        ")\n",
        "\n",
        "print(backend)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n",
        "Ora, esegui ciascun sottoesperimento:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "05fabb27",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=2, basis_gates=backend.configuration().basis_gates\n",
        ")\n",
        "basis_subexperiments = {\n",
        "    label: pm_basis.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "b22a1b00",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=AerSimulator())\n",
        "jobs = {\n",
        "    label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "    for label, subsystem_subexpts in basis_subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50b94af2",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Fase 4: Post-elaborazione e restituzione del risultato nel formato classico desiderato\n",
        "\n",
        "Ora recuperiamo il risultato di ciascuna esecuzione del sottoesperimento e ricostruiamo il valore atteso del circuito non tagliato:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "31dc35ea-6554-4ca7-9c3b-0b5394c46e4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve results\n",
        "results = {label: job.result() for label, job in jobs.items()}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "4972e4e4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.float64(0.9953821063041687)"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "reconstructed_expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "fb5f955a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0, 0.5, '$M_Z$')"
            ]
          },
          "execution_count": 32,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/fb5f955a-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "methods = [\n",
        "    \"Uncut\",\n",
        "    \"Wire cut\",\n",
        "]\n",
        "values = [\n",
        "    1,\n",
        "    reconstructed_expval,\n",
        "]  # since the ideal expectation value in noiseless simulation is +1\n",
        "\n",
        "ax = plt.gca()\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "ax.set_ylabel(r\"$M_Z$\", fontsize=12)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d6db390-e7a8-4efe-902c-8d9a312170c6",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae69c5e0-32b1-4f03-ab13-7b95a9acfd25",
      "metadata": {},
      "source": [
        "Ora illustriamo il taglio dei fili per un circuito MBL da 60 qubit. Sia i circuiti non tagliati che quelli tagliati saranno realizzati su hardware dell' IBM Quantum® :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "37834c72",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/37834c72-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "num_qubits = 60\n",
        "depth = 2\n",
        "\n",
        "# construct the circuit\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "\n",
        "# create parameters\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis\n",
        "\n",
        "# construct the cut circuit\n",
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_move = cut_wires(mbl_cut)\n",
        "\n",
        "# Define observable and expand to account for the wire cut\n",
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "\n",
        "# Construct a SparsePauliOp version of the observable for later use in reconstruction\n",
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")\n",
        "\n",
        "# Partition the circuit and get subcircuits and subobservables\n",
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables\n",
        "\n",
        "# Obtain subexperiments and coefficients\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")\n",
        "\n",
        "# Transpile the subexperiments to the backend\n",
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "isa_subexperiments = {\n",
        "    label: pm.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}\n",
        "\n",
        "# Execute the subexperiments and retrieve results\n",
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(mode=batch)\n",
        "    sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "    jobs = {\n",
        "        label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "        for label, subsystem_subexpts in isa_subexperiments.items()\n",
        "    }\n",
        "results = {label: job.result() for label, job in jobs.items()}\n",
        "\n",
        "# Reconstruct the expectation value of the original observable\n",
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "\n",
        "# Compute the uncut circuit to obtain the noisy expectation value for comparison\n",
        "sampler = SamplerV2(mode=backend)\n",
        "sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "\n",
        "if mbl.num_clbits == 0:\n",
        "    mbl.measure_all()\n",
        "isa_mbl = pm.run(mbl)\n",
        "\n",
        "pub = (isa_mbl, params)\n",
        "uncut_job = sampler.run([pub])\n",
        "\n",
        "uncut_counts = uncut_job.result()[0].data.meas.get_counts()\n",
        "uncut_expval = sampled_expectation_value(uncut_counts, M_z)\n",
        "\n",
        "# visualize the results\n",
        "ax = plt.gca()\n",
        "methods = [\"uncut\", \"cut\"]\n",
        "values = [uncut_expval, reconstructed_expval]\n",
        "\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "plt.axhline(y=1, color=\"k\", linestyle=\"--\")\n",
        "plt.text(0.3, 0.95, \"Exact result\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "1445d099",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.9202473958333336"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "uncut_expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75f48e6a-c7e4-46f3-9d39-a7a877427a04",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti contenuti:\n",
        "\n",
        "  * [Condizioni al contorno periodiche con interruzione del circuito](/docs/tutorials/periodic-boundary-conditions-with-circuit-cutting)\n",
        "  * [Taglio a circuito chiuso per la riduzione della profondità](/docs/tutorials/depth-reduction-with-circuit-cutting)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "23cd3042",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## Riferimenti\n",
        "\n",
        "\\[1] Peng, T., Harrow, A. W., Ozols, M. e Wu, X. (2020). Simulazione di grandi circuiti quantistici su un piccolo computer quantistico. Physical Review Letters, 125(15), 150504.\n",
        "\n",
        "\\[2] Tang, W, Tomesh, T., Suchara, M., Larson, J. e Martonosi, M. (2021, aprile). Cutqc: utilizzo di piccoli computer quantistici per la valutazione di grandi circuiti quantistici. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).\n",
        "\n",
        "\\[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). Taglio di circuiti quantistici con tomografia a massima verosimiglianza. npj Quantum Information, 7(1), 64.\n",
        "\n",
        "\\[4] Majumdar, R., & Wood, C. J. (2022). Taglio di circuiti quantistici mitigato dagli errori. arXiv preprint arXiv:2211.13431.\n",
        "\n",
        "\\[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V. e Simmhan, Y. (2023). Parallelizzazione di carichi di lavoro quantistico-classici: Profilazione dell'impatto delle tecniche di suddivisione. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.\n",
        "\n",
        "\\[6] Bhoumik, D., Majumdar, R., Saha, A. e Sur-Kolay, S. (2023). Scheduling distribuito di circuiti quantistici con rumore e ottimizzazione del tempo. arXiv preprint arXiv:2309.06005.\n",
        "\n",
        "\\[7] Majumdar, R. (2024). Riduzione efficiente delle risorse e del rumore nei circuiti di calcolo quantistico discreto (Tesi di dottorato, Indian Statistical Institute - Calcutta). [https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y](https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y)\n",
        "\n"
      ]
    },
    {
      "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"
    },
    "hours": 1.5,
    "qpuSeconds": 22
  },
  "nbformat": 4,
  "nbformat_minor": 5
}