{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Pauli-Terme während der Rückpropagation abschneiden\"\n",
        "description: \"Kürzung der Pauli-Terme während der Rückpropagation bei der neuesten Version der Operator-Rückpropagation (OBP)\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "11cf5076-2777-499c-be50-531e305bf011",
      "metadata": {},
      "source": [
        "<span id=\"truncate-pauli-terms-during-backpropagation\" />\n",
        "\n",
        "# Pauli-Terme während der Rückpropagation abschneiden\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1453472-3506-4008-b432-f8ef9ccbf3ee",
      "metadata": {},
      "source": [
        "In dieser Anleitung wird erläutert, wie der Mechanismus zur Trunkierung von Pauli-Termen konfiguriert wird, der vom Modul „ [qiskit\\_addon\\_obp.utils.truncating](/docs/api/qiskit-addon-obp/utils-truncating#module-qiskit_addon_obp.utils.truncating) “ bereitgestellt wird.\n",
        "\n",
        "Die Operator-Backpropagation (OBP) kann genutzt werden, um die Tiefe von Quantenschaltungen zu verringern, was jedoch auf Kosten einer komplexeren Observablen geht. Um aussagekräftige Ergebnisse mit OBP zu erhalten, muss man in der Regel Terme aus der Beobachtungsvariablen kürzen, um zu verhindern, dass diese zu groß wird. Eine Möglichkeit, eine tiefere Rückpropagierung in die Schaltung zu ermöglichen und gleichzeitig zu verhindern, dass der Operator zu groß wird, besteht darin, Terme mit kleinen Koeffizienten zu kürzen, anstatt sie zum Operator hinzuzufügen. Das Kürzen von Termen kann zwar dazu führen, dass weniger Quantenschaltungen ausgeführt werden müssen, führt jedoch zu einem gewissen Fehler bei der Berechnung des endgültigen Erwartungswerts, der proportional zur Größe der Koeffizienten der gekürzten Terme ist.\n",
        "\n",
        "Die [Backpropagation-Methode](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) akzeptiert ein optionales [„TruncationErrorBudget“](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget), das die Kürzung von Pauli-Termen mit geringem Gewicht für jede Observable nach der erfolgreichen Backpropagation jedes Slice konfiguriert.\n",
        "Die Anzahl der gekürzten Terme hängt von verschiedenen, vom Benutzer festgelegten Konfigurationsparametern ab.\n",
        "Derzeit steht nur eine einzige Trunkierungsstrategie zur Verfügung – die Methode „[truncate\\_binary\\_search](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search) “.\n",
        "Ausgehend von einem Observablen und einem bestimmten *Budget* führt das Verfahren eine binäre Suche über die Pauli-Terme und Koeffizienten innerhalb dieses Observablen durch, um den optimalen Schwellenwert zu finden,\n",
        "sodass die Summe der abgeschnittenen Koeffizienten maximal ist, aber unter dem Budget liegt.\n",
        "\n",
        "**Hinweis** : Standardmäßig wird zur Bewertung und Begrenzung des Abschneidefehlers die L1 -Norm verwendet; mit der Einstellung `p_norm` kann jedoch die zu verwendende Lp-Norm festgelegt werden.\n",
        "Weitere Informationen zur Verwendung dieser Einstellung finden Sie im Leitfaden „[Verwendung unterschiedlicher Lp-Normen für die Trunkierung des Pauli-Terms](/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm) “.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aaf5f046-6d5e-4b6f-add7-b60604edef30",
      "metadata": {},
      "source": [
        "Die folgenden Beispiele veranschaulichen verschiedene Möglichkeiten, ein „ [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) “ mithilfe der beiliegenden Funktion [„setup\\_budget“](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget) zu erstellen.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c83c0741-4a9e-4f89-92a9-4756ef133130",
      "metadata": {},
      "source": [
        "<span id=\"construct-an-example-circuit\" />\n",
        "\n",
        "## Bauen Sie eine Beispielschaltung auf\n",
        "\n",
        "In dieser Anleitung werden die folgenden Schaltungsabschnitte verwendet:\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": [
        "Wir werden uns eine einzige einfache Observable ansehen:\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",
        "## Der einfachste Fall: ein festes Trunkierungsbudget für jede Scheibe\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e04e5c4-1e6b-4720-9563-becbb9edc4d7",
      "metadata": {},
      "source": [
        "Das für die Trunkierung der Pauli-Terme zur Verfügung stehende Budget kann in jedem Schritt der Rückwärtsausbreitung variieren.\n",
        "Um zu verstehen, wie dies funktioniert, betrachten wir zunächst den einfachsten Fall eines vom Benutzer festgelegten Trunkierungsbudgets.\n",
        "\n",
        "Die einfachste Möglichkeit, das Trunkierungsbudget festzulegen, ist die Verwendung des Arguments `max_error_per_slice` .\n",
        "Genau das wird übrigens im Tutorial „[Reduzierung der Schaltungstiefe mit Operator-Backpropagation](/docs/tutorials/operator-back-propagation) “ behandelt. Wird `float` der Wert auf `max_error_per_slice` gesetzt, wird jedem Segment ein Budget in Höhe dieses Werts zugewiesen.\n",
        "Im folgenden Beispiel setzen wir diesen Wert auf, was `0.001` einen impliziten Rundungsfehler von höchstens garantiert `0.018`, wenn alle 18 Schichten rückwärtspropagiert würden.\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  Beachten Sie, dass ein nach der Rückpropagierung eines Schnitts und dem Weglassen von Termen mit kleinen Koeffizienten verbleibendes Fehlerbudget stets dem Fehlerbudget des folgenden Schnitts hinzugefügt wird.\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": [
        "Anschließend nutzen wir die [OBPMetadata-](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata) Instanz und die vom [Visualisierungsmodul](/docs/api/qiskit-addon-obp/utils-visualization#module-qiskit_addon_obp.utils.visualization) bereitgestellten Werkzeuge, um den Backpropagation-Prozess zu visualisieren.\n",
        "\n",
        "* **Die Grafik oben links** zeigt, dass wir über genügend Rechenkapazität verfügen, um nach der Rückpropagierung des dritten Abschnitts mit der Kürzung der beobachtbaren Terme zu beginnen. Ab dem dritten Abschnitt wissen wir, dass wir bei jedem Abschnitt, den wir rückwärtsverbreiten, mindestens einen Term abschneiden, da nach jedem Abschnitt ein gewisser Abschneidefehler auftritt.\n",
        "* **Die Grafik oben rechts** zeigt, dass das Fehlerbudget für `.003` den dritten Ausschnitt auf anstieg. Wir stellen einen starken Rückgang des verbleibenden Budgets fest, was bedeutet, dass bestimmte Zeiträume aus den beobachtbaren Daten herausgeschnitten wurden. Dies stimmt mit dem überein, was wir aus der Grafik oben links abgeleitet haben.\n",
        "* **Die Grafik unten links** zeigt, dass unser insgesamt kumulierter Fehler monoton ansteigt, wenn wir Terme aus unserer Observablen weglassen. Diese Grafik bestätigt zudem, dass erst nach der Rückpropagierung des dritten Segments Terme abgeschnitten wurden.\n",
        "* **Die Grafik unten rechts** zeigt, dass die Anzahl der pendelnden Pauli-Gruppen in unserer Observable auf den angegebenen Grenzwert von `10`… angestiegen ist. Diese Grafik zeigt zudem, wie die Rückpropagierung einer weiteren Schicht dazu führen würde, dass unsere Beobachtungsgröße die festgelegte Grenze überschreitet, wie aus dem Schnittpunkt der schwarzen und roten Linien ersichtlich ist.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8ff436fa-3b52-4387-a9f4-d8d9df13c480",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\">\n",
        "  Beachten Sie, dass in allen diesen Diagrammen die x-Achse die rückwärtspropagierten Slices auflistet; da OBP jedoch am Ende der Schaltung arbeitet, `slice 2` ist `slice 1` das allerletzte Slice, das davorliegende und so weiter.\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",
        "## Das Slice-Budget explizit festlegen\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "80f9337d-ea37-454c-ae0b-b101259568c9",
      "metadata": {},
      "source": [
        "Wenn Sie wissen, wie Sie jedem Slice ein Budget zuweisen können, sodass die Backpropagation-Leistung optimiert wird, sollten Sie jedem Slice explizit ein Budget zuweisen. Zu Demonstrationszwecken weisen wir den ersten drei Schnitten ein Budget von Null zu und verwenden für die übrigen Schnitte ein Budget von pro `.001` Schnitt.\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": [
        "Durch das Entfernen des Budgets aus den ersten drei Schichten wurden erst ab der vierten Schicht keine Terme mehr abgeschnitten, wie aus drei dieser Diagramme hervorgeht. Etwas überraschend mag sein, dass, obwohl dem vierten Segment kein Restbudget zugewiesen wurde, dennoch ein Term durch die Verwendung des zugewiesenen Budgets `.001` gekürzt wurde. Dies geht aus der Grafik **oben links** hervor, lässt sich aber auch in der Grafik **oben rechts** beobachten, da sich die Kurve des verbleibenden Budgets zwischen den Schnitten drei und vier abflacht. Ein weiteres bemerkenswertes Detail ist, dass ab diesem Punkt mindestens ein Term abgeschnitten wurde, genau wie im vorherigen Beispiel.\n",
        "\n",
        "Die wichtigste Erkenntnis ist, dass wir im zweiten Beispiel zwar aufgrund der Nullsetzung der Budgets einiger Slices einen geringeren Trunkierungsfehler hatten, aber dennoch die gleiche Anzahl von Slices rückwärts propagieren konnten und unser Observabil enthält die gleiche Anzahl von kommutierenden Pauli-Gruppen. Anhand der Grafik **unten links** können wir bestätigen, dass die Fehlergrenze im zweiten Beispiel kleiner ist.\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",
        "## Das Budget zyklisch festlegen\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62f0ea1f-eaec-408d-99df-25a8647c1eb9",
      "metadata": {},
      "source": [
        "Wenn Sie eine Schaltung mit einem sich wiederholenden Muster haben, wie beispielsweise eine Trotter-Schaltung, möchten Sie möglicherweise ein Budget für diese sich wiederholende Teilmenge von Slices festlegen und dieses Budget für alle nachfolgenden Wiederholungen dieser Slices verwenden lassen.\n",
        "\n",
        "Genauer gesagt besteht die von uns verwendete Beispielschaltung aus sechs Segmenten, die dreimal wiederholt werden, was insgesamt 18 Segmente ergibt. Wir weisen den Einzel-Qubit- und den Schichten `RYY` willkürlich ein Budget von Null zu und jeder der Schichten `RXX` ein Budget `.003` von zu. Wir werden beobachten, wie die Festlegung des Budgets als „ length-6 “-Sequenz dazu führt, dass das Budget zyklisch auf alle 18 Slices angewendet wird.\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  Beachten Sie auch hier, dass die Schichten in umgekehrter Reihenfolge (d. h. beginnend am Ende) rückwärts propagiert werden. Daher wird der erste Eintrag in unserem zyklischen Budget tatsächlich für das letzte Segment verwendet, der zweite Eintrag für das Segment davor und so weiter.\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": [
        "Wie auf den Abbildungen **oben links** und **unten links** zu sehen ist, wurde bei den ersten vier Schichten keine Kürzung vorgenommen, da ihnen kein Fehlerbudget zugewiesen wurde. Bei den Tranchen fünf und sechs wurden einige der Laufzeiten gekürzt, sobald Mittel zur Verfügung standen.\n",
        "\n",
        "Wie auf dem Bild **oben links** zu sehen ist, entstand nach der Rückpropagierung von Schnitt sieben ein relativ großer Fehler, obwohl für diesen Schnitt kein Budget zugewiesen worden war. Das liegt daran, dass nur etwa `.002` des für die Segmente fünf und sechs vorgesehenen `.006` Gesamtbudgets verwendet wurde; der Restbetrag wurde daher auf Segment sieben übertragen und größtenteils ausgegeben, wie in der Abbildung **oben rechts** zu sehen ist.\n",
        "\n",
        "Die Abbildungen **oben links** und **oben rechts** zeigen, dass der geringe Restbetrag des Budgets zwischen den Abschnitten 8 und 10 aufgebraucht wird und in Abschnitt 11, wie erwartet, neues Budget zur Verfügung steht. Die Abbildungen **oben links**, **oben rechts** und **unten links** veranschaulichen alle das zyklische Verhalten des Arguments `max_error_per_slice` , wenn dessen Länge kleiner ist als die Anzahl der Segmente. Dieses zyklische Verhalten hätte sich über alle Schichten hinweg fortgesetzt, doch das `max_qwc_groups` Abbruchkriterium wurde nach der Rückpropagierung von 13 Schichten erreicht, wie auf dem Bild **unten rechts** zu sehen ist.\n",
        "\n",
        "Interessant ist auch, dass die Anzahl der Pauli-Gruppen tatsächlich abnimmt, nachdem in Slice 11 die zweite Budgetrunde verfügbar wird. Das liegt daran, dass es Gruppen mit kleinen Koeffizienten gab, die erst gekürzt werden konnten, als nach der Rückpropagierung von Slice 11 mehr Budget zur Verfügung stand; daher sammelten sie sich über mehrere Iterationen hinweg in der Observablen an. Dieser Fall verdeutlicht zudem, dass *überschritten* werden `max_qwc_groups` muss, damit der Algorithmus beendet wird.\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",
        "## Den Gesamtfehler begrenzen\n",
        "\n",
        "Neben der Festlegung des Fehlerbudgets pro Slice kann man auch den maximalen Fehler festlegen, der durch die Abschneidung entstehen darf. Sobald diese Grenze erreicht ist, wird keine weitere Kürzung mehr vorgenommen; die Rückpropagation wird jedoch fortgesetzt, bis die Beobachtungsgröße zu groß wird und eines der Abbruchkriterien erfüllt ist.\n",
        "\n",
        "Anschließend wiederholen wir das obige Experiment und legen eine Obergrenze für fest, `max_error_total` sodass das Fehlerbudget nach der Rückpropagierung von Slice sieben aufgebraucht ist.\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": [
        "Wie erwartet erreicht unser Abschneidefehler seinen Höchstwert bei `.006` (Diagramm **unten links** ). Bemerkenswert ist, dass wir bei diesem Durchlauf nicht in der Lage waren, die Rückpropagierung für Slice 11 durchzuführen. Das liegt daran, dass wir nicht über genügend Budget verfügten, um Terme zu kürzen, und die Anzahl der pendelnden Pauli-Gruppen die Grenze von überschritten hat `20`, wie auf dem Bild **unten rechts** zu sehen ist.\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": [
        "Es kann wünschenswert sein, das Gesamtbudget einfach auf zu begrenzen, ohne `max_error_total` genauer anzugeben `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": [
        "Die Ausgabe der obigen Zelle mag etwas überraschend sein, da auf gesetzt ist `per_slice_budget``max_error_total` . Dies deutet darauf hin, dass das gesamte verfügbare Budget **gierig** aufgebraucht wird.\n",
        "Man kann es sich so vorstellen: Jedem Segment steht das gesamte Budget zur Verfügung (da wir eine Schleife über die durchlaufen `per_slice_budget`). Jedoch wird jedes bereits verbrauchte Budget von dem Budget abgezogen, das zu diesem bestimmten Zeitpunkt im Algorithmus noch zur Verfügung steht.\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": [
        "Das Bild **oben rechts** zeigt, wie das gesamte `.018` Fehlerbudget dem ersten Schnitt zur Verfügung gestellt wird. Bis zum dritten Abschnitt erfolgt keine Kürzung, sodass das verbleibende Budget konstant bleibt. Das Budget nimmt monoton ab, da jedem rückwärtspropagierten Segment das gesamte Budget zur Verfügung gestellt wird, bis es aufgebraucht ist.\n",
        "\n",
        "Bemerkenswert ist, dass bei diesem Experiment zwei zurückpropagierte Schichten weniger erzielt wurden als beim ersten Experiment in diesem Notizbuch, das nahezu identisch ist. Dies zeigt, dass bei manchen Problemen eine gleichmäßige Verteilung des Budgets möglicherweise optimal ist. Bei anderen Problemen kann es zu einer besseren Leistung führen, wenn man den Slices erlaubt, das gesamte Budget gierig auszuschöpfen.\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",
        "## Die Anzahl der rückwärtspropagierten Schichten und den Gesamtfehler gemeinsam begrenzen\n",
        "\n",
        "Wenn Sie das Fehlerbudget nicht über die gesamte Schaltung verteilen, es aber auch nicht zu großzügig verbrauchen möchten, können Sie die Anzahl der Slices, die Sie rückwärtsverbreiten möchten (`num_slices`), zusammen mit einem Gesamtfehlerbudget (`max_error_total`) angeben. Dadurch wird das Fehlerbudget gleichmäßig (gemäß `p_norm`) auf die Eingangs-Slices verteilt.\n",
        "\n",
        "Hier werden wir die Anzahl der Schichten, die wir rückwärtsverbreiten `12`, auf begrenzen und das gesamte Fehlerbudget beibehalten.\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": [
        "Nun werden wir versuchen, die 12 Slices, für die wir im vorherigen Schritt ein gewisses Budget zugewiesen haben, rückwärts zu propagieren. Dazu übergeben wir einfach die letzten 12 Segmente an unsere Schaltung. Mit `p_norm=1`verfügt jedes der 12 Segmente über ein Budget von `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": [
        "Da wir eine Teilmenge unserer Slices (im Code `slices[-num_slices:]`) an übergeben haben `backpropagate`, müssen wir die nach der Rückpropagation verbleibenden Slices mit den Slices kombinieren, die nie zur Rückpropagation gesendet wurden (im Code ) `slices[:-num_slices]`.\n",
        "\n",
        "Sobald wir alle verbleibenden Slices zusammengefügt haben, können wir mit [`combine\\_slices`](/docs/api/qiskit-addon-utils/slicing#combine_slices) den „ [QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) “ mit reduzierter Tiefe erzeugen. Als Nächstes untersuchen wir, wie viele Schichten rückwärtsverbreitet wurden und wie groß unsere Beobachtungsgröße dabei geworden ist.\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": [
        "Die Diagramme zeigen, dass es uns gelungen ist, alle 12 Schichten erfolgreich rückwärts zu propagieren und dabei unsere Observable unter 10 kommutierenden Pauli-Gruppen zu halten. Wir können außerdem feststellen, dass die Verwendung von `num_slices` zusammen mit zu einer Verteilung der Budgets auf die einzelnen Abschnitte `max_error_total` führt und nicht verwendete Mittel wiederum auf den nächsten Abschnitt übertragen werden. Am deutlichsten wird dies in der Grafik **oben rechts**, da das Budget während der Backpropagation sowohl aufgebraucht als auch wieder aufgefüllt wird.\n",
        "\n",
        "Bemerkenswert ist, dass diese Methode der Fehlerverteilung im Vergleich zum ersten Experiment in diesem Notizbuch und zum unmittelbar vorangegangenen Beispiel die besten Ergebnisse lieferte (mehr rückwärtspropagierte Schichten). In all diesen Beispielen wurde ein `.018` Fehlerbudget zugewiesen, doch die Backpropagation verlief je nach Verteilung dieses Budgets unterschiedlich.\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",
        "## Arbeiten mit mehreren Observables\n",
        "\n",
        "Mit dieser Methode `qiskit_addon_obp.backpropagate` kann man eine Folge von Observables übergeben. Dies vereinfacht den Arbeitsablauf beim Umgang mit mehreren Zielgrößen.\n",
        "\n",
        "Wir weisen hier ausdrücklich darauf hin, um Ihnen zu verdeutlichen, wie die Trunkierungsstrategie in einem solchen Fall vorgeht. Für dieses Beispiel fügen wir dem bisher verwendeten Observable ein weiteres hinzu:\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": [
        "Zum Schluss wiederholen wir das erste Experiment aus diesem Tutorial., allerdings mit zwei Observablen, in die die Rückpropagierung der Schaltung erfolgen soll.\n",
        "\n",
        "In diesem Beispiel hat dies keinen Einfluss auf die Anzahl der Schichten, die rückwärtsverbreitet werden könnten. Es zeigt sich jedoch, dass die beiden Observablen zu einer unterschiedlichen Anzahl von Pauli-Termen und kommutierenden Gruppen führten.\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": [
        "Die folgenden Grafiken veranschaulichen, wie der Backpropagation-Algorithmus mit mehreren Beobachtungsgrößen umgeht.\n",
        "\n",
        "Zunächst zeigen die Diagramme **oben links**, **oben rechts** und **unten links**, dass das Budget für das Abschneiden von Termen für jede Beobachtungsgröße individuell festgelegt wird. Mit anderen Worten: Beide Observablen können Terme unter der Annahme eines Fehlers von pro `0.001` rückwärtsverbreitetem Slice abschneiden.\n",
        "Aufgrund der unterschiedlichen Beschaffenheit der Beobachtungsgrößen führt dies zu einer unterschiedlichen Inanspruchnahme des Budgets. In diesem Beispiel überschneiden sie sich stark, was jedoch nicht immer der Fall ist.\n",
        "\n",
        "Die Grafik **unten rechts** verdeutlicht, dass *alle* Beobachtungsgrößen `max_qwc_groups` berücksichtigt. Das bedeutet, dass die Terme aller Observablen zusammengefasst werden, um eine endgültige Anzahl von qubit-weisen kommutierenden Gruppen zu erhalten, die dann mit verglichen wird `max_qwc_paulis`. Das Gleiche gilt für den Schwellenwert `max_paulis` (der in diesem Notizbuch nicht behandelt wird), mit dem Sie eine Obergrenze für die Anzahl der Pauli-Terme über alle Observablen hinweg festlegen können.\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
}