{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"バックプロパゲーション中のパウリ項の切り捨て\"\n",
        "description: \"最新バージョンのオペレーターバックプロパゲーション（OBP）におけるバックプロパゲーション中のパウリ項の切り捨て\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "11cf5076-2777-499c-be50-531e305bf011",
      "metadata": {},
      "source": [
        "<span id=\"truncate-pauli-terms-during-backpropagation\" />\n",
        "\n",
        "# バックプロパゲーション中のパウリ項の切り捨て\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1453472-3506-4008-b432-f8ef9ccbf3ee",
      "metadata": {},
      "source": [
        "このガイドでは、 [qiskit\\_addon\\_obp.utils.truncating](/docs/api/qiskit-addon-obp/utils-truncating#module-qiskit_addon_obp.utils.truncating) モジュールが提供するパウリ項の切り捨てメカニズムの設定方法について説明します。\n",
        "\n",
        "演算子バックプロパゲーション（OBP）を用いることで、観測量の複雑さを増すことを代償として、量子回路の深さを減らすことができる。 OBPから有意義な結果を得るためには、通常、観測可能変数から項を切り捨て、その規模が過度に大きくなるのを防ぐ必要があります。 演算子のサイズが過度に大きくなるのを防ぎつつ、回路内でのバックプロパゲーションをより深く行うための方法の一つは、係数の小さい項を演算子に加えるのではなく、それらを切り捨てることです。 項を切り捨てると、実行すべき量子回路の数が減る可能性があるが、その一方で、最終的な期待値の計算において、切り捨てられた項の係数の大きさに比例する誤差が生じる。\n",
        "\n",
        "[バックプロパゲーション](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate)手法では、オプションの [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) を受け取ります。これは、各スライスのバックプロパゲーションが正常に完了した後、各観測量について重みの小さいパウリ項の切り捨てを設定するものです。\n",
        "切り捨てられる項の数は、ユーザーが指定したさまざまな設定パラメータによって決まります。\n",
        "現時点では、利用可能な切り捨て戦略は「 [truncate\\_binary\\_search](/docs/api/qiskit-addon-obp/utils-truncating#truncate_binary_search) 」メソッドのみです。\n",
        "ある観測量と*予算*が与えられると、その観測量に含まれるパウリ項および係数に対して二分探索を行い、\n",
        "切り捨てられた係数の和が最大となるが、予算を下回るような最適な閾値を見つけ出します。\n",
        "\n",
        "**注** ：デフォルトでは、切り捨て誤差の評価および上限の算出には L1 ノルムが使用されますが、この設定 `p_norm` を有効にすると、使用するLpノルムを指定できるようになります。\n",
        "この設定の使用方法の詳細については、「 [パウリ項の切り捨てに異なるLpノルムを使用する](/docs/addons/qiskit-addon-obp/guides/bound-error-using-p-norm)」 ガイドを参照してください。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "aaf5f046-6d5e-4b6f-add7-b60604edef30",
      "metadata": {},
      "source": [
        "以下の例は、付属のsetup [\\_budget](/docs/api/qiskit-addon-obp/utils-truncating#setup_budget) 関数を使用して [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) を構築するさまざまな方法を示しています。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c83c0741-4a9e-4f89-92a9-4756ef133130",
      "metadata": {},
      "source": [
        "<span id=\"construct-an-example-circuit\" />\n",
        "\n",
        "## 例となる回路を組み立てる\n",
        "\n",
        "このガイドでは、以下の回路スライスを使用しています：\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": [
        "ここでは、1つの単純な観測量について見ていきます：\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",
        "## 最も単純なケース：各スライスごとに固定の切り捨て予算を設定する場合\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5e04e5c4-1e6b-4720-9563-becbb9edc4d7",
      "metadata": {},
      "source": [
        "パウリ項の切り捨てに割り当てられる予算は、バックプロパゲーションの各ステップごとに異なる場合があります。\n",
        "この仕組みを理解するために、まず、ユーザーが指定した固定の切り捨て予算という最も単純なケースについて見ていきます。\n",
        "\n",
        "切り捨ての許容範囲を指定する最も簡単な方法は、\\`\\`引数 `max_error_per_slice` を使用することです。\n",
        "実際、「 [演算子によるバックプロパゲーションを用いた回路の深さの削減](/docs/tutorials/operator-back-propagation)」 というチュートリアルでは、まさにこの手法が用いられています。 を `max_error_per_slice` に設定すると `float` 、各スライスにその値に等しい予算が割り当てられます。\n",
        "以下の例では、この値を に設定しています。 `0.001` これにより、18個のスライスすべてに対してバックプロパゲーションが行われた場合 `0.018`、暗黙の切り捨て誤差が 以下に抑えられることが保証されます。\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  なお、スライスのバックプロパゲーションを行い、係数の小さい項を切り捨てた後に残った誤差許容範囲は、常に次のスライスの誤差許容範囲に加算されることに注意してください。\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": [
        "次に、 [OBPMetadata](/docs/api/qiskit-addon-obp/utils-metadata-obp-metadata) インスタンスと[可視化](/docs/api/qiskit-addon-obp/utils-visualization#module-qiskit_addon_obp.utils.visualization)モジュールが提供するツールを使用して、バックプロパゲーションのプロセスを可視化します。\n",
        "\n",
        "* **左上のグラフから、** 3番目のスライスのバックプロパゲーションを終えた時点で、観測可能な項の切り捨てを開始するのに十分な予算があることがわかります。 3番目のスライスからは、各スライスごとにバックプロパゲーションを行う際、少なくとも1つの項が切り捨てられることが分かっています。これは、各スライスの後に何らかの切り捨て誤差が生じているためです。\n",
        "* **右上のグラフは、** 3番目のスライス `.003` において誤差予算が まで増加することを示している。 残りの予算が急激に減少していることが確認され、これは観測データから条件が切り捨てられたことを意味します。 これは、左上の図から推測した内容と一致しています。\n",
        "* **左下のグラフは**、観測可能な項を順次切り捨てていくにつれて、累積誤差が単調増加していることを示しています。 このグラフからも、3番目のスライスのバックプロパゲーションが完了するまでは、どの項も切り捨てられていなかったことが確認できる。\n",
        "* **右下の図から**、我々の観測量におけるコミューティング・パウリ群の数が、指定された限界値である まで増加していることがわかる `10`。 このグラフからは、黒線と赤線が交差しているところからもわかるように、バックプロパゲーションをもう1層行うと、観測値が指定された範囲を超えてしまうことも示されています。\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8ff436fa-3b52-4387-a9f4-d8d9df13c480",
      "metadata": {},
      "source": [
        "<div class=\"alert alert-info\">\n",
        "  なお、これらのグラフすべてにおいて、x軸はバックプロパゲーションされたスライスを列挙したものですが、OBPは回路の末端で動作するため、 `slice 1` は最も最後のスライス、 はその前のスライス、というように続きます `slice 2` 。\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",
        "## スライスの予算を明示的に指定する\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "80f9337d-ea37-454c-ae0b-b101259568c9",
      "metadata": {},
      "source": [
        "バックプロパゲーションのパフォーマンスが最適化されるように、各スライスに予算を割り当てる方法をご存知であれば、各スライスに明示的に予算を割り当てることを検討するとよいでしょう。 説明のために、最初の3つのスライスには予算をゼロとし、残りのスライスにはスライス `.001` ごとに の予算を割り当てます。\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": [
        "最初の3つのレイヤーから予算を削除したところ、4番目のレイヤー以降になるまで、どの用語も切り捨てられることはなかった。これは、これらのプロットのうち3つで確認できる。 少々意外に思えるかもしれないのは、4番目のスライスには残りの予算が割り当てられていなかったにもかかわらず、割り当てられた予算 `.001` が使用された結果、1つの期間が切り詰められてしまったという点だ。 これは**左上の**グラフから明らかですが、 **右上の**グラフでも、スライス3と4の間で残りの予算曲線が横ばいになっていることから確認できます。 もう1つ注目すべき点は、前の例と同様に、この時点から少なくとも1つの項が切り捨てられたことです。\n",
        "\n",
        "重要なポイントは、2番目の例では一部のスライスの予算をゼロにしたため切り捨て誤差は少なくなったものの、バックプロパゲーションの対象となるスライスの数は同じであり、観測可能量に含まれるコミューティング・パウリ群の数も同様であったということです。 **左下の**グラフを確認することで、2番目の例の方が誤差の上限が小さいことが確認できます。\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",
        "## 予算を周期的に設定する\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "62f0ea1f-eaec-408d-99df-25a8647c1eb9",
      "metadata": {},
      "source": [
        "トロッター回路など、繰り返しパターンがある回路の場合、その繰り返しされるスライスのサブセットに対して予算を指定し、その後のそのスライスのすべての繰り返しに対してその予算が適用されるようにしたい場合があります。\n",
        "\n",
        "より具体的に言えば、今回使用している例示回路には6つのスライスが3回繰り返されており、合計で18個のスライスがあります。 単一量子ビット層および各層 `RYY` には、予算をゼロと任意に設定し、各層 `RXX` にはそれぞれ予算 `.003` を割り当てます。 予算を length-6 のシーケンスとして指定すると、18枚すべてのスライスに予算が周期的に適用される様子を確認します。\n",
        "\n",
        "<div class=\"alert alert-info\">\n",
        "  繰り返しになりますが、スライスは逆順（つまり、最後から順に）でバックプロパゲーションが行われる点に注意してください。 したがって、循環予算の最初のエントリは実際には最後のスライスに割り当てられ、2番目のエントリはその前のスライスに割り当てられ、というように続きます。\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": [
        "**左上**および**左下の**画像に見られるように、最初の4つのスライスにはエラーバジェットが割り当てられていなかったため、切り捨ては行われませんでした。 第5および第6のスライスでは、予算が確保されたことに伴い、一部の用語が省略されました。\n",
        "\n",
        "**左上の**画像に見られるように、スライス7には予算が割り当てられていなかったにもかかわらず、そのスライスのバックプロパゲーション後に比較的大きな誤差が生じました。 これは、スライス5と6に割り当てられた `.006` 総予算のうち `.002` 、約 しか使用されなかったため、残りの部分がスライス7に振り替えられ、 **右上の**画像にあるように、その大部分が支出されたためです。\n",
        "\n",
        "**左上**と**右上の**画像から、わずかに残っていた予算がスライス8～10の間に使い切られ、予想通りスライス11で新たな予算が利用可能になることがわかります。 **左上**、 **右上**、および**左下の**図はすべて、引数の長さがスライスの数より少ない場合の、その引 `max_error_per_slice` 数の周期的な挙動を示しています。 この周期的な挙動はすべてのスライスにわたって続いたはずですが、 **右下の**画像にあるように、13スライスのバックプロパゲーションを行った後に停止 `max_qwc_groups` 基準に達しました。\n",
        "\n",
        "また興味深いのは、スライス11で第2ラウンドの予算が確保された後、パウリ群の数が実際に減少することだ。 これは、スライス11のバックプロパゲーション後に予算が増えるまで切り捨てることができなかった係数の小さいグループが存在したため、それらが数回の反復にわたって観測可能量に蓄積されたからである。 また、この事例は、アルゴリズムが終了するためには、が\\*超え \\*`max_qwc_groups` なければならないことを浮き彫りにしている。\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",
        "## エラーの合計に上限を設ける\n",
        "\n",
        "スライスごとの誤差許容範囲を指定することに加え、切り捨てによって生じる誤差の最大許容値も指定することができます。 その制限に達すると、それ以上の切り捨ては行われませんが、観測値が過大になり、停止基準のいずれかが満たされるまで、バックプロパゲーションは継続されます。\n",
        "\n",
        "次に、スライス7のバックプロパゲーション後にエラーバジェットが使い果たされる `max_error_total` ように、に上限を設定して、上記の実験を再度実行する。\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": [
        "予想通り、切り捨て誤差は（ `.006`\\*\\* 左下の\\*\\*グラフ）で上限に達しています。 今回の実行では、スライス11のバックプロパゲーションを行うことができなかった点が注目に値する。 これは、項を切り捨てるための予算が足りず、 **右下の**図に示されているように `20`、通勤するパウリ群の数がの限界を超えてしまったためです。\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": [
        "全体予算に上限を設けるだけで、具体的な金額を指定しない `max_error_total` 方が望ましい場合もある `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": [
        "上のセルの出力は、 が に設定 `per_slice_budget` されているため、少し意外に思えるかもしれません `max_error_total`。 これは、利用可能な予算の全額が**貪欲に**使い尽くされることを示している。\n",
        "次のように考えてみてください。各スライスには予算全体が割り当てられます（ because we loop over the `per_slice_budget`）。 ただし、すでに消費済みの予算は、アルゴリズムのその時点で利用可能な予算から差し引かれます。\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": [
        "**右上の**図は、 `.018` エラーバジェット全体が最初のスライスに割り当てられる様子を示しています。 3番目のスライスまでは切り捨てが行われないため、残りの予算は一定のままとなります。 予算は、バックプロパゲーションされた各スライスに全予算が割り当てられ、それが使い果たされるまで、単調に減少していきます。\n",
        "\n",
        "注目すべきは、この実験では、このノートブック内の最初の実験（ほぼ同一の内容）と比較して、バックプロパゲーションが適用されたスライスの数が2つ少なかったという点である。 これは、問題によっては、予算を均等に配分することが最適である可能性があることを示している。 その他の問題については、スライスが予算を貪欲に使い切ることを許可した方が、パフォーマンスが向上する可能性があります。\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",
        "## バックプロパゲーションの対象となるスライスの数と総誤差を合わせて上限を設定する\n",
        "\n",
        "エラーバジェットを回路全体に分散させたくないが、かといって無駄に消費したくもない場合は、バックプロパゲーションを行うスライスの数 (`num_slices`) と、総エラーバジェット (`max_error_total`) を指定することができます。これにより、エラーバジェットは（それに応じて `p_norm`）入力スライス全体に均等に分散されます。\n",
        "\n",
        "ここでは、バックプロパゲーションの対象となるスライスの数を で上限設定し `12`、総誤差予算はそのまま維持します。\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": [
        "それでは、前のステップで予算を割り当てた12のスライスについて、バックプロパゲーションを試みてみましょう。 これを行うには、回路の最後の12スライスをそのまま渡すだけです。 の場合 `p_norm=1`、12のスライスそれぞれに、利用可能な予算が があります `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": [
        "（コード内の `slices[-num_slices:]`）スライスの一部を に渡したため `backpropagate`、バックプロパゲーション後に残ったスライスと、バックプロパゲーション用に送信されなかったスライス（コード内の ） `slices[:-num_slices]`とを結合する必要があります。\n",
        "\n",
        "残りのスライスをすべて結合したら、 [combine\\_slices](/docs/api/qiskit-addon-utils/slicing#combine_slices) を使用して、深さが削減された [QuantumCircuit](/docs/api/qiskit/qiskit.circuit.QuantumCircuit) を生成できます。 次に、バックプロパゲーションが行われたスライスの数と、観測可能変数の大きさがどのように変化したかを調べます。\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": [
        "これらのプロットから、観測可能量を10個の可換パウリ群以内に収めつつ、12個のスライスすべてに対してバックプロパゲーションを正常に実行できたことがわかります。 また、とを併用 `num_slices` すると、各スライスに予算が配分 `max_error_total` され、未使用の予算は再び次のスライスに繰り越されることもわかります。 これは**右上の**グラフで最も顕著に見られ、バックプロパゲーションの過程を通じて、予算が消費されると同時に補充されていることがわかります。\n",
        "\n",
        "注目すべきは、この誤差の分散方法が、このノートブックでの最初の実験や、この直前の例と比較して、最良の結果（バックプロパゲーションされたスライスの数が多い）をもたらした点である。 これらの例はすべて `.018` エラーバジェットを割り当てていましたが、そのバジェットの配分方法によって、バックプロパゲーションの挙動は異なっていました。\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",
        "## 複数のオブザーバブルを扱う\n",
        "\n",
        "このメソッド `qiskit_addon_obp.backpropagate` では、オブザーバブルのシーケンスを引数として渡すことができます。 これにより、複数のターゲットオブザーバブルを扱う際のワークフローが簡素化されます。\n",
        "\n",
        "ここでは、切り捨て戦略がこのようなケースをどのように処理するかを説明するために、あえてこの点について言及しています。 この例のために、これまで使用してきたオブザーバブルに、もう1つオブザーバブルを追加します：\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": [
        "最後に、このチュートリアルの最初の実験をもう一度行います しかし、バックプロパゲーションを行う対象となる観測量が2つある。\n",
        "\n",
        "この例では、これはバックプロパゲーションの対象となるスライスの数には影響しません。 しかし、これら2つの観測量では、パウリ項の数や可換群が異なっていることがわかる。\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": [
        "以下の図は、バックプロパゲーションアルゴリズムが複数の観測変数をどのように扱うかを示すものです。\n",
        "\n",
        "まず、 **左上**、 **右上**、および**左下の**グラフから、項を切り捨てるための予算が、観測変数ごとに個別に設定されていることがわかります。 つまり、どちらの観測量も、バックプロパゲーションされたスライス `0.001` ごとに の誤差があると仮定して、項を切り捨てる能力を持っている。\n",
        "観測可能な項目の性質が異なるため、その結果、予算の消費量も異なってきます。 この例では、両者に多くの重複が見られますが、必ずしもそうなるわけではありません。\n",
        "\n",
        "**右**下の図は、が*すべての*観測量を考慮に入 `max_qwc_groups` れていることを示している。 つまり、すべての観測可能量の項をまとめて、クビットごとの可換群の最終的な個数を算出し、それを と比較する `max_qwc_paulis`。 しきい `max_paulis` 値についても同様の処理が行われます（このノートブックでは詳しく説明しません）。これにより、すべての観測量にわたるパウリ項の数を制限することができます。\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
}