{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Démarrage rapide\"\n",
        "description: \"Guide de démarrage rapide pour la dernière version de Qiskit Paulice\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Démarrage rapide\n",
        "\n",
        "`qiskit-paulice` Ce guide présente un exemple minimal fonctionnel du paquet. Nous utilisons [des contrôles de Pauli spatio-temporels](https://arxiv.org/abs/2504.15725) pour détecter les erreurs lors de l'exécution d'un circuit de Clifford, puis nous ne sélectionnons a posteriori que les échantillons pour lesquels aucune erreur n'a été détectée. Pour découvrir un workflow complet et détaillé qui permet de quantifier l'amélioration de la fidélité, consultez le [tutoriel approfondi](https://github.com/Qiskit/qiskit-paulice/blob/main/docs/guides/low_overhead_error_detection_using_spacetime_codes.ipynb).\n",
        "\n",
        "**Étapes du flux de travail**\n",
        "\n",
        "1. Préparez les données d'entrée : un circuit de charge utile de Clifford, les qubits cibles/auxiliaires destinés à accueillir les contrôles, ainsi qu'un modèle de bruit.\n",
        "2. Trouvez de bons contrôleurs de Pauli dans l'espace-temps et ajoutez-les au circuit.\n",
        "3. Testez le circuit vérifié.\n",
        "4. Ne sélectionnez que les échantillons pour lesquels aucune erreur n'a été détectée.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "build-circuit",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs\" />\n",
        "\n",
        "## 1. Préparer les données d'entrée\n",
        "\n",
        "`add_pauli_checks` prend comme entrée un **circuit** de Clifford comportant au moins une mesure terminale, les **qubits cibles** qui seront utilisés pour mettre en œuvre les vérifications, ainsi qu'un **modèle de bruit** servant à évaluer les vérifications candidates. `ibm_boston`Nous construisons ici un circuit de Clifford aléatoire peu profond, nous le disposons sur une chaîne d' 1D s de qubits physiques, nous associons chaque qubit de charge utile à un qubit auxiliaire voisin (de sorte qu'une vérification ne nécessite aucune porte SWAP), et nous déduisons un modèle approximatif de bruit dépolarisant à partir des données de référence du 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. Rechercher et ajouter les contrôles de Pauli dans l'espace-temps\n",
        "\n",
        "`add_pauli_checks` recherche des contrôles efficaces et de faible poids pour chaque qubit cible, les ajoute au circuit et renvoie une séquence `CheckedCircuit` d'instances. Les `CheckedCircuit` instances comportent un nombre croissant de vérifications, allant de `0` vérifications jusqu’à une vérification par qubit cible. Chaque vérification améliore la capacité de détection des erreurs, au prix d'une légère augmentation de la profondeur du circuit. Dans cet exemple, nous utiliserons le circuit comportant une vérification sur chaque qubit cible.\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. Effectuer un test sur le circuit vérifié\n",
        "\n",
        "`ibm_boston`Avant de procéder à l'échantillonnage, nous transpilons le circuit vérifié sur des qubits physiques (`initial_layout = layout + ancilla_qubits`) et dans l'ensemble de portes de base natif de. Pour simuler un QPU sujet au bruit, nous effectuons un échantillonnage à l'aide d'un backend de stabilisateur Aer sujet au bruit, dont les taux d'erreur correspondent à ceux `noise_model` utilisés pour sélectionner les vérifications.\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. Échantillons sélectionnés a posteriori ne présentant aucune erreur détectée\n",
        "\n",
        "`get_postselection_method` associe chaque trame à son vecteur de syndrome; nous ne conservons que les trames dont le vecteur de syndrome est entièrement composé de zéros — c'est-à-dire celles pour lesquelles aucun contrôle n'a détecté d'erreur. Le fait d'écarter les images signalées permet d'éliminer les erreurs détectées de la distribution restante, ce qui améliore sa fidélité au prix d'un taux d'échantillonnage plus faible. Consultez le [tutoriel](https://github.com/Qiskit/qiskit-paulice/blob/main/docs/guides/low_overhead_error_detection_using_spacetime_codes.ipynb) pour une comparaison quantitative de la fidélité.\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
}