{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "frontmatter",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"파울리 항 절단을 위해 서로 다른 Lp-노름 사용\"\n",
        "description: \"최신 버전의 연산자 역전파(OBP)에서 파울리 항 절단을 위해 서로 다른 Lp-노름을 사용하십시오\"\n",
        "---\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8a7c9df5-d836-4ecb-8aec-8a1f2faa017a",
      "metadata": {},
      "source": [
        "<span id=\"use-different-lp-norms-for-pauli-term-truncation\" />\n",
        "\n",
        "# 파울리 항 절단을 위해 서로 다른 Lp-노름 사용\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5958e635-64c1-4599-82ca-225b83312906",
      "metadata": {},
      "source": [
        "**참고:** 이 가이드를 읽기 전에, 지정된 중량 임계값( [TruncationErrorBudget](/docs/api/qiskit-addon-obp/utils-truncating#truncationerrorbudget) )을 기반으로 [역전파](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) (backpropagate) 방법에 내장된 저중량 파울리 항의 절단(truncation)에 대해 설명하는 ‘파울리 항 절단( [Truncate Pauli terms](/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms) )’ 가이드를 먼저 읽어보시기 바랍니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2780c587-cd2a-4398-8faa-de5e0c661a10",
      "metadata": {},
      "source": [
        "이 가이드에서는, 잘린 파울리 항으로 인해 발생하는 오차를 추정하는 데 사용되는 Lp-노름을 변경하는 데 활용할 수 있는 [‘backpropagate’](/docs/api/qiskit-addon-obp/qiskit-addon-obp#backpropagate) 키워드 인자에 대해 알아보게 됩니다 `p_norm`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "872aeae7-0312-41db-8ebb-f7b02838b54d",
      "metadata": {},
      "source": [
        "<span id=\"construct-an-example-circuit\" />\n",
        "\n",
        "## 예제 회로를 구성해 보세요\n",
        "\n",
        "이 가이드에서는 [‘파울리 항](/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms) 절단’ 가이드와 동일한 예제 회로를 사용합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "d6f281d4-706e-41ad-b7b5-bc7ebe106996",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/d6f281d4-706e-41ad-b7b5-bc7ebe106996-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",
        "num_qubits = 10\n",
        "linear_chain = rustworkx.generators.path_graph(num_qubits)\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 only, 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": "7a7c7299-0c82-4279-b3dc-4f39e8dddd7e",
      "metadata": {},
      "source": [
        "총 자화량에 대한 관측량을 정의합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "ea9e7adc-b003-4762-a889-10e8d84a63d5",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.quantum_info import SparsePauliOp\n",
        "\n",
        "obs = SparsePauliOp.from_sparse_list(\n",
        "    [(\"Z\", [i], 1.0) for i in range(num_qubits)], num_qubits=num_qubits\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "423150d8-80d2-4b80-9665-033808d30272",
      "metadata": {},
      "source": [
        "참고로, 정확한 기대값을 계산해 보겠습니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "4dc72426-2eb5-4f8d-8bba-79650da06b54",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "9.318197859862146\n"
          ]
        }
      ],
      "source": [
        "from qiskit.primitives import StatevectorEstimator\n",
        "\n",
        "estimator = StatevectorEstimator()\n",
        "job = estimator.run([(circuit, obs)])\n",
        "res = job.result()\n",
        "exact_exp = res[0].data.evs\n",
        "print(exact_exp)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d21490c0-e86c-4fe0-af90-c9625813e0c0",
      "metadata": {},
      "source": [
        "<span id=\"use-the-l1-norm\" />\n",
        "\n",
        "## L1 규범을 따르십시오\n",
        "\n",
        "기본적으로, 그리고 [‘파울리 항의](/docs/addons/qiskit-addon-obp/guides/truncate-operator-terms) 절단’ 가이드에서 이미 보셨듯이 `p_norm=1`, 이는 오차가 다음과 같이 추정됨을 의미합니다:\n",
        "\n",
        "$$\n",
        "|\\langle\\psi|\\Delta|\\psi\\rangle| \\leq \\sum_{P\\in\\mathcal{T}} |c_P|\n",
        "$$\n",
        "\n",
        "여기서 $\\psi$ 는 양자 상태이며, $\\Delta$ 는 정확한 관측량과 절단된 관측량 사이의 실제 차이(알 수 없음)이고, $\\mathcal{T}$ 는 절단된 파울리 항들의 집합이며, $c_P$ 는 파울리 항의 계수이다.\n",
        "이 부등식은 대부분의 시나리오에 대해 엄밀하지만 매우 느슨한 상한을 나타냅니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "425293ce-49fb-4bd0-8d2f-44a3a43db967",
      "metadata": {},
      "source": [
        "이 가이드에서는 예제 회로의 6개 슬라이스에 대해, 슬라이스당 오차를 상수 값으로 설정하여 역전파를 수행합니다 `0.001`. 이 수치는 주어진 범위 내의 예산으로 이해해야 합니다 `p_norm`.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b46531b3-055b-4112-903a-11f2dd52a9ac",
      "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",
        "l1_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=1)\n",
        "print(l1_truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "bc252678-58a0-407e-91fe-b7c545d57ae8",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 6 circuit slices.\n",
            "New observable contains 116 terms and 10 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "from qiskit_addon_obp import backpropagate\n",
        "\n",
        "max_slices = 6\n",
        "l1_bp_obs, l1_remaining_slices, l1_metadata = backpropagate(\n",
        "    obs,\n",
        "    slices[-max_slices:],\n",
        "    truncation_error_budget=l1_truncation_error_budget,\n",
        ")\n",
        "l1_reduced_circuit = combine_slices(\n",
        "    slices[:-max_slices] + l1_remaining_slices\n",
        ")\n",
        "print(\n",
        "    f\"Backpropagated {max_slices - len(l1_remaining_slices)} circuit slices.\"\n",
        ")\n",
        "print(\n",
        "    f\"New observable contains {len(l1_bp_obs)} terms and {len(l1_bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9f2782f8-1e93-4319-af26-dc467d7ed459",
      "metadata": {},
      "source": [
        "이제 역전파된 관측량의 기대값과 정확한 기준값에 대한 오차를 계산할 수 있습니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "5be8c457-6569-452d-8502-0fc5e39bf918",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "9.317869899338842 0.00032796052330397174\n"
          ]
        }
      ],
      "source": [
        "estimator = StatevectorEstimator()\n",
        "job = estimator.run([(l1_reduced_circuit, l1_bp_obs)])\n",
        "res = job.result()\n",
        "l1_exp = res[0].data.evs\n",
        "l1_error = exact_exp - l1_exp\n",
        "print(l1_exp, l1_error)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c0eccc5b-dc71-45b3-ad79-3d3dcb491f2d",
      "metadata": {},
      "source": [
        "마지막으로, 각 슬라이스의 역전파 과정에서 발생한 오차와 누적 오차를 그래프로 나타낼 수 있습니다.\n",
        "누적 오차는 각 슬라이스 오차의 합입니다. 누적 오차는 실제 오차에 대한 매우 느슨한 상한선임을 알 수 있다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "ff72e77f-e2c0-4cce-8575-c5aa587f78fb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/ff72e77f-e2c0-4cce-8575-c5aa587f78fb-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_slice_errors,\n",
        ")\n",
        "\n",
        "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
        "axes[1].plot([6], [l1_error], \"x\", color=\"red\", label=\"actual error\")\n",
        "plot_slice_errors(l1_metadata, axes[0])\n",
        "plot_accumulated_error(l1_metadata, axes[1])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "10dd1139-c597-43c2-9f58-0a0988de768f",
      "metadata": {},
      "source": [
        "<span id=\"use-the-l2-norm\" />\n",
        "\n",
        "## L2 규범을 따르십시오\n",
        "\n",
        "L2 노름이 L1 노름보다 발생한 오차를 더 잘 근사한다고 주장할 수 있다.\n",
        "이는 양자 상태 $|\\psi\\rangle$ 가 하르(Haar) 무작위 앙상블에서 추출된 것으로 가정할 수 있기 때문이며, 이 경우 발생하는 오차는 평균이 0이고 분산이 L2 노름으로 근사적으로 상한이 정해지는 분포를 따른다:\n",
        "\n",
        "$$\n",
        "|\\langle\\psi|\\Delta|\\psi\\rangle| \\lesssim \\left( \\sum_{P\\in\\mathcal{T}} |c_P|^2 \\right)^{1/2}\n",
        "$$\n",
        "\n",
        "이 상한은 엄밀하지는 않지만, 병리적인 경우에만 이 상한을 초과하게 될 것입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f302d808-b341-49cb-8bbd-b03f2bfc6dd6",
      "metadata": {},
      "source": [
        "예제 회로의 6개 슬라이스에 대해, 슬라이스당 최대 오차 ( `0.001` 이번에는 L2 노름으로 해석됨)를 사용하여 다시 역전파를 수행합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c7b7e21f-22e4-479d-954c-39400974f8c1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=2)\n"
          ]
        }
      ],
      "source": [
        "l2_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=2)\n",
        "print(l2_truncation_error_budget)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e92680c5-497e-47e0-ae0f-69465028dd66",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Backpropagated 6 circuit slices.\n",
            "New observable contains 84 terms and 6 commuting groups.\n"
          ]
        }
      ],
      "source": [
        "max_slices = 6\n",
        "l2_bp_obs, l2_remaining_slices, l2_metadata = backpropagate(\n",
        "    obs,\n",
        "    slices[-max_slices:],\n",
        "    truncation_error_budget=l2_truncation_error_budget,\n",
        ")\n",
        "l2_reduced_circuit = combine_slices(\n",
        "    slices[:-max_slices] + l2_remaining_slices\n",
        ")\n",
        "print(\n",
        "    f\"Backpropagated {max_slices - len(l2_remaining_slices)} circuit slices.\"\n",
        ")\n",
        "print(\n",
        "    f\"New observable contains {len(l2_bp_obs)} terms and {len(l2_bp_obs.group_commuting(qubit_wise=True))} commuting groups.\"\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a05f992a-3a38-4912-9e1b-bcd40463c35e",
      "metadata": {},
      "source": [
        "다시 한 번, 역전파된 관측값과 정확한 기준값에 대한 오차의 기대값을 계산합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "73fe737a-c3b0-4639-83dc-d614521dd77a",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "9.317829770853422 0.00036808900872387085\n"
          ]
        }
      ],
      "source": [
        "estimator = StatevectorEstimator()\n",
        "job = estimator.run([(l2_reduced_circuit, l2_bp_obs)])\n",
        "res = job.result()\n",
        "l2_exp = res[0].data.evs\n",
        "l2_error = exact_exp - l2_exp\n",
        "print(l2_exp, l2_error)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "81fd099c-f144-4c59-9b76-72fd8b06d8ad",
      "metadata": {},
      "source": [
        "슬라이스별 발생 오차와 누적 오차를 그래프로 나타내면 이전과 비슷한 양상이 나타납니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "fe7f454c-7d79-4c61-b1dd-6a98564fb5f1",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/addons/qiskit-addon-obp/guides/bound-error-using-p-norm/extracted-outputs/fe7f454c-7d79-4c61-b1dd-6a98564fb5f1-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
        "axes[1].plot([6], [l2_error], \"x\", color=\"red\", label=\"actual error\")\n",
        "plot_slice_errors(l2_metadata, axes[0])\n",
        "plot_accumulated_error(l2_metadata, axes[1])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9ef23a93-5c3b-48be-a02a-e666179c8c65",
      "metadata": {},
      "source": [
        "누적 오차는 다시 개별 슬라이스 오차의 합계라는 점에 유의하십시오. 이는 뮌크프스키 부등식 때문에 또 다른 느슨한 상한입니다. 이 상한을 재귀적으로 계산해야 하기 때문입니다:\n",
        "\n",
        "$$\n",
        "|\\langle\\psi|\\Delta_{i}|\\psi\\rangle| \\leq |\\langle\\psi|\\tilde{\\Delta}_{i-1}|\\psi\\rangle| + \\left( \\sum_{P\\in\\mathcal{T_i}} |c_P|^2 \\right)^{1/2} = |\\langle\\psi|\\tilde{\\Delta}_{i}|\\psi\\rangle|\n",
        "$$\n",
        "\n",
        "여기서 새로운 첨자 $i$ 는 현재 슬라이스 반복 횟수를 나타내며, 이에 따라 $\\Delta_i$ 는 역전파 반복 $i$ 에서의 실제 오차, $\\tilde{\\Delta}_{i-1}$ 는 반복 $i-1$ 에서 산출된 근사 절단 오차, $\\mathcal{T}_i$ 는 반복 $i$ 에서 절단된 파울리 항의 집합을 나타낸다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}