{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "3445b0b5-7f26-4610-8e1b-5112df1fccb0",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Combinare le opzioni di mitigazione degli errori con la primitiva Stimatore\"\n",
        "description: \"Combina le opzioni di mitigazione degli errori per esperimenti su scala industriale utilizzando le QPU 100Q+ IBM Quantum e la primitiva Estimator di IBM Quantum.\"\n",
        "---\n",
        "\n",
        "<span id=\"combine-error-mitigation-options-with-the-estimator-primitive\" />\n",
        "\n",
        "# Combinare le opzioni di mitigazione degli errori con la primitiva Stimatore\n",
        "\n",
        "*Stima della durata: 7 minuti su un processore Heron r2 (NOTA: si tratta solo di una stima. (La durata potrebbe variare.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "39ba4396-9111-4421-9371-542f067bc816",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "Consigliamo agli utenti di acquisire familiarità con i seguenti argomenti prima di seguire questo tutorial:\n",
        "\n",
        "* I fondamenti del disaccoppiamento dinamico, della mitigazione degli errori di misurazione, del gate twirling e dell'estrapolazione a rumore zero, come descritto nella presente [guida](/docs/guides/error-mitigation-and-suppression-techniques).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b27cb482-b77a-4953-a0a4-39df33896d12",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "Dopo aver seguito questo tutorial, gli utenti dovrebbero aver compreso:\n",
        "\n",
        "* In che modo le suddette tecniche di mitigazione degli errori vengono implementate in modo selettivo a livello hardware.\n",
        "* Come si confrontano in termini di capacità di attenuare il rumore dell'hardware.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "018313d1-d959-4011-81df-59bf7c157470",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "Questo tutorial illustra le opzioni di soppressione e mitigazione degli errori disponibili con la primitiva \" IBM Quantum \" di Estimator. Questo tutorial illustra come implementare singolarmente ciascuno dei seguenti metodi:\n",
        "\n",
        "* Disaccoppiamento dinamico\n",
        "* Attenuazione dell'errore di misura\n",
        "* Porta girevole\n",
        "* Estrapolazione a rumore zero (ZNE)\n",
        "\n",
        "Si noti che, invece di implementare queste tecniche singolarmente, è possibile implementarle utilizzando un [livello di resilienza](/docs/guides/estimator-noise-management), che `resilience_level` può assumere i valori 0, 1, 2:\n",
        "\n",
        "* 0: Non è stata implementata alcuna misura di mitigazione.\n",
        "* 1: È stata implementata la riduzione degli errori di misurazione.\n",
        "* 2: Sono state implementate le funzioni di rotazione del cancello, la riduzione degli errori di misurazione e lo ZNE.\n",
        "\n",
        "In questo tutorial, realizzerai un circuito e un osservabile e invierai dei lavori utilizzando la primitiva Estimator con diverse combinazioni di impostazioni di mitigazione dell'errore. Successivamente, rappresenterai graficamente i risultati per osservare gli effetti delle diverse impostazioni. La maggior parte del tutorial utilizza un circuito da 10 qubit per facilitare la comprensione visiva; alla fine, estenderai il flusso di lavoro a 50 qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0f3d3378-8dac-489e-bf6d-292e693ec145",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## Requisiti\n",
        "\n",
        "Prima di iniziare questa procedura guidata, assicurati di aver installato quanto segue:\n",
        "\n",
        "* Qiskit SDK v2.1 o versioni successive, con supporto [alla visualizzazione](/docs/api/qiskit/visualization)\n",
        "* Qiskit Runtime v0.40 o più tardi (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "069658fa-fa45-48b8-ab69-2610228bf6e4",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "95a72452-e067-4ee5-bcd9-e3bfcf0de208",
      "metadata": {},
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "from qiskit.circuit.library import efficient_su2, unitary_overlap\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Batch, EstimatorV2 as Estimator"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d15c5fde-5046-4d86-86d8-72d1ed748c4e",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Esempio di simulatore su piccola scala\n",
        "\n",
        "Salteremo questo passaggio poiché la mitigazione degli errori di runtime non è supportata sui simulatori.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b2be0539-4f60-46e8-be1f-8869dae30888",
      "metadata": {},
      "source": [
        "<span id=\"hardware-example\" />\n",
        "\n",
        "## Esempio di hardware\n",
        "\n",
        "<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",
        "Questa procedura dettagliata presuppone che il problema classico sia già stato mappato su quello quantistico. Inizia costruendo un circuito e un osservabile da misurare. Sebbene le tecniche qui utilizzate siano applicabili a molti tipi diversi di circuiti, per semplicità questa guida utilizza il [`efficient_su2`](/docs/api/qiskit/qiskit.circuit.library.efficient_su2) circuito incluso nella libreria di circuiti Qiskit.\n",
        "\n",
        "`efficient_su2` è un circuito quantistico parametrizzato progettato per essere eseguito in modo efficiente su hardware quantistico con connettività limitata dei qubit, pur rimanendo sufficientemente espressivo da risolvere problemi in ambiti applicativi quali l'ottimizzazione e la chimica. È costruito alternando strati di porte a singolo qubit parametrizzate con uno strato contenente uno schema fisso di porte a due qubit, per un numero prestabilito di ripetizioni. La configurazione delle porte a due qubit può essere specificata dall'utente. In questo caso è possibile utilizzare lo schema `pairwise` integrato, poiché riduce al minimo la profondità del circuito raggruppando le porte a due qubit nel modo più compatto possibile. Questo schema può essere realizzato utilizzando esclusivamente una connettività lineare dei qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "24abd7ba-bbb8-443b-9e81-866795d39a6c",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/24abd7ba-bbb8-443b-9e81-866795d39a6c-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "n_qubits = 10\n",
        "reps = 1\n",
        "\n",
        "circuit = efficient_su2(n_qubits, entanglement=\"pairwise\", reps=reps)\n",
        "\n",
        "circuit.decompose().draw(\"mpl\", scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "34fec53b-a8b2-461b-a20d-e8ad54b992fa",
      "metadata": {},
      "source": [
        "Come grandezza osservabile, prendiamo l'operatore di Pauli $Z$ che agisce sull'ultimo qubit, $Z I \\cdots I$.\n",
        "Si noti che il fatto che l'ultimo qubit corrisponda al primo elemento di questa stringa è dovuto all'uso della notazione little-endian da parte di Qiskit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "9500ba7b-4542-465f-a4e1-60165d6b6d1e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Z on the last qubit (index -1) with coefficient 1.0\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [-1], 1.0)], num_qubits=n_qubits\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46eff743-de9f-4f55-9c47-02c6d6115b80",
      "metadata": {},
      "source": [
        "A questo punto, potresti procedere con l'esecuzione del circuito e misurare la grandezza osservabile. Tuttavia, è anche necessario confrontare l'output del dispositivo quantistico con la risposta corretta, ovvero il valore teorico dell'osservabile, se il circuito fosse stato eseguito senza errori. Per i circuiti quantistici di piccole dimensioni è possibile calcolare questo valore simulando il circuito su un computer classico, ma ciò non è possibile per i circuiti più grandi, su scala industriale. È possibile ovviare a questo problema utilizzando la tecnica del \"circuito speculare\" (nota anche come \"compute-uncompute\"), utile per valutare le prestazioni dei dispositivi quantistici.\n",
        "\n",
        "<span id=\"mirror-circuit\" />\n",
        "\n",
        "#### Circuito speculare\n",
        "\n",
        "Nella tecnica del circuito a specchio, si concatena il circuito con il suo circuito inverso, che si forma invertendo ogni porta del circuito in ordine inverso. Il circuito risultante implementa l'operatore di identità, che può essere banalmente simulato. Poiché la struttura del circuito originale è conservata nel circuito speculare, l'esecuzione del circuito speculare dà comunque un'idea di come il dispositivo quantistico si comporterebbe sul circuito originale.\n",
        "\n",
        "La seguente cella di codice assegna parametri casuali al circuito, quindi costruisce il circuito speculare utilizzando la [`unitary_overlap`](/docs/api/qiskit/qiskit.circuit.library.unitary_overlap) classe. Prima di duplicare il circuito, aggiungi un'istruzione [di barriera](/docs/api/qiskit/circuit#qiskit.circuit.Barrier) per impedire al transpiler di unire le due parti del circuito situate ai lati della barriera, evitando così che il circuito transpilato risulti privo di porte logiche.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "4dbde811-1ba9-47a8-85a0-dcaff054ed60",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/4dbde811-1ba9-47a8-85a0-dcaff054ed60-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Generate random parameters\n",
        "rng = np.random.default_rng(1234)\n",
        "params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)\n",
        "\n",
        "# Assign the parameters to the circuit\n",
        "assigned_circuit = circuit.assign_parameters(params)\n",
        "\n",
        "# Add a barrier to prevent circuit optimization of mirrored operators\n",
        "assigned_circuit.barrier()\n",
        "\n",
        "# Construct mirror circuit\n",
        "mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)\n",
        "\n",
        "mirror_circuit.decompose().draw(\"mpl\", scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a37c97de-c942-4419-9409-5bcaef9c6876",
      "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",
        "È necessario ottimizzare il circuito prima di eseguirlo sull'hardware. Questo processo prevede alcune fasi:\n",
        "\n",
        "* Scegliete un layout di qubit che mappa i qubit virtuali del vostro circuito con i qubit fisici dell'hardware.\n",
        "* Inserire porte di scambio se necessario per instradare le interazioni tra qubit non collegati.\n",
        "* Tradurre le porte del circuito in istruzioni [ISA (Instruction Set Architecture)](/docs/guides/transpile#instruction-set-architecture) che possono essere eseguite direttamente sull'hardware.\n",
        "* Eseguire ottimizzazioni del circuito per ridurre al minimo la profondità del circuito e il numero di porte.\n",
        "\n",
        "Il transpiler integrato in Qiskit può eseguire tutti questi passaggi al posto tuo. Poiché questo esempio utilizza un circuito efficiente dal punto di vista hardware, il transpiler dovrebbe essere in grado di scegliere una disposizione dei qubit che non richieda l'inserimento di porte di scambio per il routing delle interazioni.\n",
        "\n",
        "Prima di ottimizzare il circuito, devi scegliere il dispositivo hardware da utilizzare. La seguente cella di codice richiede il dispositivo meno occupato con almeno 127 qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2c3ea768-0596-4db1-9b51-3ff78ec65818",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "aeaf076c-1c0a-4ec4-af66-49eff621589d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "print(backend)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "46280e59-8c9f-4dd9-a365-6a230ceccf42",
      "metadata": {},
      "source": [
        "È possibile traspilare il circuito sul backend desiderato creando un gestore di passaggi ed eseguendolo sul circuito. Un modo semplice per creare un gestore di pass è utilizzare la [`generate_preset_pass_manager`](/docs/api/qiskit/qiskit.transpiler.generate_preset_pass_manager) funzione. Per una spiegazione più dettagliata della transpilazione con [i gestori di passaggi](/docs/guides/transpile-with-pass-managers), consultare la sezione \"Transpile con i gestori di passaggi\".\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "027e829a-44d3-455e-b2bf-8ce0d7e26b9b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/027e829a-44d3-455e-b2bf-8ce0d7e26b9b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pass_manager = generate_preset_pass_manager(\n",
        "    optimization_level=3, backend=backend, seed_transpiler=1234\n",
        ")\n",
        "isa_circuit = pass_manager.run(mirror_circuit)\n",
        "\n",
        "isa_circuit.draw(\"mpl\", idle_wires=False, scale=0.7, fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1865671e-2c7a-44dd-8938-fe4637bb5993",
      "metadata": {},
      "source": [
        "Il circuito transpilato ora contiene solo istruzioni ISA. Tutte le porte sono state scomposte in porte di tipo \" $\\sqrt{X}$ \", rotazioni \" $R_z$ \" e [porte CZ](/docs/api/qiskit/qiskit.circuit.library.CZGate).\n",
        "\n",
        "Il processo di transpilazione ha mappato i qubit virtuali del circuito in qubit fisici sull'hardware. Le informazioni sulla disposizione dei qubit sono memorizzate nell'attributo `layout` del circuito transpilato. L'osservabile è stato definito anche in termini di qubit virtuali, quindi è necessario applicare questa disposizione all'osservabile, cosa che si può fare con il metodo [`apply_layout`](/docs/api/qiskit/qiskit.quantum_info.SparsePauliOp#apply_layout) di `SparsePauliOp`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "12338588-a2d0-4b9c-9e49-e25ec8617b93",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original observable:\n",
            "SparsePauliOp(['ZIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n",
            "\n",
            "Observable with layout applied:\n",
            "SparsePauliOp(['IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],\n",
            "              coeffs=[1.+0.j])\n"
          ]
        }
      ],
      "source": [
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "print(\"Original observable:\")\n",
        "print(observable)\n",
        "print()\n",
        "print(\"Observable with layout applied:\")\n",
        "print(isa_observable)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "439d4edf-d1ad-49f3-b206-320958be4f82",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: eseguire utilizzando Qiskit primitives\n",
        "\n",
        "Ora si è pronti a eseguire il circuito utilizzando la primitiva Estimator.\n",
        "\n",
        "Qui si invieranno cinque lavori distinti, iniziando senza alcuna soppressione o mitigazione degli errori e attivando successivamente le varie opzioni di soppressione e mitigazione degli errori disponibili nel servizio di calcolo di IBM Quantum. Per informazioni sulle opzioni, consultare le pagine seguenti:\n",
        "\n",
        "* [Panoramica di tutte le opzioni](/docs/api/qiskit-ibm-runtime/options)\n",
        "* [Disaccoppiamento dinamico](/docs/api/qiskit-ibm-runtime/options-dynamical-decoupling-options)\n",
        "* [Resilienza, compresa la mitigazione dell'errore di misura e l'estrapolazione a rumore zero (ZNE)](/docs/api/qiskit-ibm-runtime/options-resilience-options-v2)\n",
        "* [Twirling](/docs/api/qiskit-ibm-runtime/options-twirling-options)\n",
        "\n",
        "Poiché questi processi possono essere eseguiti indipendentemente l'uno dall'altro, è possibile utilizzare [la modalità batch](/docs/guides/run-jobs-batch) per consentire a Quantum Compute di ottimizzare i tempi di esecuzione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1114d15b-1408-4f4c-a20f-60f0da722325",
      "metadata": {},
      "outputs": [],
      "source": [
        "pub = (isa_circuit, isa_observable)\n",
        "\n",
        "jobs = []\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    estimator = Estimator(mode=batch)\n",
        "    estimator.options.environment.job_tags = [\n",
        "        \"TUT_CEM_SS\"\n",
        "    ]  # add tag for this small scale job\n",
        "    # Set number of shots\n",
        "    estimator.options.default_shots = 100_000\n",
        "    # Disable runtime compilation and error mitigation\n",
        "    estimator.options.resilience_level = 0\n",
        "\n",
        "    # Run job with no error mitigation\n",
        "    job0 = estimator.run([pub])\n",
        "    jobs.append(job0)\n",
        "\n",
        "    # Add dynamical decoupling (DD)\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "    job1 = estimator.run([pub])\n",
        "    jobs.append(job1)\n",
        "\n",
        "    # Add readout error mitigation (DD + TREX)\n",
        "    estimator.options.resilience.measure_mitigation = True\n",
        "    job2 = estimator.run([pub])\n",
        "    jobs.append(job2)\n",
        "\n",
        "    # Add gate twirling (DD + TREX + Gate Twirling)\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "    job3 = estimator.run([pub])\n",
        "    jobs.append(job3)\n",
        "\n",
        "    # Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)\n",
        "    estimator.options.resilience.zne_mitigation = True\n",
        "    estimator.options.resilience.zne.noise_factors = (1, 3, 5)\n",
        "    estimator.options.resilience.zne.extrapolator = (\"exponential\", \"linear\")\n",
        "    job4 = estimator.run([pub])\n",
        "    jobs.append(job4)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "919517c8-6f42-48d9-a976-53c715c00a4b",
      "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",
        "Infine, è possibile analizzare i dati. Qui si recuperano i risultati del lavoro, si estraggono i valori di aspettativa misurati e si tracciano i valori, comprese le barre di errore di una deviazione standard.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "eef38976-0ca2-429a-b2dc-41aac69605f7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/eef38976-0ca2-429a-b2dc-41aac69605f7-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Retrieve the job results\n",
        "results = [job.result() for job in jobs]\n",
        "\n",
        "# Unpack the PUB results (there's only one PUB result in each job result)\n",
        "pub_results = [result[0] for result in results]\n",
        "\n",
        "# Unpack the expectation values and standard errors\n",
        "expectation_vals = np.array(\n",
        "    [float(pub_result.data.evs) for pub_result in pub_results]\n",
        ")\n",
        "standard_errors = np.array(\n",
        "    [float(pub_result.data.stds) for pub_result in pub_results]\n",
        ")\n",
        "\n",
        "# Plot the expectation values\n",
        "fig, ax = plt.subplots()\n",
        "labels = [\"No mitigation\", \"+ DD\", \"+ TREX\", \"+ Twirling\", \"+ ZNE\"]\n",
        "ax.bar(\n",
        "    range(len(labels)),\n",
        "    expectation_vals,\n",
        "    yerr=standard_errors,\n",
        "    label=\"experiment\",\n",
        ")\n",
        "ax.axhline(y=1.0, color=\"gray\", linestyle=\"--\", label=\"ideal\")\n",
        "ax.set_xticks(range(len(labels)))\n",
        "ax.set_xticklabels(labels)\n",
        "ax.set_ylabel(\"Expectation value\")\n",
        "ax.legend(loc=\"upper left\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b400eae8-f38c-4566-82fe-0ccc5ee3f05d",
      "metadata": {},
      "source": [
        "A questa scala ridotta, è difficile vedere l'effetto della maggior parte delle tecniche di attenuazione degli errori, ma l'estrapolazione a rumore zero offre un miglioramento notevole. Tuttavia, si noti che questo miglioramento non è gratuito, perché il risultato ZNE presenta anche una barra di errore più grande.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "958a5d23-ea09-4750-827c-e3bf6c3fa3f4",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\n",
        "\n",
        "Quando si sviluppa un esperimento, è utile iniziare con un circuito di piccole dimensioni per facilitare la visualizzazione e la simulazione. Ora che avete sviluppato e testato il nostro flusso di lavoro su un circuito a 10 qubit, potete scalarlo fino a 50 qubit. La seguente cella di codice ripete tutti i passaggi di questa procedura dettagliata, ma ora li applica a un circuito a 50 qubit.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "d7d8408b-faf1-4eda-ab9c-bdeaab01ff53",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/combine-error-mitigation-techniques/extracted-outputs/d7d8408b-faf1-4eda-ab9c-bdeaab01ff53-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "n_qubits = 50\n",
        "reps = 1\n",
        "\n",
        "# Construct circuit and observable\n",
        "circuit = efficient_su2(n_qubits, entanglement=\"pairwise\", reps=reps)\n",
        "observable = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [-1], 1.0)], num_qubits=n_qubits\n",
        ")\n",
        "\n",
        "# Assign parameters to circuit\n",
        "params = rng.uniform(-np.pi, np.pi, size=circuit.num_parameters)\n",
        "assigned_circuit = circuit.assign_parameters(params)\n",
        "assigned_circuit.barrier()\n",
        "\n",
        "# Construct mirror circuit\n",
        "mirror_circuit = unitary_overlap(assigned_circuit, assigned_circuit)\n",
        "\n",
        "# Transpile circuit and observable\n",
        "isa_circuit = pass_manager.run(mirror_circuit)\n",
        "isa_observable = observable.apply_layout(isa_circuit.layout)\n",
        "\n",
        "# Run jobs\n",
        "pub = (isa_circuit, isa_observable)\n",
        "\n",
        "jobs = []\n",
        "\n",
        "with Batch(backend=backend) as batch:\n",
        "    estimator = Estimator(mode=batch)\n",
        "    estimator.options.environment.job_tags = [\n",
        "        \"TUT_CEM_LS\"\n",
        "    ]  # add tag for this large scale job\n",
        "    # Set number of shots\n",
        "    estimator.options.default_shots = 100_000\n",
        "    # Disable runtime compilation and error mitigation\n",
        "    estimator.options.resilience_level = 0\n",
        "\n",
        "    # Run job with no error mitigation\n",
        "    job0 = estimator.run([pub])\n",
        "    jobs.append(job0)\n",
        "\n",
        "    # Add dynamical decoupling (DD)\n",
        "    estimator.options.dynamical_decoupling.enable = True\n",
        "    estimator.options.dynamical_decoupling.sequence_type = \"XpXm\"\n",
        "    job1 = estimator.run([pub])\n",
        "    jobs.append(job1)\n",
        "\n",
        "    # Add readout error mitigation (DD + TREX)\n",
        "    estimator.options.resilience.measure_mitigation = True\n",
        "    job2 = estimator.run([pub])\n",
        "    jobs.append(job2)\n",
        "\n",
        "    # Add gate twirling (DD + TREX + Gate Twirling)\n",
        "    estimator.options.twirling.enable_gates = True\n",
        "    estimator.options.twirling.num_randomizations = \"auto\"\n",
        "    job3 = estimator.run([pub])\n",
        "    jobs.append(job3)\n",
        "\n",
        "    # Add zero-noise extrapolation (DD + TREX + Gate Twirling + ZNE)\n",
        "    estimator.options.resilience.zne_mitigation = True\n",
        "    estimator.options.resilience.zne.noise_factors = (1, 3, 5)\n",
        "    estimator.options.resilience.zne.extrapolator = (\"exponential\", \"linear\")\n",
        "    job4 = estimator.run([pub])\n",
        "    jobs.append(job4)\n",
        "\n",
        "# Retrieve the job results\n",
        "results = [job.result() for job in jobs]\n",
        "\n",
        "# Unpack the PUB results (there's only one PUB result in each job result)\n",
        "pub_results = [result[0] for result in results]\n",
        "\n",
        "# Unpack the expectation values and standard errors\n",
        "expectation_vals = np.array(\n",
        "    [float(pub_result.data.evs) for pub_result in pub_results]\n",
        ")\n",
        "standard_errors = np.array(\n",
        "    [float(pub_result.data.stds) for pub_result in pub_results]\n",
        ")\n",
        "\n",
        "# Plot the expectation values\n",
        "fig, ax = plt.subplots()\n",
        "labels = [\"No mitigation\", \"+ DD\", \"+ TREX\", \"+ Twirling\", \"+ ZNE\"]\n",
        "ax.bar(\n",
        "    range(len(labels)),\n",
        "    expectation_vals,\n",
        "    yerr=standard_errors,\n",
        "    label=\"experiment\",\n",
        ")\n",
        "ax.axhline(y=1.0, color=\"gray\", linestyle=\"--\", label=\"ideal\")\n",
        "ax.set_xticks(range(len(labels)))\n",
        "ax.set_xticklabels(labels)\n",
        "ax.set_ylabel(\"Expectation value\")\n",
        "ax.legend(loc=\"upper left\")\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32d9b036-ea0a-4484-b6ee-036ff732026b",
      "metadata": {},
      "source": [
        "Confrontando i risultati a 50-qubit con quelli a 10-qubit di prima, si può notare quanto segue (i risultati potrebbero differire da un'esecuzione all'altra):\n",
        "\n",
        "* Tutti gli esperimenti danno risultati più vicini al valore ideale e tutte le barre di errore sono più strette.\n",
        "* L'introduzione del disaccoppiamento dinamico potrebbe aver peggiorato le prestazioni rispetto al caso senza misure di mitigazione. Non c'è da stupirsi, dato che il circuito è molto fitto. Il disaccoppiamento dinamico è utile soprattutto quando nel circuito si verificano lunghi intervalli in cui i qubit rimangono inattivi senza che venga loro applicata alcuna porta logica. Quando tali intervalli non sono presenti, il disaccoppiamento dinamico non è efficace e può addirittura peggiorare le prestazioni a causa di errori negli stessi impulsi di disaccoppiamento dinamico. Il circuito da 10 qubit potrebbe essere stato troppo piccolo per permetterci di osservare questo effetto.\n",
        "* Con l'estrapolazione a rumore zero, il risultato è molto vicino al valore ideale. Questo dimostra la potenza di ZNE.\n",
        "\n",
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  Se questo lavoro ti è sembrato interessante, potrebbero interessarti i seguenti materiali su alcune tecniche aggiuntive di mitigazione e soppressione degli errori che non sono state menzionate in questo tutorial:\n",
        "\n",
        "  * [Cancellazione probabilistica degli errori (PEC)](/docs/guides/error-mitigation-and-suppression-techniques#probabilistic-error-cancellation-pec)\n",
        "  * [Riduzione](https://qiskit.github.io/qiskit-addon-mthree/) [degli errori](/docs/tutorials/readout-error-mitigation-sampler/) di misurazione senza matrici ( M3 ) (vedi anche il tutorial \"Riduzione degli errori di lettura per la primitiva Sampler utilizzando M3 \")\n",
        "  * [Coni di luce ombreggiati](https://qiskit.github.io/qiskit-addon-slc/)\n",
        "  * [Assorbimento acustico propagato (PNA)](https://qiskit.github.io/qiskit-addon-pna/)\n",
        "</Admonition>\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,
    "qpuSeconds": 420
  },
  "nbformat": 4,
  "nbformat_minor": 4
}