{
  "cells": [
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "f5d21946",
      "metadata": {
        "slideshow": {
          "slide_type": "-"
        }
      },
      "source": [
        "---\n",
        "title: \"Codici di ripetizione\"\n",
        "description: \"Questo tutorial mostra come costruire codici di ripetizione di base utilizzando circuiti dinamici dell' IBM, un esempio di correzione di errori quantistici (QEC) di base.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore pcov REPCODE creference qreference csyndrome qsyndrome qdata fontsize ytick labelsize xtick */}\n",
        "\n",
        "<span id=\"repetition-codes\" />\n",
        "\n",
        "# Codici di ripetizione\n",
        "\n",
        "Stima del tempo di *esecuzione: meno di 10 secondi su un processore Heron (NOTA: si tratta solo di una stima. (La durata potrebbe variare.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4048d4c9",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## Risultati di apprendimento\n",
        "\n",
        "* Come implementare un codice di correzione degli errori di inversione di bit utilizzando circuiti dinamici\n",
        "* Come misurare gli stabilizzatori per rilevare errori quantistici senza distruggere le informazioni codificate\n",
        "* Come valutare le prestazioni della correzione quantistica degli errori confrontando i risultati corretti con quelli non corretti\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9012c057",
      "metadata": {},
      "source": [
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## Prerequisiti\n",
        "\n",
        "* [Circuiti dinamici](/docs/guides/classical-feedforward-and-control-flow)\n",
        "* [Nozioni fondamentali sulla correzione degli errori quantistici](https://arxiv.org/abs/0905.2794)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d182acd",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## Sfondo\n",
        "\n",
        "Per consentire la correzione degli errori quantistici (QEC) in tempo reale, è necessario poter controllare dinamicamente il flusso del programma quantistico durante l'esecuzione, in modo che le porte quantistiche possano essere condizionate dai risultati delle misure. Questa esercitazione esegue il codice bit-flip, che è una forma molto semplice di QEC. Dimostra un circuito quantistico dinamico in grado di proteggere un qubit codificato da un singolo errore di bit-flip e valuta le prestazioni del codice bit-flip.\n",
        "\n",
        "È possibile sfruttare qubit ancilla aggiuntivi e l'entanglement per misurare *stabilizzatori* che non trasformano le informazioni quantistiche codificate, informando comunque su alcune classi di errori che potrebbero essersi verificati. Un codice stabilizzatore quantistico codifica $k$ qubit logici in $n$ qubit fisici. I codici stabilizzatori si concentrano criticamente sulla correzione di un insieme di errori discreti con il supporto del gruppo di Pauli $\\Pi^n$.\n",
        "\n",
        "In questo tutorial illustriamo il codice “bit-flip” attraverso un semplice esperimento sulla memoria quantistica. Prepareremo il qubit codificato nello stato logico $|\\bar{1}\\rangle \\equiv |111\\rangle$ e quindi eseguiremo più cicli di inattività (per generare errori), seguiti dal rilevamento e dalla correzione degli errori. Quantifichiamo quindi la probabilità di un errore logico in funzione del numero di cicli (tempo), dove la probabilità di errore logico è la probabilità di trovare i qubit in uno stato che non ripristini l’ $|\\bar{1}\\rangle$ e dopo la misurazione finale (in particolare, gli stati corrispondenti a un errore sono $|000\\rangle$, $|001\\rangle$, $|010\\rangle$ e $|100\\rangle$ ).\n",
        "\n",
        "Confronteremo il tasso di errore in funzione del tempo con i tassi di errore dei singoli qubit non codificati, nonché con l’uso del codice di ripetizione, in cui gli errori vengono rilevati e corretti solo dopo la misurazione finale, ma non dinamicamente durante il funzionamento del circuito.\n",
        "\n",
        "Nota: il codice di ripetizione consente solo la correzione degli errori di inversione di bit e, pertanto, non è un codice di correzione degli errori completo. Tuttavia, proprio grazie alla sua semplicità, rappresenta un buon punto di partenza per illustrare come implementare la correzione degli errori su un computer quantistico. L'esperimento sulla memoria riportato di seguito verifica solo un unico tipo di errore (un \" $|\\bar{1}\\rangle$ \" che si riduce a \" $|\\bar{0}\\rangle$ \") e, tecnicamente, dimostra solo la protezione delle informazioni classiche.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "88672bd6",
      "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.40 o più tardi (`pip install qiskit-ibm-runtime`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "14c29e8b",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## Configura\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "71cb35d9",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import seaborn as sns\n",
        "import matplotlib.pyplot as plt\n",
        "from scipy.optimize import curve_fit\n",
        "\n",
        "sns.set()\n",
        "plt.rc(\"xtick\", labelsize=20)\n",
        "plt.rc(\"ytick\", labelsize=20)\n",
        "plt.rc(\"lines\", linewidth=3)\n",
        "plt.rc(\"font\", size=20)\n",
        "plt.rc(\"legend\", fontsize=\"large\")\n",
        "plt.rc(\"axes\", labelsize=20)\n",
        "plt.rcParams[\"figure.figsize\"] = 15, 6\n",
        "plt.rcParams[\"legend.title_fontsize\"] = 25"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1b9fd8ad",
      "metadata": {
        "slideshow": {
          "slide_type": "-"
        }
      },
      "outputs": [],
      "source": [
        "# Qiskit imports\n",
        "from qiskit import (\n",
        "    QuantumCircuit,\n",
        "    QuantumRegister,\n",
        "    ClassicalRegister,\n",
        ")\n",
        "\n",
        "# qiskit-ibm-runtime\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler\n",
        "from qiskit_ibm_runtime.circuit import MidCircuitMeasure\n",
        "\n",
        "service = QiskitRuntimeService()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "eb4ccf5d",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## Esempio di simulatore su piccola scala\n",
        "\n",
        "Tralasceremo questo passaggio poiché l'obiettivo di questo esperimento è misurare la durata di vita di un qubit logico in presenza di rumore hardware reale, come lo smorzamento di ampiezza durante i ritardi di inattività; un simulatore privo di rumore non mostrerebbe errori da rilevare e correggere.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8af23cd0",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## Esempio di hardware su larga scala\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"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "02427c7e",
      "metadata": {},
      "source": [
        "<span id=\"choose-a-backend\" />\n",
        "\n",
        "#### Scegli un backend\n",
        "\n",
        "Per individuare eventuali errori durante l'esecuzione del circuito, è necessario scegliere un backend che abbia accesso al metodo `MidCircuitMeasure` (vedere la [documentazione](/docs/guides/execute-dynamic-circuits#mid-circuit-measurements) ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1512c805",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[<IBMBackend('ibm_pittsburgh')>,\n",
              " <IBMBackend('ibm_boston')>,\n",
              " <IBMBackend('ibm_kingston')>]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# You can see all backends that support mid-circuit measurements by running the following code.\n",
        "service.backends(filters=lambda b: \"measure_2\" in b.supported_instructions)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b588703a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "ibm_boston\n"
          ]
        }
      ],
      "source": [
        "# Choose the least busy backend that supports mid-circuit measurements (`measure_2`).\n",
        "\n",
        "backend = service.least_busy(\n",
        "    filters=lambda b: \"measure_2\" in b.supported_instructions,\n",
        "    operational=True,\n",
        "    simulator=False,\n",
        "    dynamic_circuits=True,\n",
        ")\n",
        "# backend = service.backend(backend_name) # alternatively, you could choose a specific backend\n",
        "print(backend.name)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "09b920c2",
      "metadata": {},
      "source": [
        "<span id=\"build-a-sequence-of-bit-flip-stabilizer-circuits-implementing-multiple-rounds-of-error-detection-and-correction\" />\n",
        "\n",
        "#### Realizzare una sequenza di circuiti stabilizzatori con inversione di bit che implementino più cicli di rilevamento e correzione degli errori\n",
        "\n",
        "Il codice bit-flip è uno degli esempi più semplici di codice stabilizzatore. Protegge lo stato da un singolo errore di inversione di bit (X) su uno qualsiasi dei qubit di codifica. Consideriamo l’azione dell’errore di inversione di bit $X$, che mappa $|0\\rangle \\rightarrow |1\\rangle$ e $|1\\rangle \\rightarrow |0\\rangle$ su uno qualsiasi dei nostri qubit; in tal caso, si ottiene $\\epsilon = \\{E_0, E_1, E_2 \\} = \\{IIX, IXI, XII\\}$. Il codice richiede cinque qubit: tre vengono utilizzati per codificare lo stato protetto (i “qubit di dati”), mentre i restanti due fungono da ancilla per la misurazione degli stabilizzatori.\n",
        "\n",
        "Di seguito realizzerete dei circuiti che (1) preparano i qubit di dati nello stato logico $|\\bar{1} \\rangle$, poi (2) eseguono più cicli di un ritardo $5 \\mu s$ seguiti dalla correzione degli errori (compreso il reset dei qubit di sindrome) e (3) leggono lo stato dei qubit di dati.\n",
        "\n",
        "Verificheremo inoltre la durata dello stato “ $|1\\rangle$ ” senza ricorrere alla correzione degli errori, includendo tre qubit di riferimento che prepareremo nello stato “ $|1\\rangle$ ”, lasceremo inattivi e poi leggeremo.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 194,
      "id": "d36817e7",
      "metadata": {},
      "outputs": [],
      "source": [
        "def build_qc(\n",
        "    qreg_data,\n",
        "    qreg_syndrome,\n",
        "    creg_data,\n",
        "    creg_syndrome,\n",
        "    qreg_ref=None,\n",
        "    creg_ref=None,\n",
        "):\n",
        "    \"\"\"Build a typical error correction circuit\"\"\"\n",
        "    if qreg_ref:\n",
        "        return QuantumCircuit(\n",
        "            qreg_data,\n",
        "            qreg_syndrome,\n",
        "            creg_data,\n",
        "            creg_syndrome,\n",
        "            qreg_ref,\n",
        "            creg_ref,\n",
        "        )\n",
        "    else:\n",
        "        return QuantumCircuit(\n",
        "            qreg_data, qreg_syndrome, creg_data, creg_syndrome\n",
        "        )\n",
        "\n",
        "\n",
        "def encode_bit_flip(circuit, qreg_data, qreg_ref=None) -> QuantumCircuit:\n",
        "    \"\"\"Encode bit-flip. This is done by simply adding a cx\"\"\"\n",
        "\n",
        "    for q in qreg_data:\n",
        "        circuit.x(q)\n",
        "\n",
        "    if qreg_ref:\n",
        "        for q in qreg_ref:\n",
        "            circuit.x(q)\n",
        "\n",
        "    circuit.barrier()\n",
        "\n",
        "    return circuit\n",
        "\n",
        "\n",
        "def measure_syndrome_bit(\n",
        "    circuit, qreg_data, qreg_syndrome, creg_syndrome, qreg_ref=None\n",
        "):\n",
        "    \"\"\"\n",
        "    Measure the syndrome by measuring the parity.\n",
        "    We reset our ancilla qubits after measuring the stabilizer\n",
        "    so we can reuse them for repeated stabilizer measurements.\n",
        "    Because we have already observed the state of the qubit,\n",
        "    we can write the conditional reset protocol directly to\n",
        "    avoid another round of qubit measurement if we used\n",
        "    the `reset` instruction.\n",
        "    \"\"\"\n",
        "    circuit.cx(qreg_data[0], qreg_syndrome[0])\n",
        "    circuit.cx(qreg_data[1], qreg_syndrome[0])\n",
        "    circuit.cx(qreg_data[0], qreg_syndrome[1])\n",
        "    circuit.cx(qreg_data[2], qreg_syndrome[1])\n",
        "    circuit.barrier()\n",
        "\n",
        "    for q_measure, c_measure in zip(qreg_syndrome, creg_syndrome):\n",
        "        circuit.append(MidCircuitMeasure(), [q_measure], [c_measure])\n",
        "\n",
        "    with circuit.if_test((creg_syndrome[0], 1)):\n",
        "        circuit.x(qreg_syndrome[0])\n",
        "    with circuit.if_test((creg_syndrome[1], 1)):\n",
        "        circuit.x(qreg_syndrome[1])\n",
        "\n",
        "    circuit.barrier()\n",
        "    return circuit\n",
        "\n",
        "\n",
        "def apply_correction_bit(circuit, qreg_data, creg_syndrome):\n",
        "    \"\"\"We can detect where an error occurred and correct our state\"\"\"\n",
        "    with circuit.if_test((creg_syndrome, 3)):\n",
        "        circuit.x(qreg_data[0])\n",
        "    with circuit.if_test((creg_syndrome, 1)):\n",
        "        circuit.x(qreg_data[1])\n",
        "    with circuit.if_test((creg_syndrome, 2)):\n",
        "        circuit.x(qreg_data[2])\n",
        "    circuit.barrier()\n",
        "    return circuit\n",
        "\n",
        "\n",
        "def apply_final_readout(\n",
        "    circuit, qreg_data, creg_data, qreg_ref=None, creg_ref=None\n",
        "):\n",
        "    \"\"\"Read out the final measurements\"\"\"\n",
        "\n",
        "    circuit.barrier()\n",
        "    if qreg_ref:\n",
        "        circuit.measure(qreg_ref, creg_ref)\n",
        "    circuit.measure(qreg_data, creg_data)\n",
        "\n",
        "    return circuit"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0e8ea0be",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/repetition-codes/extracted-outputs/0e8ea0be-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 195,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "def build_error_correction_sequence(\n",
        "    num_cycles, cycles_per_circuit, nq_ref=3, delay=None\n",
        ") -> QuantumCircuit:\n",
        "    circuits = []\n",
        "    reps = []\n",
        "\n",
        "    qreg_data = QuantumRegister(3, name=\"qdata\")\n",
        "    qreg_syndrome = QuantumRegister(2, name=\"qsyndrome\")\n",
        "    creg_data = ClassicalRegister(3, name=\"cdata\")\n",
        "    creg_syndrome = ClassicalRegister(2, name=\"csyndrome\")\n",
        "    qreg_ref = QuantumRegister(nq_ref, name=\"qreference\")\n",
        "    creg_ref = ClassicalRegister(nq_ref, name=\"creference\")\n",
        "\n",
        "    circuit = build_qc(\n",
        "        qreg_data,\n",
        "        qreg_syndrome,\n",
        "        creg_data,\n",
        "        creg_syndrome,\n",
        "        qreg_ref=qreg_ref,\n",
        "        creg_ref=creg_ref,\n",
        "    )\n",
        "    circuit = encode_bit_flip(circuit, qreg_data, qreg_ref=qreg_ref)\n",
        "\n",
        "    circuit_n = circuit.copy()\n",
        "    circuit_n = apply_final_readout(\n",
        "        circuit_n, qreg_data, creg_data, qreg_ref=qreg_ref, creg_ref=creg_ref\n",
        "    )\n",
        "    circuits.append(circuit_n)\n",
        "    reps.append(0)\n",
        "\n",
        "    for i in range(1, num_cycles + 1):\n",
        "        if delay:\n",
        "            circuit.delay(delay, unit=\"us\")\n",
        "            circuit.barrier()\n",
        "        circuit = measure_syndrome_bit(\n",
        "            circuit,\n",
        "            qreg_data,\n",
        "            qreg_syndrome,\n",
        "            creg_syndrome,\n",
        "            qreg_ref=qreg_ref,\n",
        "        )\n",
        "        circuit = apply_correction_bit(circuit, qreg_data, creg_syndrome)\n",
        "        circuit_n = circuit.copy()\n",
        "        if i % cycles_per_circuit == 0:\n",
        "            circuit_n = apply_final_readout(\n",
        "                circuit_n,\n",
        "                qreg_data,\n",
        "                creg_data,\n",
        "                qreg_ref=qreg_ref,\n",
        "                creg_ref=creg_ref,\n",
        "            )\n",
        "            circuits.append(circuit_n)\n",
        "            reps.append(i)\n",
        "\n",
        "    return circuits, np.array(reps)\n",
        "\n",
        "\n",
        "num_cycles = 40\n",
        "cycles_per_circuit = 4\n",
        "nq_ref = 3\n",
        "circuits, rep_array = build_error_correction_sequence(\n",
        "    num_cycles, cycles_per_circuit, nq_ref=3, delay=5\n",
        ")\n",
        "circuits[1].draw(output=\"mpl\", cregbundle=False, fold=50)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "609c0c47",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-the-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### Fase 2: Ottimizzare il problema per l'esecuzione su hardware quantistico\n",
        "\n",
        "Per ridurre il tempo totale di esecuzione del processo, l’ Qiskit primitives e accetta solo circuiti e osservabili conformi alle istruzioni e alla connettività supportate dal sistema di destinazione (denominati “circuiti e osservabili dell’architettura del set di istruzioni” (ISA)). [Scopri di più sulla transpilazione](/docs/guides/transpile).\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "c8ea2716",
      "metadata": {
        "slideshow": {
          "slide_type": "-"
        }
      },
      "source": [
        "<span id=\"generate-isa-circuits\" />\n",
        "\n",
        "#### Generare circuiti ISA\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cc8f818f",
      "metadata": {},
      "source": [
        "Inizieremo individuando una configurazione iniziale (ovvero una selezione di qubit fisici a cui mappare i nostri circuiti) mediante la transpilazione del circuito più lungo tra quelli a nostra disposizione, utilizzando il gestore di passaggi preimpostato con livello di ottimizzazione 3.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 196,
      "id": "67b55eef",
      "metadata": {
        "slideshow": {
          "slide_type": "-"
        }
      },
      "outputs": [],
      "source": [
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "pm = generate_preset_pass_manager(backend=backend, optimization_level=3)\n",
        "isa_circuit_ref = pm.run(circuits[-1])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 197,
      "id": "3e74193a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[56, 44, 62, 43, 63, 22, 48, 67]\n"
          ]
        }
      ],
      "source": [
        "init_layout = isa_circuit_ref.layout.initial_index_layout(\n",
        "    filter_ancillas=True\n",
        ")\n",
        "print(init_layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "eaca173c",
      "metadata": {},
      "source": [
        "Come qubit di riferimento con cui confronteremo la nostra memoria quantistica logica, sceglieremo i migliori qubit disponibili in termini di tempo di coerenza con smorzamento di ampiezza ( $T_1$ ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2aedd3cb",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "[56, 44, 62, 43, 63, 143, 131, 31]\n"
          ]
        }
      ],
      "source": [
        "# get all qubits ordered by T1\n",
        "t1_data = []\n",
        "for i in range(backend.num_qubits):\n",
        "    try:\n",
        "        t1_us = backend.properties().t1(i) * 1e6\n",
        "    except Exception:\n",
        "        t1_us = 0.0\n",
        "    t1_data.append((i, t1_us))\n",
        "\n",
        "t1_data_sorted = sorted(t1_data, key=lambda x: x[1], reverse=True)\n",
        "\n",
        "# exclude the qubits we have already mapped the error correcting code to\n",
        "t1_data_sorted = [\n",
        "    t1_data for t1_data in t1_data_sorted if t1_data[0] not in init_layout[:5]\n",
        "]\n",
        "\n",
        "# use the best qubits in terms of T1 for the reference qubits\n",
        "init_layout = init_layout[:5] + [t1_data[0] for t1_data in t1_data_sorted[:3]]\n",
        "print(init_layout)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d6a7766a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Amplitude damping decoherence times code data qubits:\n",
            "qubit 56: T1 = 322 mus\n",
            "qubit 44: T1 = 263 mus\n",
            "qubit 62: T1 = 290 mus\n",
            "\n",
            "Amplitude damping decoherence times reference qubits:\n",
            "qubit 143: T1 = 442 mus\n",
            "qubit 131: T1 = 410 mus\n",
            "qubit 31: T1 = 401 mus\n"
          ]
        }
      ],
      "source": [
        "# These are the resulting T1 times\n",
        "properties = backend.properties()\n",
        "print(\"Amplitude damping decoherence times for code data qubits:\")\n",
        "for q in init_layout[:3]:\n",
        "    t1 = properties.t1(q)\n",
        "    print(f\"qubit {q}: T1 = {t1 * 1e6:.0f} mus\")\n",
        "\n",
        "print(\"\\nAmplitude damping decoherence times for reference qubits:\")\n",
        "for q in init_layout[-3:]:\n",
        "    t1 = properties.t1(q)\n",
        "    print(f\"qubit {q}: T1 = {t1 * 1e6:.0f} mus\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ee5da865",
      "metadata": {},
      "outputs": [],
      "source": [
        "# now we transpile all circuits to this initial layout; this way each circuit is run on the same qubits and we can make a fair comparison\n",
        "pm = generate_preset_pass_manager(\n",
        "    backend=backend,\n",
        "    optimization_level=3,\n",
        "    initial_layout=init_layout,\n",
        ")\n",
        "isa_circuits = pm.run(circuits)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 180,
      "id": "67acea4f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/repetition-codes/extracted-outputs/67acea4f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 180,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "isa_circuits[1].draw(\"mpl\", cregbundle=False, fold=50)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bcd61a1f",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### Passaggio 3: Eseguire il comando utilizzando Qiskit primitives\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d53319ba",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = Sampler(mode=backend)\n",
        "\n",
        "sampler.options.environment.job_tags = [\"TUT_REPCODE\"]\n",
        "sampler.options.max_execution_time = 600  # this workload is expected to be under 10s, but it is generally a good habit to set a max execution time (here 600s = 10m)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "437397a5",
      "metadata": {},
      "outputs": [],
      "source": [
        "job = sampler.run(isa_circuits, shots=1000)\n",
        "print(job.job_id())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 201,
      "id": "b1025c42",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "'DONE'"
            ]
          },
          "execution_count": 201,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "job.status()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1b652319",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### Fase 4: Elaborazione successiva e restituzione del risultato nel formato classico desiderato\n",
        "\n",
        "Confronteremo ora i tassi di errore in funzione del tempo tra la memoria logica che utilizza il codice di ripetizione a 3 qubit, da un lato, e i singoli qubit di riferimento non codificati, dall’altro.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 202,
      "id": "ea871eb9",
      "metadata": {},
      "outputs": [],
      "source": [
        "results = job.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f27099e5",
      "metadata": {},
      "outputs": [],
      "source": [
        "def correct_counts(counts_dict):\n",
        "    \"\"\"\n",
        "    Corrects the measured logical qubit encoded in the repetition code using majority vote\n",
        "    \"\"\"\n",
        "\n",
        "    result = {\"000\": 0, \"111\": 0}\n",
        "    for bitstring, count in counts_dict.items():\n",
        "        key = \"111\" if bitstring.count(\"1\") > 1 else \"000\"\n",
        "        result[key] += count\n",
        "    return result\n",
        "\n",
        "\n",
        "accuracy = []  # logical qubit\n",
        "accuracies_ref = np.zeros(\n",
        "    (len(results), nq_ref)\n",
        ")  # accuracies on individual reference qubits\n",
        "\n",
        "for n, pub_result in enumerate(results):\n",
        "    # logical accuracy (one minus error probability) for active error correction with repetition code\n",
        "    counts = pub_result.data.cdata.get_counts()\n",
        "    shots = sum(counts.values())\n",
        "    counts_corrected = correct_counts(counts)\n",
        "    accuracy.append(counts_corrected.get(\"111\", 0) / shots)\n",
        "\n",
        "    # accuracy for individual physical reference qubits without any error correction\n",
        "    for i in range(nq_ref):\n",
        "        counts = pub_result.data.creference.slice_bits(i).get_counts()\n",
        "        accuracies_ref[n, i] = counts.get(\"1\", 0) / shots\n",
        "\n",
        "accuracy = np.array(accuracy)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "72ad282b",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Best-fit effective T1 = 1397 us\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/repetition-codes/extracted-outputs/72ad282b-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "def error_proba_t1(N, t1):\n",
        "    \"\"\"\n",
        "    Exponential fitting function for amplitude damping vs. number of cycles\n",
        "    \"\"\"\n",
        "\n",
        "    t_cycle = 7.3e-6  # approximate time per cycle = 5 mus delay + 2.3 mus for error correction\n",
        "    return 1 - np.exp(-t_cycle * N / t1)\n",
        "\n",
        "\n",
        "fig, ax = plt.subplots(1, 1, figsize=(15, 5))\n",
        "ax.plot(\n",
        "    rep_array, (1.0 - accuracy) * 100.0, \"ko-\", linewidth=3, label=\"rep code\"\n",
        ")\n",
        "\n",
        "for i in range(nq_ref):\n",
        "    accuracy_1q = accuracies_ref[:, i]\n",
        "    if i == 0:\n",
        "        ax.plot(\n",
        "            rep_array,\n",
        "            (1.0 - accuracy_1q) * 100.0,\n",
        "            \"go-\",\n",
        "            linewidth=1,\n",
        "            label=\"1q reference\",\n",
        "        )\n",
        "    else:\n",
        "        ax.plot(rep_array, (1.0 - accuracy_1q) * 100.0, \"go-\", linewidth=1)\n",
        "\n",
        "params_bf, pcov = curve_fit(\n",
        "    error_proba_t1, rep_array, 1.0 - accuracy, bounds=([0, 5e-3])\n",
        ")\n",
        "t1_bf = params_bf[0]\n",
        "print(f\"Best-fit effective T1 = {t1_bf * 1e6:.0f} us\")\n",
        "error_prob_bf = error_proba_t1(\n",
        "    rep_array, t1_bf\n",
        ")  # np.array([1 - np.exp(-t_cycle * nt/t1) for nt in rep_array])\n",
        "ax.plot(rep_array, error_prob_bf * 100.0, \"k--\", linewidth=1)\n",
        "\n",
        "ax.set_xlabel(\"error correction rounds\")\n",
        "ax.set_ylabel(\"error [%]\")\n",
        "ax.set_ylim(bottom=0)\n",
        "ax.legend(fontsize=15);"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "49c2f443",
      "metadata": {},
      "source": [
        "Si osserva chiaramente un aumento della durata dello stato \" $|\\bar{1}\\rangle$ \", anche rispetto ai migliori qubit fisici (non codificati). Tuttavia, tieni presente che questo esperimento corregge solo un tipo di errore, ovvero i bit-flip. È possibile aumentare la durata di vita del qubit logico? Potresti valutare la possibilità di ottimizzare il tempo di ritardo, aumentare la dimensione del codice di ripetizione oltre $n=3$, ecc.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "next_steps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## Passi successivi\n",
        "\n",
        "Se questo lavoro ti è sembrato interessante, potrebbero interessarti anche i seguenti materiali:\n",
        "\n",
        "<Admonition type=\"tip\" title=\"Suggerimenti\">\n",
        "  * [Corso sui fondamenti della correzione degli errori quantistici](/learning/courses/foundations-of-quantum-error-correction) - scopri di più sulla correzione degli errori quantistici\n",
        "  * [Tutorial sul rilevamento degli errori a basso overhead con codici spazio-temporali](/docs/tutorials/spacetime-codes) : scopri come utilizzare i controlli di Pauli per rilevare gli errori e post-selezionare i campioni\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "celltoolbar": "Slideshow",
    "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": 60
  },
  "nbformat": 4,
  "nbformat_minor": 5
}