{
  "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 Shaded lightcones\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "38de8ef2",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Démarrage rapide\"\n",
        "description: \"Guide de démarrage rapide pour l'extension Qiskit « Shaded lightcones » (qiskit-addon-slc)\"\n",
        "---\n",
        "\n",
        "<span id=\"quickstart\" />\n",
        "\n",
        "# Démarrage rapide\n",
        "\n",
        "Ce guide présente un exemple minimal fonctionnel du paquet `qiskit-addon-slc` . Nous calculons un cône de lumière ombré afin de réduire le coût d'échantillonnage de la correction probabiliste des erreurs (PEC).\n",
        "\n",
        "Le PEC atténue le bruit de porte en effectuant un échantillonnage à partir d'une décomposition en quasi-probabilités du canal de bruit inverse. Le coût de l'échantillonnage augmente avec chaque terme d'erreur qu'il doit atténuer; cependant, toutes les erreurs n'ont pas le même impact sur l'observable. Une erreur située en dehors du cône de lumière causal de l'observable ne peut en aucun cas influencer la valeur attendue mesurée; et même à l'intérieur de ce cône, certaines erreurs sont plus préjudiciables que d'autres. Un cône de lumière ombré permet de quantifier cela en délimitant l'effet que chaque terme d'erreur de Pauli exerce sur l'observable. Le tronquage des termes d'erreur ayant l'effet le plus faible réduit la complexité du modèle de bruit que le PEC doit atténuer, ce qui diminue le coût d'échantillonnage en échange d'un biais faible et borné.\n",
        "\n",
        "Pour découvrir comment mettre en place un workflow réaliste et l'exécuter sur du matériel quantique, consultez le tutoriel « [Probabilistic error cancellation with shaded lightcones » (Annulation probabiliste des erreurs avec cônes de lumière ombrés)](/docs/tutorials/pec-with-shaded-lightcones) sur le site IBM Quantum Platform.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "71bc0134",
      "metadata": {},
      "source": [
        "<span id=\"1-prepare-the-inputs-for-slc\" />\n",
        "\n",
        "## 1. Préparer les données d'entrée pour SLC\n",
        "\n",
        "Nous construisons ici un circuit d'Ising à champ transversal « trotterisé » de 6 qubits et choisissons une observable d' $Z$ e à un seul qubit sur le qubit central.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "0fff4e37",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Observable: IIZIII\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-slc/guides/quickstart/extracted-outputs/0fff4e37-1.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.quantum_info import Pauli\n",
        "\n",
        "\n",
        "def trotter_ising_circuit(num_qubits, num_steps, rx_angle, rzz_angle):\n",
        "    \"\"\"Trotterized transverse-field Ising evolution on a 1D chain.\"\"\"\n",
        "    circuit = QuantumCircuit(num_qubits)\n",
        "    for _ in range(num_steps):\n",
        "        circuit.rx(rx_angle, range(num_qubits))\n",
        "        circuit.barrier()\n",
        "        for start in (0, 1):  # even then odd bonds\n",
        "            for i in range(start, num_qubits - 1, 2):\n",
        "                circuit.rzz(rzz_angle, i, i + 1)\n",
        "        circuit.barrier()\n",
        "    return circuit\n",
        "\n",
        "\n",
        "num_qubits = 6\n",
        "circuit = trotter_ising_circuit(\n",
        "    num_qubits, num_steps=2, rx_angle=np.pi / 16, rzz_angle=-np.pi / 2\n",
        ")\n",
        "\n",
        "# Measure <Z> on the middle qubit\n",
        "observable = Pauli(\"I\" * num_qubits).compose(\"Z\", [num_qubits // 2])\n",
        "\n",
        "print(f\"Observable: {observable}\")\n",
        "circuit.draw(\"mpl\", fold=-1, scale=0.7)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9a732427",
      "metadata": {},
      "source": [
        "Le SLC opère sur les couches de portes à deux qubits du circuit, qui sont sujettes au bruit. Ici, nous utilisons `samplomatic` pour regrouper les portes dans des encadrés annotés et nous attribuons une annotation d'injection de bruit à chaque couche de deux qubits. `generate_noise_model_paulis` puis énumère les termes d'erreur de Pauli de chaque couche bruyante distincte.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "3f4301ac",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Noisy layers: 2\n",
            "Pauli error terms across all layers: 102\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_slc.utils import generate_noise_model_paulis\n",
        "from samplomatic.transpiler import generate_boxing_pass_manager\n",
        "from samplomatic.utils import find_unique_box_instructions\n",
        "\n",
        "# Group gates into boxes and annotate each two-qubit layer with a noise-injection point\n",
        "boxing_pass = generate_boxing_pass_manager(\n",
        "    inject_noise_targets=\"all\",\n",
        "    inject_noise_strategy=\"individual_modification\",\n",
        "    inject_noise_site=\"after\",\n",
        "    twirling_strategy=\"active\",\n",
        "    remove_barriers=\"never\",\n",
        ")\n",
        "boxed_circuit = boxing_pass.run(circuit)\n",
        "\n",
        "# Enumerate the 1- and 2-weight Pauli error terms of each unique noisy layer\n",
        "noise_model_paulis = generate_noise_model_paulis(\n",
        "    find_unique_box_instructions(boxed_circuit)\n",
        ")\n",
        "\n",
        "num_terms = sum(len(paulis) for paulis in noise_model_paulis.values())\n",
        "print(f\"Noisy layers: {len(noise_model_paulis)}\")\n",
        "print(f\"Pauli error terms across all layers: {num_terms}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "97101eed",
      "metadata": {},
      "source": [
        "<span id=\"2-compute-the-shaded-lightcone\" />\n",
        "\n",
        "## 2. Calculer le cône de lumière ombré\n",
        "\n",
        "Le cône de lumière ombré est construit en attribuant un coefficient à chaque terme d'erreur de Pauli du modèle de bruit, en fonction de l'importance de son effet sur la valeur attendue de l'observable. Ces échelles sont calculées à partir des limites d'erreur en avant et en arrière de chaque terme d'erreur (décrites ci-dessous), ainsi que de son taux d'erreur :\n",
        "\n",
        "* `compute_forward_bounds` fait évoluer chaque terme d'erreur *jusqu'à* la fin du circuit afin de limiter son effet sur l'observable qui y est mesurée.\n",
        "* `compute_backward_bounds` fait évoluer chaque terme d'erreur *à rebours* jusqu'au début du circuit afin de limiter son effet sur l'état initial.\n",
        "\n",
        "`merge_bounds` combine les deux en un seul terme d'erreur. Fusion des échelles, chacune étant limitée par le taux d'erreur du terme. Ces taux proviennent généralement d'une expérience d'apprentissage du bruit (par exemple `NoiseLearnerV3`); Ici, par souci de simplicité, nous utilisons des taux aléatoires.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "82205ea3",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import PauliLindbladMap, QubitSparsePauliList\n",
        "from qiskit_addon_slc.bounds import (\n",
        "    compute_backward_bounds,\n",
        "    compute_forward_bounds,\n",
        "    merge_bounds,\n",
        ")\n",
        "\n",
        "forward_bounds = compute_forward_bounds(\n",
        "    boxed_circuit, noise_model_paulis, observable\n",
        ")\n",
        "backward_bounds = compute_backward_bounds(boxed_circuit, noise_model_paulis)\n",
        "\n",
        "# Stand-in for rates that would be measured by a noise-learning experiment on hardware\n",
        "rng = np.random.default_rng(42)\n",
        "noise_rates = {\n",
        "    layer_id: PauliLindbladMap.from_components(\n",
        "        rng.random(len(paulis)) * 5e-3,\n",
        "        QubitSparsePauliList.from_sparse_list(\n",
        "            paulis.to_sparse_list(), paulis.num_qubits\n",
        "        ),\n",
        "    )\n",
        "    for layer_id, paulis in noise_model_paulis.items()\n",
        "}\n",
        "\n",
        "merged_bounds = merge_bounds(\n",
        "    boxed_circuit, forward_bounds, backward_bounds, noise_rates\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c324ebd6",
      "metadata": {},
      "source": [
        "Dans la visualisation du cône de lumière ombré ci-dessous, chaque case est ombrée en fonction de l'importance avec laquelle les erreurs à cet emplacement peuvent affecter la grandeur observable : les cases claires correspondent aux limites les plus larges, tandis que les cases qui s'estompent vers l'arrière-plan contiennent des termes d'erreur qui ont peu d'effet sur le calcul — ces erreurs sont des candidates naturelles à exclure du modèle de bruit. Les valeurs indiquées dans la visualisation ci-dessous correspondent à la somme des limites d'erreur de toutes les erreurs de Pauli à cet emplacement; c'est pourquoi certaines de ces valeurs dépassent `2.0` -- la limite applicable à une erreur de Pauli isolée.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "a2201cec",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-slc/guides/quickstart/extracted-outputs/a2201cec-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from qiskit_addon_slc.visualization import draw_shaded_lightcone\n",
        "\n",
        "draw_shaded_lightcone(boxed_circuit, merged_bounds, noise_model_paulis)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "42483e78",
      "metadata": {},
      "source": [
        "<span id=\"3-reduce-the-sampling-cost\" />\n",
        "\n",
        "## 3. Réduire le coût de l'échantillonnage\n",
        "\n",
        "`compute_local_scales` transforme le cône de lumière ombré en une configuration PEC concrète. Elle classe les termes d'erreur par ordre de priorité en fonction de leur effet limité sur la grandeur observable et élimine ceux qui ont le moins d'impact jusqu'à ce que la valeur souhaitée `bias_tolerance` soit atteinte. Elle renvoie des coefficients pour chaque terme d'erreur : `0.0` pour les termes à ignorer lors de l'atténuation et `-1.0` pour les termes qui doivent être atténués. La fonction renvoie également le surcoût **d'échantillonnage** qui en résulte ( $\\gamma^2$ ) ainsi qu'une borne sur le **biais résiduel** induit par la troncature.\n",
        "\n",
        "Ce paramètre atténue `bias_tolerance=0.0` chaque terme d'erreur au sein du cône de lumière causal de l'observable et fournit un coût d'échantillonnage de référence. En autorisant un léger biais, SLC peut écarter les termes à faible impact et réduire encore davantage ce coût.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "31fe87d9",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Full PEC (bias_tolerance=0.0):  sampling cost 1.923, residual bias 0.000\n",
            "Shaded    (bias_tolerance=0.05): sampling cost 1.441, residual bias 0.044\n",
            "\n",
            "Sampling-cost reduction: 25% for <= 0.05 bias\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_slc.bounds import compute_local_scales\n",
        "\n",
        "_, full_cost, full_bias = compute_local_scales(\n",
        "    boxed_circuit, merged_bounds, noise_rates, bias_tolerance=0.0\n",
        ")\n",
        "local_scales, reduced_cost, reduced_bias = compute_local_scales(\n",
        "    boxed_circuit, merged_bounds, noise_rates, bias_tolerance=0.05\n",
        ")\n",
        "\n",
        "print(\n",
        "    f\"Full PEC (bias_tolerance=0.0):  sampling cost {full_cost:.3f}, residual bias {full_bias:.3f}\"\n",
        ")\n",
        "print(\n",
        "    f\"Shaded    (bias_tolerance=0.05): sampling cost {reduced_cost:.3f}, residual bias {reduced_bias:.3f}\"\n",
        ")\n",
        "print(\n",
        "    f\"\\nSampling-cost reduction: {(1 - reduced_cost / full_cost):.0%} for <= 0.05 bias\"\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}