{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Avvio rapido\"\n",
        "description: \"Guida rapida all'ultima versione di Qiskit Paulice\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Avvio rapido\n",
        "\n",
        "Questa guida illustra un esempio minimale funzionante del `qiskit-paulice` pacchetto. Utilizziamo [i controlli di Pauli nello spazio-tempo](https://arxiv.org/abs/2504.15725) per rilevare gli errori durante l'esecuzione di un circuito di Clifford, quindi selezioniamo a posteriori solo i campioni per i quali non è stato rilevato alcun errore. Per un flusso di lavoro completo e più dettagliato che quantifichi il miglioramento della fedeltà, consulta il [tutorial approfondito](https://github.com/Qiskit/qiskit-paulice/blob/main/docs/guides/low_overhead_error_detection_using_spacetime_codes.ipynb).\n",
        "\n",
        "**Passi del flusso di lavoro**\n",
        "\n",
        "1. Preparare gli input: un circuito di carico Clifford, i qubit target/ancilla su cui eseguire i controlli e un modello di rumore.\n",
        "2. Trova dei buoni controlli di Pauli nello spazio-tempo e aggiungili al circuito.\n",
        "3. Verificare il funzionamento del circuito contrassegnato.\n",
        "4. Selezionare solo i campioni per i quali non è stato rilevato alcun errore.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "build-circuit",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs\" />\n",
        "\n",
        "## 1. Preparare i dati di input\n",
        "\n",
        "`add_pauli_checks` richiede un **circuito** di Clifford con almeno una misurazione terminale, i **qubit di destinazione** che saranno utilizzati per implementare i controlli e un **modello di rumore** utilizzato per valutare i controlli candidati. `ibm_boston`In questa sede costruiamo un circuito Clifford casuale a profondità ridotta, lo disponiamo su una catena di qubit fisici di tipo “ 1D ”, accoppiamo ciascun qubit di payload con un qubit ancilla adiacente (in modo che un controllo non richieda porte SWAP) e deduciamo un modello approssimativo di rumore depolarizzante dai dati dei benchmark del backend.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "make-circuit",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-paulice/guides/quickstart/extracted-outputs/make-circuit-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit import QuantumCircuit\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_paulice.layout import get_check_qubits\n",
        "from qiskit_paulice.noise_models import NoiseModel\n",
        "\n",
        "# Backend and a 1D chain of physical qubits to run on\n",
        "backend = QiskitRuntimeService().backend(\"ibm_boston\")\n",
        "layout = [68, 69, 78, 89, 90, 91, 98, 111, 112, 113, 119, 133]\n",
        "\n",
        "# A shallow brickwork random Clifford payload circuit\n",
        "rng = np.random.default_rng(1764)\n",
        "circuit = QuantumCircuit(len(layout))\n",
        "circuit.h(range(circuit.num_qubits))\n",
        "for d in range(4):\n",
        "    for i in range(d % 2, circuit.num_qubits - 1, 2):\n",
        "        circuit.cz(i, i + 1)\n",
        "    for q in range(circuit.num_qubits):\n",
        "        if rng.integers(0, 2):\n",
        "            circuit.sx(q)\n",
        "        if rng.integers(0, 2):\n",
        "            circuit.s(q)\n",
        "circuit.measure_all()\n",
        "\n",
        "# Pair each payload qubit with a neighboring ancilla to host a check (no SWAPs needed)\n",
        "target_qubits, ancilla_qubits = get_check_qubits(backend.coupling_map, layout)\n",
        "\n",
        "# A rough depolarizing noise model from backend benchmark data, used to score checks\n",
        "noise_model = NoiseModel.from_backend(\n",
        "    backend, layout, uniform_gate_noise=True\n",
        ")\n",
        "\n",
        "circuit.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "add-checks-md",
      "metadata": {},
      "source": [
        "<span id=\"2-find-and-add-spacetime-pauli-checks\" />\n",
        "\n",
        "## 2. Individuare e aggiungere i controlli di Pauli nello spazio-tempo\n",
        "\n",
        "`add_pauli_checks` cerca controlli efficaci e a basso peso per ciascun qubit di destinazione e li aggiunge al circuito, restituendo una sequenza di `CheckedCircuit` istanze. Le `CheckedCircuit` istanze contengono un numero crescente di controlli, che va da `0` controlli fino a un controllo per ogni qubit bersaglio. Ogni controllo migliora la capacità di rilevamento degli errori, a scapito di un leggero aumento della profondità del circuito. In questo esempio useremo il circuito che prevede un controllo su ogni qubit di destinazione.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "add-checks",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-paulice/guides/quickstart/extracted-outputs/add-checks-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_paulice import add_pauli_checks\n",
        "\n",
        "# The circuit has virtual qubits, so we specify our target qubits with virtual indices\n",
        "target_qubits_v = [layout.index(q) for q in target_qubits]\n",
        "\n",
        "# Add spacetime Pauli checks\n",
        "checked_circuit = add_pauli_checks(circuit, target_qubits_v, noise_model)[-1]\n",
        "checked_circuit.circuit.draw(\"mpl\", fold=-1, scale=0.4, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postselect-md",
      "metadata": {},
      "source": [
        "<span id=\"3-sample-the-checked-circuit\" />\n",
        "\n",
        "## 3. Effettuare il campionamento del circuito controllato\n",
        "\n",
        "`ibm_boston`Prima di eseguire il campionamento, trasponiamo il circuito verificato sui qubit fisici (`initial_layout = layout + ancilla_qubits`) e nel set di gate della base nativa di. Per emulare una QPU soggetta a rumore, effettuiamo il campionamento utilizzando un backend stabilizzatore Aer soggetto a rumore, i cui tassi di errore corrispondono a quelli `noise_model` utilizzati per la selezione dei controlli.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "sample",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "sampled 1000 shots\n"
          ]
        }
      ],
      "source": [
        "from qiskit import transpile\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel as AerNoiseModel\n",
        "from qiskit_aer.noise import ReadoutError, depolarizing_error\n",
        "\n",
        "# Transpile once: lay the checked circuit out on our qubits and into the native basis\n",
        "isa_circuit = transpile(\n",
        "    checked_circuit.circuit,\n",
        "    backend,\n",
        "    initial_layout=layout + ancilla_qubits,\n",
        "    optimization_level=0,\n",
        ")\n",
        "\n",
        "# Build an Aer noise model matching the depolarizing model used to pick checks\n",
        "aer_noise = AerNoiseModel()\n",
        "aer_noise.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(noise_model.gate_noise, 2), [\"cz\"]\n",
        ")\n",
        "p = noise_model.readout_noise\n",
        "aer_noise.add_all_qubit_readout_error(ReadoutError([[1 - p, p], [p, 1 - p]]))\n",
        "simulator = AerSimulator(method=\"stabilizer\", noise_model=aer_noise)\n",
        "\n",
        "counts = (\n",
        "    simulator.run(isa_circuit, shots=1000, seed_simulator=1764)\n",
        "    .result()\n",
        "    .get_counts()\n",
        ")\n",
        "print(f\"sampled {sum(counts.values())} shots\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "postselect-step-md",
      "metadata": {},
      "source": [
        "<span id=\"4-postselect-samples-with-no-detected-error\" />\n",
        "\n",
        "## 4. Campioni post-selezione in cui non è stato rilevato alcun errore\n",
        "\n",
        "`get_postselection_method` assegna a ogni scatto il proprio vettore di sindrome; conserviamo solo gli scatti con una sindrome composta interamente da zeri, ovvero quelli in cui nessun controllo ha rilevato un errore. L'eliminazione degli scatti contrassegnati rimuove gli errori rilevati dalla distribuzione rimanente, migliorandone la fedeltà a scapito di una frequenza di campionamento inferiore. Consulta il [tutorial](https://github.com/Qiskit/qiskit-paulice/blob/main/docs/guides/low_overhead_error_detection_using_spacetime_codes.ipynb) per un confronto quantitativo della fedeltà.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "postselect",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "kept 927 of 1000 shots (93%)\n"
          ]
        }
      ],
      "source": [
        "# Keep only the shots in which no check reported an error\n",
        "ps_fn = checked_circuit.get_postselection_method()\n",
        "counts_postselected = {\n",
        "    bs: n for bs, n in counts.items() if not ps_fn(bs).any()\n",
        "}\n",
        "\n",
        "kept, total = sum(counts_postselected.values()), sum(counts.values())\n",
        "print(f\"kept {kept} of {total} shots ({kept / total:.0%})\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}