{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d2c31ae8",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"기대값 추정을 위한 와이어 절단\"\n",
        "description: \"선 절단을 사용하여 회로를 여러 개의 더 작은 하위 회로로 분할하십시오.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore edgecolor Cutqc forall fontsize */}\n",
        "\n",
        "<span id=\"wire-cutting-for-expectation-values-estimation\" />\n",
        "\n",
        "# 기대값 추정을 위한 와이어 절단\n",
        "\n",
        "*예상 실행 시간: 헤론 프로세서 기준 22초 (참고: 이는 단지 추정치일 뿐입니다.) (실행 시간은 다를 수 있습니다.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8bf80006",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## 학습 성과\n",
        "\n",
        "* 대규모 회로를 더 작은 하위 회로로 분할하여 노이즈의 영향을 줄이는 방법 [`qiskit-addon-cutting`](https://github.com/Qiskit/qiskit-addon-cutting)\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## 전제조건\n",
        "\n",
        "* 이 워크플로우에서 사용되는 [샘플러](/docs/api/qiskit-ibm-runtime/sampler-v2) 프리미티브를 사용하여\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## 배경\n",
        "\n",
        "회로 분할(Circuit-knitting)은 회로를 게이트나 큐비트의 수가 더 적은 여러 개의 작은 하위 회로로 나누는 다양한 방법을 포괄하는 총칭입니다. 각 하위 회로는 독립적으로 실행될 수 있으며, 최종 결과는 각 하위 회로의 산출물에 대해 고전적인 후처리를 수행함으로써 얻어집니다. 이 기법은 [Circuit cutting Qiskit](https://qiskit.github.io/qiskit-addon-cutting/index.html) 애드온에서 사용할 수 있습니다. 이 기법에 대한 자세한 설명은 [문서와](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html) 기타 [입문 자료를](https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html) 참고하시기 바랍니다.\n",
        "\n",
        "이 튜토리얼은 회로를 전선을 따라 분할하는 **‘와이어** 커팅’이라는 기법에 중점을 둡니다 [\\[1\\], \\[2\\]](#references). 고전 회로에서는 분할 지점에서의 결과가 결정론적으로 결정될 수 있으며, 0이거나 1이므로 분할이 간단하다는 점에 유의하십시오. 그러나 일반적으로, 절단 시점에서의 큐비트의 상태는 혼합 상태이다. 따라서 각 하위 회로는 서로 다른 기저(일반적으로 파울리 기저 [\\[3\\], \\[4\\]](#references) 와 같은 단층 촬영적으로 완전한 기저)에서 여러 번 측정되어야 하며, 이에 따라 고유 상태로 준비되어야 한다. 아래 그림( [\\[7\\]](#references) 에서 인용)은 4큐비트 GHZ 상태를 세 개의 하위 회로로 분할하는 와이어 커팅의 예를 보여준다. 여기서 $M_j$ 는 기저 집합(일반적으로 Pauli X, Y, Z)을 나타내며, $P_i$ 는 고유상태 집합(일반적으로 $|0\\rangle$, $|1\\rangle$, $|+\\rangle$ 및 $|+i\\rangle$ )을 나타냅니다.\n",
        "\n",
        "![wc-1.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/0ce8857b-7f5f-400e-8536-6a496c724d50.avif)\n",
        "![wc-2.png](https://quantum.cloud.ibm.com/docs/images/tutorials/wire-cutting/cbce4455-4794-4c81-8630-3e3993e1b29f.avif)\n",
        "\n",
        "각 하위 회로는 큐비트와 게이트의 수가 적기 때문에, 노이즈에 덜 취약할 것으로 예상됩니다. 이 튜토리얼에서는 이 방법을 사용하여 시스템의 노이즈를 효과적으로 억제할 수 있는 사례를 보여줍니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55b94021",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## 요구사항\n",
        "\n",
        "이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:\n",
        "\n",
        "* Qiskit SDK v2.0 또는 이후 버전, [시각화](/docs/api/qiskit/visualization) 지원 기능 포함\n",
        "* Qiskit Runtime v0.22 또는 이후 ( `pip install qiskit-ibm-runtime` )\n",
        "* 회로 생성 Qiskit 애드온 v0.10.0 이상 (`pip install qiskit-addon-cutting`)\n",
        "* Qiskit 애드온 유틸리티 0.3 이상 (`pip install qiskit-addon-utils`)\n",
        "* Qiskit Aer (`pip install qiskit-aer` )\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "7db2e559",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## 설정\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "bc380c46",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit\n",
        "from qiskit.quantum_info import PauliList, SparsePauliOp\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit.result import sampled_expectation_value\n",
        "\n",
        "from qiskit_addon_cutting.instructions import CutWire\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_wires,\n",
        "    expand_observables,\n",
        "    partition_problem,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "431a5bd2-e6ed-471b-ad9e-c4edd27784a8",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## 소규모 시뮬레이터 예시\n",
        "\n",
        "이 튜토리얼에서는 1차원( 1D ) 다체 국소화(MBL) 회로를 시뮬레이션하기 위한 [Qiskit 패턴](/docs/guides/intro-to-patterns) 을 구현합니다. MBL 회로는 하드웨어 효율이 높은 회로이며, 두 가지 매개변수 $\\theta$ 와 $\\vec{\\phi}$ 로 정의됩니다. $\\theta$ 를 $0$ 로 설정하고, 모든 큐비트에 대해 $|0\\rangle$ 에서 초기 상태를 준비하면, $\\vec{\\phi}$ 의 값과 무관하게 모든 큐비트 위치 $i$ 에 대해 $\\langle Z_i \\rangle$ 의 이상적인 기대값은 $+1$ 가 됩니다. 이 회로에 대한 자세한 내용은 [이](https://www.nature.com/articles/s41467-025-57623-x) 기사에서 확인할 수 있습니다.\n",
        "\n",
        "소음이 없는 시뮬레이터에서는 회로 절단 유무에 관계없이 얻어지는 기대값이 동일하다는 점에 유의하십시오.\n",
        "\n"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "988ee237",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### 1단계: 고전적 입력을 양자 문제에 매핑하기\n",
        "\n",
        "<span id=\"construct-the-1d-mbl-circuit\" />\n",
        "\n",
        "#### 1D MBL 회로를 구성하십시오\n",
        "\n",
        "먼저, 1D MBL 회로를 구성하는 함수를 제시합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "d2aa988a",
      "metadata": {},
      "outputs": [],
      "source": [
        "class MBLChainCircuit(QuantumCircuit):\n",
        "    def __init__(\n",
        "        self, num_qubits: int, depth: int, use_cut: bool = False\n",
        "    ) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainCircuit<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "        evolution = MBLChainEvolution(num_qubits, depth, use_cut)\n",
        "        self.compose(evolution, inplace=True)\n",
        "\n",
        "\n",
        "class MBLChainEvolution(QuantumCircuit):\n",
        "    def __init__(self, num_qubits: int, depth: int, use_cut) -> None:\n",
        "        super().__init__(\n",
        "            num_qubits, name=f\"MBLChainEvolution<{num_qubits}, {depth}>\"\n",
        "        )\n",
        "\n",
        "        theta = Parameter(\"θ\")\n",
        "        phis = ParameterVector(\"φ\", num_qubits)\n",
        "\n",
        "        for layer in range(depth):\n",
        "            layer_parity = layer % 2\n",
        "            # print(\"layer parity\", layer_parity)\n",
        "            for qubit in range(layer_parity, num_qubits - 1, 2):\n",
        "                # print(qubit)\n",
        "                self.cz(qubit, qubit + 1)\n",
        "                self.u(theta, 0, np.pi, qubit)\n",
        "                self.u(theta, 0, np.pi, qubit + 1)\n",
        "                if (\n",
        "                    use_cut\n",
        "                    and layer_parity == 0\n",
        "                    and (\n",
        "                        qubit == num_qubits // 2 - 1\n",
        "                        or qubit == num_qubits // 2\n",
        "                    )\n",
        "                ):\n",
        "                    self.append(CutWire(), [num_qubits // 2])\n",
        "                if use_cut and layer < depth - 1 and layer_parity == 1:\n",
        "                    if qubit == num_qubits // 2:\n",
        "                        self.append(CutWire(), [qubit])\n",
        "            for qubit in range(num_qubits):\n",
        "                self.p(phis[qubit], qubit)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "a3debf65-06df-4277-933e-14b6f6170756",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/a3debf65-06df-4277-933e-14b6f6170756-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_qubits = 10\n",
        "depth = 2\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "mbl.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ef13f367",
      "metadata": {},
      "source": [
        "우리는 $\\theta = 0$ 에 대해 모든 큐비트에 대한 평균 기대값 $O = \\frac{1}{n} \\sum_i Z_i$ 을 계산한다. $\\langle Z_i \\rangle = 1$ 의 이상적인 기대값은 $\\forall$ $i$ 이므로, $O$ 의 이상적인 기대값 또한 $1$ 이다. 매개변수 $\\phi$ 는 무작위로 선택된다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "b0f1e5fd",
      "metadata": {},
      "outputs": [],
      "source": [
        "np.random.seed(42)\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a81f0346",
      "metadata": {},
      "source": [
        "회로를 분할하려면 원하는 위치에 **CutWire** 를 삽입하여 주석을 달아야 합니다. 이 튜토리얼에서는 균등 분할 방식을 선택합니다. MBL 회로는 함수 내의 설정 `use_cut=True` (setting)이 $\\frac{n}{2}$ 큐비트 뒤에 주석을 올바르게 삽입하도록 설계되었으며, 여기서 $n$ 는 원래 회로의 큐비트 수를 나타냅니다. 또한 무작위로 생성된 매개변수를 회로에 적용했습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "5208e0a8",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/5208e0a8-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_cut.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac6f36e3",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### 2단계: 양자 하드웨어 실행을 위한 문제 최적화\n",
        "\n",
        "<span id=\"cut-the-circuit-into-smaller-subcircuits\" />\n",
        "\n",
        "#### 회로를 더 작은 하위 회로로 분할하십시오\n",
        "\n",
        "[`qiskit-addon-cutting`](https://qiskit.github.io/qiskit-addon-cutting/)이제 를 사용하여 회로를 두 개의 더 작은 하위 회로로 나눕니다. `qiskit-addon-cutting` 큐비트 수를 적절히 조정하여 선 절단 지점을 분할하는 가상 `Move` 게이트를 추가합니다. 이제 이 가상 게이트를 사용하여 회로를 만들겠습니다. 전선이 하나 끊어졌으므로, 관련 큐비트의 수는 1개 증가하게 됩니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "1834cb22",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1834cb22-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "mbl_move = cut_wires(mbl_cut)\n",
        "mbl_move.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1df16e6b",
      "metadata": {},
      "source": [
        "<span id=\"construct-and-expand-the-observable\" />\n",
        "\n",
        "#### 관측 가능한 항을 구성하고 확장한다\n",
        "\n",
        "앞서 정의한 바와 같이, 관측량은 각 큐비트에 대한 $Z$ 의 평균이 될 것입니다. 그러나 가상 `Move` 게이트를 삽입하면 회로 내의 유효 큐비트 수가 증가한다. 또한 큐비트 수의 이러한 변화를 반영하기 위해 관측량도 그에 맞게 확장되어야 한다. 가상 `Move` 게이트를 위해 추가된 큐비트에 대해 관측 연산자는 항상 사소하게 작용한다는 점에 유의하십시오( $I$ 와 같이).\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "3074b173",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',\n",
              "           'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',\n",
              "           'IIIIIIIIZI', 'IIIIIIIIIZ'])"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "observable"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "32b7081b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',\n",
              "           'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',\n",
              "           'IIIIIIIIIZI', 'IIIIIIIIIIZ'])"
            ]
          },
          "execution_count": 8,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "new_obs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0ad0886b",
      "metadata": {},
      "source": [
        "이제 회로를 게이트를 `Move` 따라 분할할 수 있으며, 이를 통해 하위 회로와 함께 각 하위 회로와 연관된 원래 관측량의 일부인 하위 관측량을 얻을 수 있다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "c8894a06",
      "metadata": {},
      "outputs": [],
      "source": [
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f47ab33",
      "metadata": {},
      "source": [
        "여기서는 두 개의 하위 회로를 시각화합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "1b0e779f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/1b0e779f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 13,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[0].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "3c802f28",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/3c802f28-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 14,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "subcircuits[1].draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e4c03fb",
      "metadata": {},
      "source": [
        "연산을 `Move` 사용하여 관측 가능한 영역을 확장하려면 데이터 구조가 `PauliList` 필요합니다. 원래 회로의 기대값을 재구성하려면, 관측량을 형식으로 `SparsePauliOp` 표현해야 합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "7dc51220",
      "metadata": {},
      "outputs": [],
      "source": [
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2b5a5746",
      "metadata": {},
      "source": [
        "앞서 논의한 바와 같이, 각 측정 시마다 상류 회로는 파울리 기저에서 측정되어야 하며, 하류 회로는 해당 기저의 고유상태로 준비되어야 한다. 이 함수는 `generate_cutting_experiments` 재구성에 필요한 모든 회로와 각 회로에 해당하는 계수를 생성합니다. 자세한 내용은 [이 논문](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.125.150504) 에서 확인하실 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "id": "648519f0",
      "metadata": {},
      "outputs": [],
      "source": [
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "43f58cfb",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuits-onto-the-backend\" />\n",
        "\n",
        "#### 회로를 백엔드로 변환합니다\n",
        "\n",
        "시뮬레이션만 포함된 첫 번째 예제에서는 회로를 백엔드의 기본 게이트 집합으로 변환합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "id": "29d71cd3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "<IBMBackend('ibm_fez')>\n"
          ]
        }
      ],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=133\n",
        ")\n",
        "\n",
        "print(backend)"
      ]
    },
    {
      "attachments": {},
      "cell_type": "markdown",
      "id": "b4d480b3",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### 3단계: `Qiskit primitives` 명령어로 실행합니다\n",
        "\n",
        "이제 각 하위 실험을 실행하십시오:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "05fabb27",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=2, basis_gates=backend.configuration().basis_gates\n",
        ")\n",
        "basis_subexperiments = {\n",
        "    label: pm_basis.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "id": "b22a1b00",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=AerSimulator())\n",
        "jobs = {\n",
        "    label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "    for label, subsystem_subexpts in basis_subexperiments.items()\n",
        "}"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "50b94af2",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### 4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환\n",
        "\n",
        "이제 각 하위 실험 실행 결과를 가져와 미수정 회로의 기대값을 재구성합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "id": "31dc35ea-6554-4ca7-9c3b-0b5394c46e4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve results\n",
        "results = {label: job.result() for label, job in jobs.items()}"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "4972e4e4",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.float64(0.9953821063041687)"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "reconstructed_expval"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "fb5f955a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Text(0, 0.5, '$M_Z$')"
            ]
          },
          "execution_count": 32,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/fb5f955a-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "methods = [\n",
        "    \"Uncut\",\n",
        "    \"Wire cut\",\n",
        "]\n",
        "values = [\n",
        "    1,\n",
        "    reconstructed_expval,\n",
        "]  # since the ideal expectation value in noiseless simulation is +1\n",
        "\n",
        "ax = plt.gca()\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "ax.set_ylabel(r\"$M_Z$\", fontsize=12)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d6db390-e7a8-4efe-902c-8d9a312170c6",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## 대규모 하드웨어 예시\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ae69c5e0-32b1-4f03-ab13-7b95a9acfd25",
      "metadata": {},
      "source": [
        "이제 60큐비트 MBL 회로의 와이어 커팅을 시연해 보겠습니다. 미수정 버전과 수정된 버전의 회로 모두 IBM Quantum® 하드웨어에서 실행될 예정입니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "37834c72",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/wire-cutting/extracted-outputs/37834c72-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "num_qubits = 60\n",
        "depth = 2\n",
        "\n",
        "# construct the circuit\n",
        "mbl = MBLChainCircuit(num_qubits, depth)\n",
        "\n",
        "# create parameters\n",
        "phis = list(np.random.rand(mbl.num_parameters - 1))\n",
        "theta = [0]\n",
        "params = theta + phis\n",
        "\n",
        "# construct the cut circuit\n",
        "mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)\n",
        "mbl_cut.assign_parameters(params, inplace=True)\n",
        "mbl_move = cut_wires(mbl_cut)\n",
        "\n",
        "# Define observable and expand to account for the wire cut\n",
        "observable = PauliList(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)]\n",
        ")\n",
        "new_obs = expand_observables(observable, mbl, mbl_move)\n",
        "\n",
        "# Construct a SparsePauliOp version of the observable for later use in reconstruction\n",
        "M_z = SparsePauliOp(\n",
        "    [\"I\" * i + \"Z\" + \"I\" * (num_qubits - i - 1) for i in range(num_qubits)],\n",
        "    coeffs=[1 / num_qubits] * num_qubits,\n",
        ")\n",
        "\n",
        "# Partition the circuit and get subcircuits and subobservables\n",
        "partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)\n",
        "subcircuits = partitioned_problem.subcircuits\n",
        "subobservables = partitioned_problem.subobservables\n",
        "\n",
        "# Obtain subexperiments and coefficients\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=subcircuits,\n",
        "    observables=subobservables,\n",
        "    num_samples=np.inf,\n",
        ")\n",
        "\n",
        "# Transpile the subexperiments to the backend\n",
        "pm = generate_preset_pass_manager(optimization_level=2, backend=backend)\n",
        "isa_subexperiments = {\n",
        "    label: pm.run(partition_subexpts)\n",
        "    for label, partition_subexpts in subexperiments.items()\n",
        "}\n",
        "\n",
        "# Execute the subexperiments and retrieve results\n",
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(mode=batch)\n",
        "    sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "    jobs = {\n",
        "        label: sampler.run(subsystem_subexpts, shots=2**12)\n",
        "        for label, subsystem_subexpts in isa_subexperiments.items()\n",
        "    }\n",
        "results = {label: job.result() for label, job in jobs.items()}\n",
        "\n",
        "# Reconstruct the expectation value of the original observable\n",
        "reconstructed_expval_terms = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    subobservables,\n",
        ")\n",
        "reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real\n",
        "\n",
        "# Compute the uncut circuit to obtain the noisy expectation value for comparison\n",
        "sampler = SamplerV2(mode=backend)\n",
        "sampler.options.environment.job_tags = [\"TUT_WC\"]\n",
        "\n",
        "if mbl.num_clbits == 0:\n",
        "    mbl.measure_all()\n",
        "isa_mbl = pm.run(mbl)\n",
        "\n",
        "pub = (isa_mbl, params)\n",
        "uncut_job = sampler.run([pub])\n",
        "\n",
        "uncut_counts = uncut_job.result()[0].data.meas.get_counts()\n",
        "uncut_expval = sampled_expectation_value(uncut_counts, M_z)\n",
        "\n",
        "# visualize the results\n",
        "ax = plt.gca()\n",
        "methods = [\"uncut\", \"cut\"]\n",
        "values = [uncut_expval, reconstructed_expval]\n",
        "\n",
        "plt.bar(methods, values, color=\"#a56eff\", width=0.4, edgecolor=\"#8a3ffc\")\n",
        "plt.axhline(y=1, color=\"k\", linestyle=\"--\")\n",
        "plt.text(0.3, 0.95, \"Exact result\")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "1445d099",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "0.9202473958333336"
            ]
          },
          "execution_count": 29,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "uncut_expval"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "75f48e6a-c7e4-46f3-9d39-a7a877427a04",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## 다음 단계\n",
        "\n",
        "<Admonition type=\"tip\" title=\"권장사항\">\n",
        "  이 글이 흥미로웠다면, 다음 자료도 참고해 보시기 바랍니다:\n",
        "\n",
        "  * [회로 절단이 적용된 주기적 경계 조건](/docs/tutorials/periodic-boundary-conditions-with-circuit-cutting)\n",
        "  * [깊이 감소를 위한 회로 절단](/docs/tutorials/depth-reduction-with-circuit-cutting)\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "23cd3042",
      "metadata": {},
      "source": [
        "<span id=\"references\" />\n",
        "\n",
        "## 참조\n",
        "\n",
        "\\[1] Peng, T., Harrow, A. W., Ozols, M., & Wu, X. (2020). 소형 양자 컴퓨터에서 대규모 양자 회로를 시뮬레이션합니다. 물리적 검토 편지, 125(15), 150504.\n",
        "\n",
        "\\[2] Tang, W., Tomesh, T., Suchara, M., 라슨, J., & 마르토노시, M. (2021, 4월). Cutqc: 대규모 양자 회로 평가에 소형 양자 컴퓨터 사용. 프로그래밍 언어 및 운영 체제를 위한 아키텍처 지원에 관한 제26회 ACM 국제 컨퍼런스 논문집(473-486쪽).\n",
        "\n",
        "\\[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). 최대 가능성 단층 촬영을 통한 양자 회로 절단. npj 양자 정보, 7(1), 64.\n",
        "\n",
        "\\[4] Majumdar, R., & Wood, C. J. (2022). 오류로 인해 양자 회로 절단이 완화되었습니다. arXiv 미리 인쇄 arXiv:2211.13431.\n",
        "\n",
        "\\[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., 세샤드리, P. V., & 심한, Y. (2023). 퀀텀-클래식 워크로드 병렬화: 분할 기법의 영향 프로파일링. 2023년 IEEE 양자 컴퓨팅 및 엔지니어링(QCE) 국제 컨퍼런스(1권, 990-1000페이지). IEEE.\n",
        "\n",
        "\\[6] Bhoumik, D., Majumdar, R., 사하, A., & Sur-Kolay, S. (2023). 노이즈 및 시간 최적화를 통한 양자 회로의 분산 스케줄링. arXiv 미리 인쇄 arXiv:2309.06005.\n",
        "\n",
        "\\[7] Majumdar, R. (2024). 이산 양자 컴퓨팅 회로에서 자원 및 잡음의 효율적 저감 (박사 학위 논문, 인도 통계 연구소 - 콜카타). [https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y](https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar\\&cbl=2026366\\&diss=y)\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"
    },
    "hours": 1.5,
    "qpuSeconds": 22
  },
  "nbformat": 4,
  "nbformat_minor": 5
}