{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Tronquer les termes de Pauli pendant la rétropropagation\"\n",
        "description: \"Tronquer les termes de Pauli lors de la rétropropagation pour la dernière version de la rétropropagation par opérateurs (OBP)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "11cf5076-2777-499c-be50-531e305bf011",
      "metadata": {},
      "source": [
        "<span id=\"truncate-pauli-terms-during-backpropagation\" />\n",
        "\n",
        "# Tronquer les termes de Pauli pendant la rétropropagation\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1453472-3506-4008-b432-f8ef9ccbf3ee",
      "metadata": {},
      "source": [
        "Ce guide explique comment configurer le mécanisme de troncature des termes de Pauli fourni par le module « [qiskit\\_addon\\_obp.utils.truncating](/docs/api/qiskit-addon-obp/utils-truncating#module-qiskit_addon_obp.utils.truncating) ».\n",
        "\n",
        "La rétropropagation des opérateurs (OBP) peut être utilisée pour réduire la profondeur des circuits quantiques, au prix d'une observable plus complexe. Pour obtenir des résultats pertinents avec l'OBP, il faut généralement tronquer certains termes de la variable observable afin d'éviter qu'elle ne devienne trop volumineuse. Une façon de permettre une rétropropagation plus profonde dans le circuit, tout en évitant que l'opérateur ne devienne trop volumineux, consiste à tronquer les termes dont les coefficients sont faibles, plutôt que de les ajouter à l'opérateur. La troncature de termes peut permettre de réduire le nombre de circuits quantiques à exécuter, mais elle entraîne une certaine erreur dans le calcul final de la valeur d'espérance, proportionnelle à la valeur absolue des coefficients des termes tronqués.\n",
        "\n",
        "La méthode [de rétropropagation](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) accepte un paramètre facultatif, [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget), qui permet de configurer la troncature des termes de Pauli de faible poids pour chaque observable après la rétropropagation réussie de chaque tranche.\n",
        "Le nombre de termes tronqués dépend de divers paramètres de configuration définis par l'utilisateur.\n",
        "À l'heure actuelle, une seule stratégie de troncature est disponible : la méthode [truncate\\_binary\\_search](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search).\n",
        "À partir d'un observable et d'un *budget* donné, l'algorithme effectuera une recherche binaire sur les termes de Pauli et les coefficients de cet observable afin de déterminer le seuil optimal,\n",
        "de sorte que la somme des coefficients tronqués soit maximale tout en restant inférieure au budget.\n",
        "\n",
        "**Remarque** : par défaut, la norme L1 est utilisée pour évaluer et bornes l'erreur de troncature; toutefois, le paramètre `p_norm` permet de spécifier la norme Lp à utiliser.\n",
        "Pour plus d'informations sur l'utilisation de ce paramètre, consultez le guide intitulé « [Utilisation de différentes normes Lp pour la troncature du terme de Pauli](/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm) ».\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aaf5f046-6d5e-4b6f-add7-b60604edef30",
      "metadata": {},
      "source": [
        "Les exemples suivants illustrent différentes façons de créer un « [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) » à l'aide de la fonction [setup\\_budget](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget) fournie.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c83c0741-4a9e-4f89-92a9-4756ef133130",
      "metadata": {},
      "source": [
        "<span id=\"construct-an-example-circuit\" />\n",
        "\n",
        "## Construisez un circuit type\n",
        "\n",
        "Ce guide utilise les tranches de circuit suivantes :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "297fc3b0-d3b4-432d-96cd-7e175b7e6a52",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/297fc3b0-d3b4-432d-96cd-7e175b7e6a52-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 1,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "import rustworkx.generators\n",
        "from qiskit.synthesis import LieTrotter\n",
        "from qiskit_addon_utils.problem_generators import (\n",
        "    PauliOrderStrategy,\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 linear chain of 10 qubits\n",
        "linear_chain = rustworkx.generators.path_graph(10)\n",
        "\n",
        "# Use an arbitrary XY model\n",
        "hamiltonian = generate_xyz_hamiltonian(\n",
        "    linear_chain,\n",
        "    coupling_constants=(0.05, 0.02, 0.0),\n",
        "    ext_magnetic_field=(0.02, 0.08, 0.0),\n",
        "    pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,\n",
        ")\n",
        "# Evolve for some time\n",
        "circuit = generate_time_evolution_circuit(\n",
        "    hamiltonian, synthesis=LieTrotter(reps=3), time=2.0\n",
        ")\n",
        "# slice the circuit by gate type\n",
        "slices = slice_by_gate_types(circuit)\n",
        "\n",
        "# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit\n",
        "combine_slices(slices, include_barriers=True).draw(\"mpl\", fold=50, scale=0.6)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "89ce4fa9-06ba-4009-8594-d1b85f5e719f",
      "metadata": {},
      "source": [
        "Nous allons nous intéresser à une seule grandeur observable simple :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "b168d34c-f147-4524-9af9-264f56440a55",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "obs = SparsePauliOp(\"IIIIIZIIII\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "723dcf4e-1dab-4c26-a8e4-cd30b1564189",
      "metadata": {},
      "source": [
        "<span id=\"the-simplest-case-a-fixed-truncation-budget-for-each-slice\" />\n",
        "\n",
        "## Le cas le plus simple : un budget de troncature fixe pour chaque tranche\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e04e5c4-1e6b-4720-9563-becbb9edc4d7",
      "metadata": {},
      "source": [
        "Le budget disponible pour la troncature des termes de Pauli peut varier à chaque étape de la rétropropagation.\n",
        "Pour comprendre comment cela fonctionne, nous allons d'abord examiner le cas le plus simple, celui d'un budget de troncature fixe défini par l'utilisateur.\n",
        "\n",
        "La manière la plus simple de définir le budget de troncature consiste à utiliser l'argument `max_error_per_slice` .\n",
        "C'est d'ailleurs ce qui est présenté dans le tutoriel « [Réduire la profondeur des circuits grâce à la rétropropagation des opérateurs](/docs/tutorials/operator-back-propagation) ». Si `float` l'on définit `max_error_per_slice` sur une valeur, chaque tranche se voit alors attribuer un budget égal à cette valeur.\n",
        "Dans l'exemple ci-dessous, nous avons défini cette valeur à, ce qui `0.001` garantit une erreur de troncature implicite inférieure ou égale à `0.018`, si les 18 tranches faisaient l'objet d'une rétropropagation.\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  Notez que tout budget d'erreur restant après la rétropropagation d'une tranche et la suppression des termes à faibles coefficients sera toujours ajouté au budget d'erreur de la tranche suivante.\n",
        "</div>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "5f20a9b9-c3c4-43c4-9f13-fdf451958081",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp.utils.truncating import setup_budget\n",
        "\n",
        "truncation_error_budget = setup_budget(max_error_per_slice=0.001)\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "c3b8b9ef-7d5c-43dd-8cb9-90756d4753b7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 11 circuit slices.\n",
            "New observable contains 29 terms and 10 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp import backpropagate\n",
        "from qiskit_addon_obp.utils.simplify import OperatorBudget\n",
        "\n",
        "op_budget = OperatorBudget(max_qwc_groups=10)\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4a772af6-edc6-4e53-81c7-53f3b72bf58a",
      "metadata": {},
      "source": [
        "Nous utilisons ensuite l'instance [OBPMetadata](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata) et les outils fournis par le module [de visualisation](/docs/api/qiskit-addon-obp/utils-visualization#module-qiskit_addon_obp.utils.visualization) pour visualiser le processus de rétropropagation.\n",
        "\n",
        "* **Le graphique en haut à gauche** montre que nous disposons d'un budget suffisant pour commencer à tronquer les termes observables après la rétropropagation de la troisième tranche. À partir de la troisième tranche, nous savons que nous tronquons au moins un terme de chaque tranche que nous soumettons à la rétropropagation, car nous subissons une certaine erreur de troncature après chaque tranche.\n",
        "* **Le graphique en haut à droite** montre que le budget d'erreur atteint pour `.003` la troisième tranche. Nous constatons une forte baisse du budget restant, ce qui signifie que certaines périodes ont été omises des données observables. Cela correspond à ce que nous avions déduit du graphique en haut à gauche.\n",
        "* **Le graphique en bas à gauche** montre que, à mesure que nous supprimons des termes de notre observable, notre erreur cumulée globale augmente de manière monotone. Ce graphique confirme également qu'aucun terme n'a été tronqué avant que la troisième tranche n'ait fait l'objet d'une rétropropagation.\n",
        "* **Le graphique en bas à droite** montre que le nombre de groupes de Pauli commutatifs dans notre observable a atteint la limite spécifiée de `10`. Ce graphique montre également comment la propagation en arrière d'une couche supplémentaire entraînerait un dépassement de la limite spécifiée par notre observable, comme le montre le croisement des lignes noire et rouge.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8ff436fa-3b52-4387-a9f4-d8d9df13c480",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\">\n",
        "  Notez que sur tous ces graphiques, l'axe des x répertorie les tranches soumises à la rétropropagation; toutefois, comme l'OBP intervient à la fin du circuit, `slice 1` correspond à la toute dernière tranche, `slice 2` à celle qui la précède, et ainsi de suite.\n",
        "</div>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "68f118df-6718-49b2-ba80-c37ef461f71a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/68f118df-6718-49b2-ba80-c37ef461f71a-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from matplotlib import pyplot as plt\n",
        "from qiskit_addon_obp.utils.visualization import (\n",
        "    plot_accumulated_error,\n",
        "    plot_left_over_error_budget,\n",
        "    plot_num_qwc_groups,\n",
        "    plot_slice_errors,\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "523f5a81-afbc-4eee-9d66-05cc3b4a781a",
      "metadata": {},
      "source": [
        "<span id=\"specify-slice-budget-explicitly\" />\n",
        "\n",
        "## Spécifier explicitement le budget par tranche\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "80f9337d-ea37-454c-ae0b-b101259568c9",
      "metadata": {},
      "source": [
        "Si vous savez comment attribuer un budget à chaque tranche de manière à optimiser les performances de la rétropropagation, vous pouvez choisir d'attribuer explicitement un budget à chaque tranche. À titre d'illustration, nous attribuerons un budget nul aux trois premières tranches et utiliserons un budget de `.001` par tranche pour les tranches restantes.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "17ce7232-7e3a-44e4-8081-1b1b27430bad",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "# Zero out the first 3 slices' budgets\n",
        "max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_per_slice=max_error_per_slice\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "7cd711a7-1aea-4b45-a46a-63d771230969",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 11 circuit slices.\n",
            "New observable contains 32 terms and 10 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3e853723-454b-4c69-b9c5-180eb1b2e6c2",
      "metadata": {},
      "source": [
        "La suppression du budget des trois premières couches a eu pour conséquence qu'aucun terme n'a été tronqué avant la quatrième couche, comme le confirment trois de ces graphiques. Ce qui peut paraître quelque peu surprenant, c'est que, bien qu'aucun budget résiduel n'ait été transféré à la quatrième tranche, une période a tout de même été tronquée en utilisant le budget `.001` alloué. Cela ressort clairement du graphique **du** haut à gauche, mais on peut également le constater sur celui **du haut à droite**, où la courbe du budget restant s'aplatit entre les tranches trois et quatre. Autre détail à noter : à partir de ce moment-là, au moins un terme a été tronqué, comme dans l'exemple précédent.\n",
        "\n",
        "La conclusion principale est que, bien que nous ayons subi moins d'erreurs de troncature dans le deuxième exemple en remettant à zéro les budgets de certaines tranches, nous avons pu effectuer la rétropropagation sur le même nombre de tranches, et notre observable contient le même nombre de groupes de Pauli commutatifs. Nous pouvons confirmer que la borne de notre erreur est plus petite dans le deuxième exemple en examinant le graphique **en bas à gauche**.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "b7167f80-5d29-4c32-be97-cba733f18b1d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b7167f80-5d29-4c32-be97-cba733f18b1d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f03a92ee-e9a0-4707-b23a-ebdc3c9f35a3",
      "metadata": {},
      "source": [
        "<span id=\"specify-the-budget-cyclically\" />\n",
        "\n",
        "## Définir le budget de manière périodique\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62f0ea1f-eaec-408d-99df-25a8647c1eb9",
      "metadata": {},
      "source": [
        "Si vous disposez d'un circuit présentant un motif répétitif, tel qu'un circuit de Trotter, vous pouvez définir un budget pour ce sous-ensemble de tranches qui se répète et faire en sorte que ce budget soit utilisé pour toutes les répétitions suivantes de ces tranches.\n",
        "\n",
        "Plus précisément, le circuit d'exemple que nous utilisons comporte six tranches qui se répètent trois fois, soit un total de 18 tranches. Nous attribuerons arbitrairement un budget nul aux couches `RYY` à un seul qubit et, et un budget `.003` à chacune des couches `RXX` . Nous verrons comment le fait de définir le budget sous la forme d'une séquence de type « length-6 » entraîne son application cyclique à l'ensemble des 18 tranches.\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  Une fois encore, notez que les tranches sont propagées en arrière dans l'ordre inverse (c'est-à-dire en commençant par la fin). Par conséquent, la première entrée de notre budget cyclique est en réalité utilisée pour la dernière tranche, la deuxième entrée pour la tranche précédente, et ainsi de suite.\n",
        "</div>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4f37a576-5a61-4589-a896-8304056d0e19",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "# Specify a length-6 per-slice budget.\n",
        "# This will be cycled over three times to be applied to the 18 slices\n",
        "max_error_per_slice = [0.0] * 4 + [0.003] * 2\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_per_slice=max_error_per_slice\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "7f78cfe4-13e4-472c-b6cc-9d1e867cbd9c",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 13 circuit slices.\n",
            "New observable contains 49 terms and 14 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "op_budget = OperatorBudget(max_qwc_groups=20)\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3511206b-2972-41ba-b981-6c20ccb7712b",
      "metadata": {},
      "source": [
        "Comme le montrent les images **en haut à gauche** et **en bas à gauche**, aucune troncature n'a été effectuée sur les quatre premières coupes, car aucune marge d'erreur ne leur avait été attribuée. Les tranches cinq et six ont vu certains de leurs postes budgétaires réduits à mesure que des crédits étaient débloqués.\n",
        "\n",
        "Comme le montre l'image **en haut à gauche**, une erreur relativement importante s'est produite après la rétropropagation de la tranche sept, alors même qu'aucun budget n'avait été alloué à cette tranche. En effet, seule une partie d'environ `.002` du budget total `.006` alloué aux tranches cinq et six a été utilisée; le reste a donc été transféré à la tranche sept et a été en grande partie dépensé, comme le montre l'image en **haut à droite**.\n",
        "\n",
        "Les images **en** haut à gauche et **en haut à droite** montrent que le faible solde budgétaire restant est épuisé entre les tranches 8 et 10, et qu'un nouveau budget devient disponible à partir de la tranche 11, comme prévu. Les images **en** haut à gauche, **en haut à droite** et **en bas à gauche** illustrent toutes le comportement cyclique de l'argument `max_error_per_slice` lorsque sa longueur est inférieure au nombre de tranches. Ce comportement cyclique se serait poursuivi sur toutes les tranches, mais le critère d'arrêt `max_qwc_groups` a été atteint après la rétropropagation de 13 tranches, comme le montre l'image en **bas à droite**.\n",
        "\n",
        "Il est également intéressant de noter que le nombre de groupes de Pauli diminue en réalité après la mise à disposition de la deuxième tranche du budget, à la tranche 11. En effet, certains groupes présentant de faibles coefficients n'ont pas pu être tronqués tant que le budget disponible n'était pas suffisant après la rétropropagation de la tranche 11; ils se sont donc accumulés dans l'observable pendant plusieurs itérations. Ce cas montre également que `max_qwc_groups` doit être *dépassée* pour que l'algorithme s'arrête.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "4c36768e-03e7-4e1b-81b3-59b6e9eab43e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/4c36768e-03e7-4e1b-81b3-59b6e9eab43e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "269536c5-8ba4-48da-8e99-6b8ce3a52bd0",
      "metadata": {},
      "source": [
        "<span id=\"cap-the-total-error\" />\n",
        "\n",
        "## Limiter l'erreur totale\n",
        "\n",
        "Outre la définition du budget d'erreur par tranche, il est possible de définir le niveau maximal d'erreur pouvant résulter de la troncature. Une fois cette limite atteinte, aucune troncature ne sera plus effectuée; toutefois, la rétropropagation se poursuivra jusqu’à ce que l’observable devienne trop grande et que l’un des critères d’arrêt soit rempli.\n",
        "\n",
        "Nous répétons ensuite l'expérience ci-dessus en fixant une limite maximale pour `max_error_total` de telle sorte que le budget d'erreur soit épuisé après la rétropropagation de la septième tranche.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "8ef6a100-fb4b-4b66-be6e-a094db2c29d4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "# Specify a length-6 per-slice budget.\n",
        "# This will be cycled over 3 times to be applied to the 18 slices\n",
        "max_error_per_slice = [0.0] * 4 + [0.003] * 2\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_per_slice=max_error_per_slice, max_error_total=0.006\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "c58bccd5-aa40-482f-acaf-4ef50d420053",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 10 circuit slices.\n",
            "New observable contains 67 terms and 20 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dadb77a5-00d7-427d-921b-4a005f5ab36f",
      "metadata": {},
      "source": [
        "Comme prévu, notre erreur de troncature atteint son maximum à `.006` (graphique **en bas à gauche** ). Il convient de noter que, lors de cette exécution, nous n'avons pas pu effectuer la rétropropagation de la tranche 11. Cela s'explique par le fait que nous ne disposions pas d'un budget suffisant pour tronquer les termes et que le nombre de groupes de Pauli en va-et-vient a dépassé la limite de `20`, comme le montre l'image **en** bas à droite.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "69c9069a-9038-4319-8dc6-37a1be973ebf",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/69c9069a-9038-4319-8dc6-37a1be973ebf-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2049d223-1b91-4042-ba98-a86f7a59483c",
      "metadata": {},
      "source": [
        "Il peut être souhaitable de simplement plafonner le budget global sans `max_error_total` préciser de montant `max_error_per_slice`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "fac59f4f-3528-49c1-9d4d-9be98d2c7d78",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "truncation_error_budget = setup_budget(max_error_total=0.018)\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "20c55e75-f619-4ac9-aecd-29491867ab82",
      "metadata": {},
      "source": [
        "Le résultat affiché dans la cellule ci-dessus peut paraître un peu surprenant, car la valeur est `per_slice_budget` définie sur `max_error_total`. Cela signifie que la totalité du budget disponible sera dépensée **sans compter**.\n",
        "On peut voir les choses ainsi : chaque tranche dispose de la totalité du budget (puisque nous parcourons toutes les `per_slice_budget`). Toutefois, tout budget déjà utilisé sera déduit du budget disponible à ce stade précis de l'algorithme.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "5d65a057-eb5e-4308-b84d-82bfbea06989",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 9 circuit slices.\n",
            "New observable contains 25 terms and 9 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "op_budget = OperatorBudget(max_qwc_groups=10)\n",
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a77ceb1e-bc6d-462d-aebe-ec478d312233",
      "metadata": {},
      "source": [
        "L'image **en** haut à droite montre comment l'intégralité du budget d'erreur `.018` est mise à la disposition de la première tranche. Aucun tronquage n'est effectué avant la troisième tranche; le budget restant reste donc constant. Le budget diminue de manière monotone, car la totalité du budget est mise à la disposition de chaque tranche issue de la rétropropagation jusqu'à épuisement de celui-ci.\n",
        "\n",
        "Il est à noter que cette expérience a donné deux tranches de rétropropagation de moins que la première expérience de ce cahier, qui est pourtant presque identique. Cela montre que, pour certains problèmes, une répartition équitable du budget pourrait s'avérer optimale. Pour d'autres problèmes, le fait de laisser les tranches utiliser sans restriction la totalité du budget pourrait permettre d'obtenir de meilleures performances.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "51d36d7e-0bd5-4388-bbb8-6831bf1f02e6",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/51d36d7e-0bd5-4388-bbb8-6831bf1f02e6-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8859d36c-258f-441f-a7cf-3997b9f6e8ff",
      "metadata": {},
      "source": [
        "<span id=\"cap-the-number-of-backpropagated-slices-and-the-total-error-together\" />\n",
        "\n",
        "## Limiter simultanément le nombre de tranches soumises à la rétropropagation et l'erreur totale\n",
        "\n",
        "Si vous ne souhaitez pas répartir le budget d'erreur sur l'ensemble du circuit, mais que vous ne voulez pas non plus l'épuiser de manière excessive, vous pouvez spécifier le nombre de tranches que vous comptez soumettre à la rétropropagation (`num_slices`), ainsi qu'un budget d'erreur total (`max_error_total`). Cela permettra de répartir le budget d'erreur de manière uniforme (conformément à `p_norm`) entre les tranches d'entrée.\n",
        "\n",
        "Ici, nous limiterons à le nombre de tranches sur lesquelles nous pourrions effectuer la rétropropagation `12`, tout en conservant le même budget d'erreur total.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "474cf708-5447-4984-873c-5f62f81a402d",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "num_slices = 12\n",
        "\n",
        "truncation_error_budget = setup_budget(\n",
        "    max_error_total=0.018, num_slices=num_slices, p_norm=1\n",
        ")\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ac2fb58-db97-4c9e-9a96-fce97b52e7e2",
      "metadata": {},
      "source": [
        "Nous allons maintenant tenter d'effectuer la rétropropagation des 12 tranches auxquelles nous avons alloué un certain budget à l'étape précédente. Pour ce faire, il suffit d'intégrer les 12 dernières tranches dans notre circuit. Avec `p_norm=1`, chacune des 12 tranches dispose d'un budget de `0.018 / num_slices = 0.0015`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "id": "6efc92bc-3433-4ea6-a683-684205201f4f",
      "metadata": {},
      "outputs": [],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices[-num_slices:],\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b15c2a20-19c3-417c-b32d-173c10f13852",
      "metadata": {},
      "source": [
        "Comme nous avons transmis un sous-ensemble de nos tranches (dans le code `slices[-num_slices:]`) à `backpropagate`, nous devons combiner les tranches restantes après la rétropropagation avec celles qui n'ont jamais été envoyées pour la rétropropagation (dans le code `slices[:-num_slices]`).\n",
        "\n",
        "Une fois que nous avons combiné toutes les tranches restantes, nous pouvons utiliser [la fonction `combine\\_slices`](/docs/api/qiskit-addon-utils/slicing#combine_slices) pour générer l'[QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) à profondeur réduite. Nous allons ensuite examiner le nombre de tranches ayant fait l'objet d'une rétropropagation par rapport à l'augmentation de la taille de notre observable.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "7c42256f-e7f2-40ed-999e-6a51fd9351c4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 12 circuit slices.\n",
            "New observable contains 29 terms and 9 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "# Recombine the slices remaining after backprop with the rest of the original circuit\n",
        "reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)\n",
        "\n",
        "print(f\"Backpropagated {num_slices - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae2d383c-c11c-457e-856f-85602f2df306",
      "metadata": {},
      "source": [
        "Les graphiques montrent que nous avons réussi à effectuer la rétropropagation sur les 12 tranches tout en conservant notre observable dans le cadre de 10 groupes de Pauli commutatifs. On constate également que l'utilisation de `num_slices` en combinaison avec `max_error_total` entraîne une répartition des budgets entre les tranches, et que le budget non utilisé est à nouveau reporté sur la tranche suivante. C'est ce qui ressort le plus clairement du graphique **en** haut à droite, puisque le budget est à la fois dépensé et reconstitué tout au long de la rétropropagation.\n",
        "\n",
        "Il convient de noter que cette méthode de répartition de l'erreur a donné les meilleurs résultats (un plus grand nombre de tranches soumises à la rétropropagation), par rapport à la première expérience de ce cahier et à l'exemple qui précède immédiatement celui-ci. Dans tous ces exemples, un « budget d'erreur `.018` » avait été alloué, mais la rétropropagation fonctionnait différemment selon la manière dont ce budget était réparti.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "b0bf5050-2928-41a1-9c3d-c9d79e918f6f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/b0bf5050-2928-41a1-9c3d-c9d79e918f6f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 1)])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5c554774-2986-43a0-bdcd-45eaeb81ef5a",
      "metadata": {},
      "source": [
        "<span id=\"work-with-multiple-observables\" />\n",
        "\n",
        "## Utilisation de plusieurs observables\n",
        "\n",
        "Cette méthode `qiskit_addon_obp.backpropagate` permet de lui transmettre une séquence d'observables. Cela simplifie le déroulement des opérations lorsqu'il s'agit de plusieurs observables cibles.\n",
        "\n",
        "Nous le mentionnons explicitement ici afin de vous expliquer comment la stratégie de troncature gère ce type de cas. Dans le cadre de cet exemple, nous ajoutons un observable supplémentaire à celui que nous avons utilisé jusqu'à présent :\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "0857ea4c-82c1-4641-a268-1a9ba44a312b",
      "metadata": {},
      "outputs": [],
      "source": [
        "obs = [SparsePauliOp(\"IIIIIZIIII\"), SparsePauliOp(\"IIIIIXIIII\")]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ba3ac81c-7395-4287-907a-742a1751cbd0",
      "metadata": {},
      "source": [
        "Pour finir, nous répétons la première expérience de ce tutoriel., mais avec deux observables vers lesquelles effectuer la rétropropagation du circuit.\n",
        "\n",
        "Dans cet exemple, cela n'a aucune incidence sur le nombre de tranches pouvant faire l'objet d'une rétropropagation. On constate toutefois que ces deux observables ont donné lieu à un nombre différent de termes de Pauli et de groupes commutatifs.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "10c50f14-5bbd-427d-ae77-24389498a1e4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)\n"
          ]
        }
      ],
      "source": [
        "truncation_error_budget = setup_budget(max_error_per_slice=0.001)\n",
        "print(truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "862edaec-fc2c-488b-875d-23841ecc80b3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 11 circuit slices.\n",
            "The new first observable contains 29 terms and 10 commuting groups.\n",
            "The new second observable contains 23 terms and 8 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "bp_obs, remaining_slices, metadata = backpropagate(\n",
        "    obs,\n",
        "    slices,\n",
        "    operator_budget=op_budget,\n",
        "    truncation_error_budget=truncation_error_budget,\n",
        ")\n",
        "reduced_circuit = combine_slices(remaining_slices)\n",
        "print(f\"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.\")\n",
        "print(\n",
        "    f\"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")\n",
        "print(\n",
        "    f\"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "467c614d-6bac-4826-a42b-39937b9d0c72",
      "metadata": {},
      "source": [
        "Les graphiques ci-dessous permettent d'illustrer la manière dont l'algorithme de rétropropagation gère plusieurs observables.\n",
        "\n",
        "Tout d'abord, les graphiques en **haut à gauche**, en **haut à droite** et en **bas à gauche** montrent que le budget alloué à la troncature des termes est défini individuellement pour chaque variable observable. En d'autres termes, ces deux observables permettent de tronquer des termes en supposant une erreur de par `0.001` tranche rétropropagée.\n",
        "En raison de la nature différente des observables, cela se traduit par une utilisation différente du budget. Dans cet exemple, on constate de nombreux recoupements, ce qui n'est pas toujours le cas.\n",
        "\n",
        "Le graphique **en bas à droite** montre que `max_qwc_groups` prend en compte *toutes* les grandeurs observables. Cela signifie que les termes de toutes les observables sont regroupés pour obtenir un nombre final de groupes commutatifs au niveau des qubits, qui est ensuite comparé à `max_qwc_paulis`. La même procédure s'applique au seuil `max_paulis` (qui n'est pas abordé dans ce cahier), ce qui permet de fixer une limite au nombre de termes de Pauli pour l'ensemble des observables.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/truncate-operator-terms/extracted-outputs/82271b0d-ddaf-4ad2-b5c6-e9e4c2a4cff7-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(2, 2, figsize=(20, 10))\n",
        "plot_slice_errors(metadata, axes[(0, 0)])\n",
        "plot_left_over_error_budget(metadata, axes[(0, 1)])\n",
        "plot_accumulated_error(metadata, axes[(1, 0)])\n",
        "plot_num_qwc_groups(metadata, axes[(1, 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
}