{
  "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 la rétropropagation de l'opérateur (OBP)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "intro",
      "metadata": {},
      "source": [
        "<span id=\"quick-start\" />\n",
        "\n",
        "# Démarrage rapide\n",
        "\n",
        "Ce guide présente un exemple minimal fonctionnel du paquet `qiskit-addon-obp` . Nous utilisons la rétropropagation des opérateurs (OBP) pour réduire la profondeur d'un circuit quantique en intégrant les portes de fin dans l'observable.\n",
        "\n",
        "Une valeur attendue $\\langle O \\rangle = \\langle \\psi | U^\\dagger O U | \\psi \\rangle$ reste inchangée si l'on retire un bloc de portes situé à la fin du circuit $U$ et si l'on effectue la conjugaison classique de l'observable par rapport à ce bloc. OBP applique cette méthode à plusieurs reprises, en évaluant une partie du circuit de manière classique afin que seul un circuit moins complexe soit exécuté sur le matériel. Le prix à payer est que chaque porte absorbée peut décomposer l'observable en un plus grand nombre de termes de Pauli; il faut donc mettre en balance le gain en profondeur obtenu et l'augmentation de la complexité de l'observable.\n",
        "\n",
        "Pour découvrir des exemples illustrant comment créer des workflows réalistes à l'aide de cet outil et les exécuter sur du matériel quantique, consultez les tutoriels disponibles sur la plateforme « IBM Quantum Platform » ( [Tutoriel OBP](/docs/tutorials/operator-back-propagation) ).\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "inputs-header",
      "metadata": {},
      "source": [
        "<span id=\"prepare-inputs-for-obp\" />\n",
        "\n",
        "## Préparer les données pour l'OBP\n",
        "\n",
        "L'OBP prend en entrée une liste de tranches de circuit et une observable. Il effectue une rétropropagation des tranches une par une, depuis la fin du circuit vers l'observable, ce qui réduit la profondeur du circuit au prix de l'ajout de termes de Pauli supplémentaires dans l'observable. Nous générons ici un circuit d'évolution temporelle pour un modèle de Heisenberg à 10 qubits et le découpons par type de porte.\n",
        "\n",
        "Nous représentons ci-dessous le circuit d'origine, puis ce même circuit en version modifiée, avec des barrières marquant les limites des tranches — chaque tranche constituant une unité pouvant être intégrée à l'observable en une seule étape de rétropropagation.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "setup-inputs",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit depth: 18\n",
            "Number of slices: 18\n",
            "Observable terms: 1\n"
          ]
        }
      ],
      "source": [
        "import numpy as np\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.synthesis import LieTrotter\n",
        "from qiskit.transpiler import CouplingMap\n",
        "from qiskit_addon_utils.problem_generators import (\n",
        "    generate_time_evolution_circuit,\n",
        "    generate_xyz_hamiltonian,\n",
        ")\n",
        "from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types\n",
        "\n",
        "# Generate a circuit to reduce\n",
        "coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)\n",
        "reduced_coupling_map = coupling_map.reduce(\n",
        "    [0, 13, 1, 14, 10, 16, 5, 12, 8, 18]\n",
        ")\n",
        "\n",
        "hamiltonian = generate_xyz_hamiltonian(\n",
        "    reduced_coupling_map,\n",
        "    coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),\n",
        "    ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),\n",
        ")\n",
        "\n",
        "circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian,\n",
        "    time=0.2,\n",
        "    synthesis=LieTrotter(reps=2),\n",
        ")\n",
        "\n",
        "# Slice the circuit and define an observable\n",
        "slices = slice_by_gate_types(circuit)\n",
        "observable = SparsePauliOp(\"IIIIIIIIIZ\")\n",
        "\n",
        "print(f\"Original circuit depth: {circuit.depth()}\")\n",
        "print(f\"Number of slices: {len(slices)}\")\n",
        "print(f\"Observable terms: {len(observable)}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "46efdcfc",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit:\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/46efdcfc-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Sliced circuit (recombined with barriers for visualization)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/46efdcfc-3.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Recombine the slices with barriers to make the slice boundaries visible\n",
        "sliced_circuit = combine_slices(slices, include_barriers=True)\n",
        "\n",
        "print(\"Original circuit:\")\n",
        "display(circuit.draw(\"mpl\", scale=0.6, fold=-1))\n",
        "print(\"Sliced circuit (recombined with barriers for visualization)\")\n",
        "sliced_circuit.draw(\"mpl\", scale=0.6, fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "obp-header",
      "metadata": {},
      "source": [
        "<span id=\"reduce-circuit-depth-with-obp\" />\n",
        "\n",
        "## Réduire la profondeur du circuit grâce à l'OBP\n",
        "\n",
        "Nous appelons `backpropagate` cela « intégrer des tranches » dans l'observable. La fonction renvoie l'observable développé, les tranches de circuit qui n'ont pas été propagées, ainsi que les métadonnées relatives à la procédure.\n",
        "\n",
        "Si on ne fait rien, l'observable peut tendre vers des termes d' $2^n$ -Pauli. Cette limite `operator_budget` restreint cette croissance : ici, nous autorisons au maximum 8 groupes commutatifs au niveau des qubits, ce qui détermine approximativement le nombre d'itérations nécessaires pour évaluer l'observable sur le QPU. La rétropropagation s'arrête dès que l'absorption de la tranche suivante dépasserait le budget, ce qui est exactement ce qui se passe ci-dessous : seules 7 des 18 tranches sont absorbées avant que l'observable ne remplisse les 8 groupes de commutation et que la procédure ne s'arrête.\n",
        "\n",
        "Pour approfondir l'analyse du circuit, l'argument « keyword `truncation_error_budget` » de peut `backpropagate` être utilisé pour éliminer les termes de Pauli à faible coefficient de l'observable à mesure que celle-ci augmente. Cela atténue la croissance de la grandeur observable, au prix d'une erreur proportionnelle à l'amplitude des termes de Pauli qui sont tronqués. Ces deux budgets sont complémentaires et peuvent être utilisés conjointement : permet de `operator_budget` contrôler la *taille* de la grandeur observable, tandis que permet de `truncation_error_budget` contrôler l' *erreur* induite par la suppression de certains termes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "run-obp",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 7 of 18 slices.\n",
            "Reduced circuit depth: 11 (was 18)\n",
            "Observable grew from 1 to 18 Pauli terms.\n",
            "Filled 8 of 8 commuting groups, exhausting the budget.\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "\n",
        "max_qwc_groups = 8\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    observable,\n",
        "    slices,\n",
        "    operator_budget=OperatorBudget(max_qwc_groups=max_qwc_groups),\n",
        ")\n",
        "\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "num_groups = len(bp_obs.group_commuting(qubit_wise=True))\n",
        "\n",
        "print(\n",
        "    f\"Backpropagated {metadata.num_backpropagated_slices} of {len(slices)} slices.\"\n",
        ")\n",
        "print(\n",
        "    f\"Reduced circuit depth: {reduced_circuit.depth()} (was {circuit.depth()})\"\n",
        ")\n",
        "print(f\"Observable grew from {len(observable)} to {len(bp_obs)} Pauli terms.\")\n",
        "print(\n",
        "    f\"Filled {num_groups} of {max_qwc_groups} commuting groups, exhausting the budget.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b006625e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/quickstart/extracted-outputs/b006625e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reduced_circuit.draw(\"mpl\", scale=0.6, fold=-1)"
      ]
    },
    {
      "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
}