{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "eb16d89d-f6fc-417f-9ce5-19251d039df7",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"주기 경계 조건을 위한 회로 절단\"\n",
        "description: \"회로 절단 기법을 사용하여 첫 번째와 마지막 큐비트가 인접하지 않은 유틸리티 규모의 주기적 체인 문제를 해결한다.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize edgecolor */}\n",
        "\n",
        "<span id=\"circuit-cutting-for-periodic-boundary-conditions\" />\n",
        "\n",
        "# 주기 경계 조건을 위한 회로 절단\n",
        "\n",
        "*사용량 추정치: Eagle 프로세서에서 2분(참고: 이는 추정치일 뿐입니다. 런타임은 다를 수 있습니다.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f053a0b1",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## 배경\n",
        "\n",
        "이 노트북에서는 첫 번째 큐비트와 마지막 큐비트를 포함해 인접한 두 큐비트 사이에 두 개의 큐비트 연산이 있는 주기적인 큐비트 체인 시뮬레이션을 고려합니다. 주기적 사슬은 아이싱 모델이나 분자 시뮬레이션과 같은 물리학 및 화학 문제에서 자주 발견됩니다.\n",
        "\n",
        "현재 IBM 퀀텀® 디바이스는 평면형입니다. 첫 번째 큐비트와 마지막 큐비트가 이웃하는 토폴로지에 직접 주기적 체인을 삽입할 수 있습니다. 그러나 충분히 큰 문제의 경우 첫 번째 큐비트와 마지막 큐비트가 멀리 떨어져 있을 수 있으므로 이 두 큐비트 사이의 2큐비트 연산을 위해 많은 스왑 게이트가 필요합니다. 이러한 주기적 경계 문제는 <a href=\"https://arxiv.org/abs/2402.17833\">이 논문</a> 에서 연구되었습니다.\n",
        "\n",
        "이 노트에서는 첫 번째 큐비트와 마지막 큐비트가 이웃이 아닌 유틸리티 스케일의 주기적 체인 문제를 처리하기 위해 회로 절단을 사용하는 방법을 보여드립니다. 이 장거리 연결을 끊으면 회로의 여러 인스턴스를 실행하는 데 드는 추가 SWAP 게이트와 일부 고전적인 후처리를 피할 수 있습니다. 요약하면, 절단은 장거리 2쿼비트 연산을 논리적으로 계산하기 위해 통합될 수 있습니다. 즉, 이 접근 방식은 커플링 맵의 연결성을 효과적으로 증가시켜 더 적은 수의 스왑 게이트로 이어집니다.\n",
        "\n",
        "회로 와이어를 자르는 방법( `wire cutting`)과 2큐비트 게이트를 여러 개의 단일 큐비트 연산으로 대체하는 방법( `gate cutting`)의 두 가지 유형이 있습니다. 이 노트에서는 게이트 커팅에 중점을 두겠습니다. 게이트 절단에 대한 자세한 내용은 `qiskit-addon-cutting` 의 <a href=\"https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html\">설명 자료</a> 과 해당 참조를 참조하세요. 와이어 절단에 대한 자세한 내용은 [기대값 추정을 위한 와이어 절단](/docs/tutorials/wire-cutting) 자습서 또는 <a href=\"https://qiskit.github.io/qiskit-addon-cutting/tutorials/index.html\">키스킷-애드온-커팅</a> 의 자습서를 참조하십시오.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "120312e5-0eed-4168-9098-633a3d0e6e57",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## 요구사항\n",
        "\n",
        "이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:\n",
        "\n",
        "* Qiskit SDK v1.2 또는 이후 (`pip install qiskit`)\n",
        "* Qiskit Runtime v0.3 또는 이후 (`pip install qiskit-ibm-runtime`)\n",
        "* 회로 절단 Qiskit 애드온 v.9.0 또는 이후 버전 (`pip install qiskit-addon-cutting`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f0af3c7d",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## 설정\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f01e3062",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib as mpl\n",
        "\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.passes import (\n",
        "    BasisTranslator,\n",
        "    Optimize1qGatesDecomposition,\n",
        ")\n",
        "from qiskit.circuit.equivalence_library import (\n",
        "    SessionEquivalenceLibrary as sel,\n",
        ")\n",
        "from qiskit.converters import circuit_to_dag, dag_to_circuit\n",
        "from qiskit.result import sampled_expectation_value\n",
        "from qiskit.quantum_info import SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "from qiskit.circuit.library import TwoLocal\n",
        "\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_gates,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import SamplerV2, SamplerOptions, Batch"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6e7c685c-3e10-4cf1-a435-2b0fc761ebc4",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## 1단계: 고전적 입력을 양자 문제에 매핑하기\n",
        "\n",
        "여기서는 TwoLocal 회로를 생성하고 몇 가지 관찰 가능 항목을 정의하겠습니다.\n",
        "\n",
        "<ul>\n",
        "  <li>입력: 회로를 생성하기 위한 파라미터</li>\n",
        "  <li>출력: 추상 회로 및 관측 가능</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8590c112-cf8a-4bc3-ab8b-411d1a1b010a",
      "metadata": {},
      "source": [
        "`entangler map` 의 마지막 큐비트와 첫 번째 큐비트 사이에 주기적으로 연결되는 TwoLocal 회로에 대해 하드웨어적으로 효율적인 `entangler map` 을 고려합니다. 이러한 장거리 상호 작용은 트랜스필레이션 중에 추가 SWAP 게이트로 이어질 수 있으므로 회로의 깊이를 증가시킬 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0776185d-4ea8-4a8c-ab14-121636444d8f",
      "metadata": {},
      "source": [
        "<span id=\"select-backend-and-initial-layout\" />\n",
        "\n",
        "#### 백엔드 및 초기 레이아웃 선택\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "81c616a7-248a-412b-bac5-080eb9199760",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "15b9c4e5-7b25-4b95-9d79-bf73b9adad97",
      "metadata": {},
      "source": [
        "이 노트북에서는 127 큐비트 IBM 양자 디바이스의 토폴로지에서 가장 긴 1D 체인인 109 큐비트 주기적 1D 체인을 고려하겠습니다. 127 큐비트 장치에서 첫 번째 큐비트와 마지막 큐비트가 이웃하도록 109 큐비트 주기적 체인을 배열하는 것은 추가 SWAP 게이트를 통합하지 않고는 불가능합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "6f8c4588-0532-41f9-9d6d-50e754466593",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "109"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "init_layout = [\n",
        "    13,\n",
        "    12,\n",
        "    11,\n",
        "    10,\n",
        "    9,\n",
        "    8,\n",
        "    7,\n",
        "    6,\n",
        "    5,\n",
        "    4,\n",
        "    3,\n",
        "    2,\n",
        "    1,\n",
        "    0,\n",
        "    14,\n",
        "    18,\n",
        "    19,\n",
        "    20,\n",
        "    21,\n",
        "    22,\n",
        "    23,\n",
        "    24,\n",
        "    25,\n",
        "    26,\n",
        "    27,\n",
        "    28,\n",
        "    29,\n",
        "    30,\n",
        "    31,\n",
        "    32,\n",
        "    36,\n",
        "    51,\n",
        "    50,\n",
        "    49,\n",
        "    48,\n",
        "    47,\n",
        "    46,\n",
        "    45,\n",
        "    44,\n",
        "    43,\n",
        "    42,\n",
        "    41,\n",
        "    40,\n",
        "    39,\n",
        "    38,\n",
        "    37,\n",
        "    52,\n",
        "    56,\n",
        "    57,\n",
        "    58,\n",
        "    59,\n",
        "    60,\n",
        "    61,\n",
        "    62,\n",
        "    63,\n",
        "    64,\n",
        "    65,\n",
        "    66,\n",
        "    67,\n",
        "    68,\n",
        "    69,\n",
        "    70,\n",
        "    74,\n",
        "    89,\n",
        "    88,\n",
        "    87,\n",
        "    86,\n",
        "    85,\n",
        "    84,\n",
        "    83,\n",
        "    82,\n",
        "    81,\n",
        "    80,\n",
        "    79,\n",
        "    78,\n",
        "    77,\n",
        "    76,\n",
        "    75,\n",
        "    90,\n",
        "    94,\n",
        "    95,\n",
        "    96,\n",
        "    97,\n",
        "    98,\n",
        "    99,\n",
        "    100,\n",
        "    101,\n",
        "    102,\n",
        "    103,\n",
        "    104,\n",
        "    105,\n",
        "    106,\n",
        "    107,\n",
        "    108,\n",
        "    112,\n",
        "    126,\n",
        "    125,\n",
        "    124,\n",
        "    123,\n",
        "    122,\n",
        "    121,\n",
        "    120,\n",
        "    119,\n",
        "    118,\n",
        "    117,\n",
        "    116,\n",
        "    115,\n",
        "    114,\n",
        "    113,\n",
        "]\n",
        "\n",
        "# the number of qubits in the circuit is governed by the length of the initial layout\n",
        "num_qubits = len(init_layout)\n",
        "num_qubits"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da94a31f-3bce-40e5-a580-b938ff255425",
      "metadata": {},
      "source": [
        "<span id=\"build-the-entangler-map-for-the-twolocal-circuit\" />\n",
        "\n",
        "#### TwoLocal 회로에 대한 얽힘 생성기(entangler) 맵을 구축하십시오\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "15bc3ac1-dbb6-4bb5-8d3e-63f3638f7a96",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = [(i, i + 1) for i in range(0, len(init_layout) - 1)]\n",
        "coupling_map.append(\n",
        "    (len(init_layout) - 1, 0)\n",
        ")  # adding in the periodic connectivity"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b7b4970f-ac13-4644-a92f-1796de876767",
      "metadata": {},
      "source": [
        "TwoLocal 회로를 사용하면 `rotation_blocks` 와 `entangler map` 을 여러 번 반복할 수 있습니다. 이 경우 반복 횟수에 따라 절단해야 하는 주기적 게이트의 수가 결정됩니다. 샘플링 오버헤드는 절단 횟수에 따라 기하급수적으로 증가하므로(자세한 내용은 [기대값 추정을 위한 와이어 절단](/docs/tutorials/wire-cutting) 자습서 참조), 이 노트에서는 반복 횟수를 2로 고정하겠습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "0cd786ff-9798-4e20-937f-a258eea88077",
      "metadata": {},
      "outputs": [],
      "source": [
        "num_reps = 2\n",
        "entangler_map = []\n",
        "\n",
        "for even_edge in coupling_map[0 : len(coupling_map) : 2]:\n",
        "    entangler_map.append(even_edge)\n",
        "\n",
        "for odd_edge in coupling_map[1 : len(coupling_map) : 2]:\n",
        "    entangler_map.append(odd_edge)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "79428537-66cf-40ce-87cf-0f75f591cb4b",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/79428537-66cf-40ce-87cf-0f75f591cb4b-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "ansatz = TwoLocal(\n",
        "    num_qubits=num_qubits,\n",
        "    rotation_blocks=\"rx\",\n",
        "    entanglement_blocks=\"cx\",\n",
        "    entanglement=entangler_map,\n",
        "    reps=num_reps,\n",
        ").decompose()\n",
        "ansatz.draw(\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "18b2f9b3-07f0-47d0-88db-7182e03c5f53",
      "metadata": {},
      "source": [
        "회로 절단을 사용하여 결과물의 품질을 검증하려면 이상적인 결과를 알아야 합니다. 현재 선택되는 회로는 무차별 대입 방식의 기존 시뮬레이션을 뛰어넘습니다. 따라서 회로의 파라미터를 신중하게 수정하여 절벽으로 만듭니다.\n",
        "\n",
        "`Rx` 게이트의 처음 두 레이어에는 $0$ 파라미터 값을 할당하고, 마지막 레이어에는 $\\pi$ 값을 할당합니다. 이렇게 하면 이 회로의 이상적인 결과는 $|1\\rangle^{\\otimes n}$, $n$ 이 큐비트 수입니다. 따라서 $i$ 이 큐비트의 인덱스인 $\\langle Z_i \\rangle$ 과 $\\langle Z_i Z_{i+1} \\rangle$ 의 기대값은 각각 $-1$ 과 $+1$ 입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a0d70827-ae61-49a0-b14c-c10b27963262",
      "metadata": {},
      "outputs": [],
      "source": [
        "params_last_layer = [np.pi] * ansatz.num_qubits\n",
        "params = [0] * (ansatz.num_parameters - ansatz.num_qubits)\n",
        "params.extend(params_last_layer)\n",
        "\n",
        "ansatz.assign_parameters(params, inplace=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8f4289dd-30fd-4295-9d33-488f2fc03a3a",
      "metadata": {},
      "source": [
        "<span id=\"select-observables\" />\n",
        "\n",
        "#### 선택 가능한 관측량\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a359888-c685-4b28-b2d4-c29b89448e3b",
      "metadata": {},
      "source": [
        "게이트 절단의 이점을 정량화하기 위해 관측 변수 $\\frac{1}{n}\\sum_{i=1}^n \\langle Z_i \\rangle$ 와 $\\frac{1}{n-1}\\sum_{i=1}^{n-1} \\langle Z_i Z_{i+1} \\rangle$ 의 기대값을 측정합니다. 앞서 설명한 것처럼 이상적인 기대값은 각각 $-1$ 와 $+1$ 입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "87a6a367-2d4e-410c-8e5f-3ef069f968d8",
      "metadata": {},
      "outputs": [],
      "source": [
        "observables = []\n",
        "\n",
        "for i in range(num_qubits):\n",
        "    obs = \"I\" * (i) + \"Z\" + \"I\" * (num_qubits - i - 1)\n",
        "    observables.append(obs)\n",
        "\n",
        "for i in range(num_qubits):\n",
        "    if i == num_qubits - 1:\n",
        "        obs = \"Z\" + \"I\" * (num_qubits - 2) + \"Z\"\n",
        "    else:\n",
        "        obs = \"I\" * i + \"ZZ\" + \"I\" * (num_qubits - i - 2)\n",
        "    observables.append(obs)\n",
        "\n",
        "observables = SparsePauliOp(observables)\n",
        "paulis = observables.paulis\n",
        "coeffs = observables.coeffs"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "906440c1-4e5b-4f18-9bc7-d450dd7d2b24",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## 2단계: 양자 하드웨어 실행을 위한 문제 최적화\n",
        "\n",
        "<ul>\n",
        "  <li>입력: 추상 회로 및 관측값</li>\n",
        "  <li>출력: 장거리 게이트를 절단하여 생성된 목표 회로 및 관측 가능 항목</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "2a7387e5-ead4-4dd8-a01f-974138cda3d9",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-circuit\" />\n",
        "\n",
        "#### 회로를 트랜스파일하다\n",
        "\n",
        "회로는 이 단계에서 또는 잘라낸 후에 트랜스파일할 수 있습니다. 절단 후 트랜스파일링하면 샘플링 오버헤드로 인해 생성된 각 하위 실험을 트랜스파일링해야 합니다. 따라서 이 단계에서 트랜스파일링하는 것이 트랜스파일링 오버헤드를 줄이는 데 더 현명합니다.\n",
        "\n",
        "그러나 이 단계에서 네이티브 하드웨어 연결을 통해 트랜스파일링을 수행하면 트랜스파일러는 주기적인 2쿼비트 연산을 배치하기 위해 여러 개의 SWAP 게이트를 추가하여 회로 절단의 이점을 무력화합니다. 이 문제를 피하기 위해 우리는 잘라야 할 게이트를 정확히 알고 있다는 점을 활용할 수 있습니다. 특히, 이러한 주기적인 2큐비트 게이트를 수용하기 위해 멀리 떨어진 큐비트 사이에 가상 연결을 추가하여 가상 커플링 맵을 만들 수 있습니다. 이렇게 하면 추가 스왑 게이트를 통합하지 않고도 이 단계에서 회로를 트랜스파일링할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "c12bb07e-54a0-4597-8b4d-4d0fbf6a4c99",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_map = backend.configuration().coupling_map\n",
        "\n",
        "# create a virtual coupling map with long range connectivity\n",
        "virtual_coupling_map = coupling_map.copy()\n",
        "virtual_coupling_map.append([init_layout[-1], init_layout[0]])\n",
        "virtual_coupling_map.append([init_layout[0], init_layout[-1]])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ad38aa32-4613-46c5-bf62-da332a1b9dfb",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/ad38aa32-4613-46c5-bf62-da332a1b9dfb-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pm_virtual = generate_preset_pass_manager(\n",
        "    optimization_level=1,\n",
        "    coupling_map=virtual_coupling_map,\n",
        "    initial_layout=init_layout,\n",
        "    basis_gates=backend.configuration().basis_gates,\n",
        ")\n",
        "\n",
        "virtual_mapped_circuit = pm_virtual.run(ansatz)\n",
        "virtual_mapped_circuit.draw(\"mpl\", fold=-1, idle_wires=False)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ecb4ae6e-f9a5-4453-a8a3-1a5aaaf954da",
      "metadata": {},
      "source": [
        "<span id=\"cut-the-long-range-periodic-connectivities\" />\n",
        "\n",
        "#### 장거리 주기적 연결성을 차단하라\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c155eda9-ba08-4069-9962-ece21926f1db",
      "metadata": {},
      "source": [
        "이제 트랜스파일된 회로에서 게이트를 잘라냅니다. 잘라내야 하는 2큐비트 게이트는 레이아웃의 마지막 큐비트와 첫 번째 큐비트를 연결하는 게이트입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "718ea31e-c8d8-4cf9-975b-fc0e77fb27c0",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Find the indices of the distant gates\n",
        "cut_indices = [\n",
        "    i\n",
        "    for i, instruction in enumerate(virtual_mapped_circuit.data)\n",
        "    if {virtual_mapped_circuit.find_bit(q)[0] for q in instruction.qubits}\n",
        "    == {init_layout[-1], init_layout[0]}\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c6091cb0-227d-454a-a2d8-7ba685b66121",
      "metadata": {},
      "source": [
        "트랜스파일된 회로의 레이아웃을 관측 가능 영역에 적용합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "af57d942-5997-4920-91c6-295fbfef478d",
      "metadata": {},
      "outputs": [],
      "source": [
        "trans_observables = observables.apply_layout(virtual_mapped_circuit.layout)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6a9a20e4-b722-456d-885b-d68ead3d341e",
      "metadata": {},
      "source": [
        "마지막으로 다양한 측정 및 준비 기반에 대한 샘플링을 통해 하위 실험을 생성합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "981f111c-69e6-44c2-a297-c7df302cbc0e",
      "metadata": {},
      "outputs": [],
      "source": [
        "qpd_circuit, bases = cut_gates(virtual_mapped_circuit, cut_indices)\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=qpd_circuit,\n",
        "    observables=trans_observables.paulis,\n",
        "    num_samples=np.inf,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "60582298-52ae-4f10-b8c2-bc754cb6d24c",
      "metadata": {},
      "source": [
        "장거리 상호 작용을 잘라내면 측정 및 준비 기반이 다른 회로의 여러 샘플을 실행하게 된다는 점에 유의하세요. 이에 대한 자세한 내용은 <a href=\"https://arxiv.org/abs/1909.07534\">단일 큐비트 연산을 샘플링하여 가상 2큐비트 게이트 구축하기</a> 및 <a href=\"https://arxiv.org/abs/2312.11638\">여러 개의 2큐비트 유니터리를 사용한 회로 절단</a> 에서 확인할 수 있습니다.\n",
        "\n",
        "절단할 주기적 게이트의 수는 위의 `num_reps` 으로 정의된 `TwoLocal` 레이어의 반복 횟수와 같습니다. 게이트 커팅의 샘플링 오버헤드는 6입니다. 따라서 총 하위 실험 수는 $6^{num\\_reps}$ 입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "id": "be4a43b4-c035-4814-a486-45eb9fe23d86",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Number of subexperiments is 36 = 6**2\n"
          ]
        }
      ],
      "source": [
        "print(f\"Number of subexperiments is {len(subexperiments)} = 6**{num_reps}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41a11eed-a8c0-4cce-b59d-ca49c54b52b1",
      "metadata": {},
      "source": [
        "<span id=\"transpile-the-subexperiments\" />\n",
        "\n",
        "#### 하위 실험을 트랜스파일하다\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5772d662-c030-4de6-9be4-e90cd599b173",
      "metadata": {},
      "source": [
        "이 시점에서 하위 실험에는 기본 게이트 세트에 없는 일부 1큐비트 게이트가 있는 회로가 포함되어 있습니다. 이는 절단된 큐비트가 다른 기준으로 측정되고, 이를 위해 사용되는 회전 게이트가 반드시 기준 게이트 세트에 속하지 않기 때문입니다. 예를 들어, X 기준으로 측정한다는 것은 Z 기준으로 측정하는 일반적인 방식보다 먼저 하다마드 게이트를 적용하는 것을 의미합니다. 하지만 하다마드는 기본 게이트 세트의 일부가 아닙니다.\n",
        "\n",
        "하위 실험의 각 회로에 전체 트랜스필레이션 프로세스를 적용하는 대신 특정 트랜스필레이션 패스를 사용할 수 있습니다. 사용 가능한 모든 트랜스퓔레이션 패스에 대한 자세한 설명은 <a href=\"/docs/api/qiskit/transpiler_passes\">이 문서</a> 을 참조하세요.\n",
        "\n",
        "`BasisTranslator` 및 `Optimize1qGatesDecomposition` 패스를 적용하여 이 회로의 모든 게이트가 기본 게이트 세트에 속하도록 합니다. 이 두 패스를 사용하면 라우팅 및 초기 레이아웃 선택과 같은 다른 단계를 다시 수행하지 않기 때문에 전체 트랜스파일링 프로세스보다 빠릅니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "9c6d074f-5e48-4adb-9f4f-c5b03c9f7e36",
      "metadata": {},
      "outputs": [],
      "source": [
        "pass_ = PassManager(\n",
        "    [Optimize1qGatesDecomposition(basis=backend.configuration().basis_gates)]\n",
        ")\n",
        "\n",
        "subexperiments = pass_.run(\n",
        "    [\n",
        "        dag_to_circuit(\n",
        "            BasisTranslator(sel, target_basis=backend.basis_gates).run(\n",
        "                circuit_to_dag(circ)\n",
        "            )\n",
        "        )\n",
        "        for circ in subexperiments\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "4c5c0c60-3caa-4bd6-80aa-f7dc412680bb",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## 3단계: `Qiskit primitives` 명령어로 실행합니다\n",
        "\n",
        "<ul>\n",
        "  <li>입력: 대상 회로</li>\n",
        "  <li>출력: 준확률 분포</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1fb3b96c-e55c-40e4-a5ca-4c327a192d73",
      "metadata": {},
      "source": [
        "절단 회로를 실행하기 위해 `SamplerV2` 프리미티브를 사용합니다. `dynamical decoupling` 및 `twirling` 을 비활성화하여 결과에서 얻을 수 있는 개선은 이러한 유형의 회로에 대한 게이트 절단을 효과적으로 적용했기 때문일 뿐입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "69a7cc63-173b-467c-87c5-f0924b943f34",
      "metadata": {},
      "outputs": [],
      "source": [
        "options = SamplerOptions()\n",
        "options.default_shots = 10000\n",
        "options.dynamical_decoupling.enable = False\n",
        "options.twirling.enable_gates = False\n",
        "options.twirling.enable_measure = False"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e9311a78-cdc7-4ce2-9716-a0cb5fc03589",
      "metadata": {},
      "source": [
        "이제 배치 모드를 사용하여 작업을 제출하겠습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "6c1ba1d4-b4ed-4781-99f5-41e3f93672d7",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Job ID cwxf7wq60bqg008pvt8g\n"
          ]
        }
      ],
      "source": [
        "with Batch(backend=backend) as batch:\n",
        "    sampler = SamplerV2(options=options)\n",
        "    cut_job = sampler.run(subexperiments)\n",
        "\n",
        "print(f\"Job ID {cut_job.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "54ca98b9-7f31-45d2-910a-97ad29b37a0d",
      "metadata": {},
      "outputs": [],
      "source": [
        "result = cut_job.result()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "dd7cbb19-22ab-4e5a-bd18-3c7b06f67478",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## 4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환\n",
        "\n",
        "<ul>\n",
        "  <li>입력: 준확률 분포</li>\n",
        "  <li>출력: 재구성된 기대값</li>\n",
        "</ul>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "3ee4af4c-4585-4a8d-87a6-921c2cdb1bd2",
      "metadata": {},
      "outputs": [],
      "source": [
        "reconstructed_expvals = reconstruct_expectation_values(\n",
        "    result,\n",
        "    coefficients,\n",
        "    paulis,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f2d78c56-001c-4fd5-97eb-6d45b0f84bda",
      "metadata": {},
      "source": [
        "이제 weight-1 및 weight-2 Z형 관측값의 평균을 계산합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "id": "00714269-8c72-47eb-8651-3e2f5f65d505",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average of weight-1 expectation values is -0.741733944954063\n",
            "Average of weight-2 expectation values is 0.6968862385320495\n"
          ]
        }
      ],
      "source": [
        "cut_weight_1 = np.mean(reconstructed_expvals[:num_qubits])\n",
        "cut_weight_2 = np.mean(reconstructed_expvals[num_qubits:])\n",
        "\n",
        "print(f\"Average of weight-1 expectation values is {cut_weight_1}\")\n",
        "print(f\"Average of weight-2 expectation values is {cut_weight_2}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1d15b180-107a-4078-afad-d306cf3098f5",
      "metadata": {},
      "source": [
        "<span id=\"cross-verify-obtain-uncut-expectation-value\" />\n",
        "\n",
        "### 교차 검증: 절단되지 않은 기대값 획득\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b6387268-cbd6-4766-a43d-154e461dfbc4",
      "metadata": {},
      "source": [
        "회로 절단 기술의 이점을 미절단 기술과 교차 검증하는 것이 유용합니다. 여기서는 회로를 절단하지 않고 기대값을 계산해 보겠습니다. 이러한 언컷 회로는 첫 번째 큐비트와 마지막 큐비트 사이의 2큐비트 연산을 구현하는 데 필요한 많은 수의 스왑 게이트로 인해 문제가 발생할 수 있습니다. `SamplerV2` 을 통해 확률 분포를 구한 후 `sampled_expectation_value` 함수를 사용하여 절단되지 않은 회로의 기대값을 구합니다. 이렇게 하면 모든 인스턴스에서 프리미티브를 균일하게 사용할 수 있습니다. 하지만 `EstimatorV2` 을 사용하여 기대값을 직접 계산할 수도 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "id": "0fcd5c53-703d-4e67-93d5-3ee4a7870753",
      "metadata": {},
      "outputs": [],
      "source": [
        "if ansatz.num_clbits == 0:\n",
        "    ansatz.measure_all()\n",
        "\n",
        "pm_uncut = generate_preset_pass_manager(\n",
        "    optimization_level=1, backend=backend, initial_layout=init_layout\n",
        ")\n",
        "\n",
        "transpiled_circuit = pm_uncut.run(ansatz)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "00e5b011-bec5-4917-a0d3-91943cad5927",
      "metadata": {},
      "outputs": [],
      "source": [
        "sampler = SamplerV2(mode=backend, options=options)\n",
        "uncut_job = sampler.run([transpiled_circuit])"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "ec68d688-7de4-4d95-8e52-23a1eea7d94e",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "The job id for the uncut clifford circuit is cwxfads2ac5g008jhe7g\n"
          ]
        }
      ],
      "source": [
        "uncut_job_id = uncut_job.job_id()\n",
        "print(f\"The job id for the uncut clifford circuit is {uncut_job_id}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "1e04489f-3dc9-4253-aa9a-719952f260e3",
      "metadata": {},
      "outputs": [],
      "source": [
        "uncut_result = uncut_job.result()[0]\n",
        "uncut_counts = uncut_result.data.meas.get_counts()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fb150558-6571-4475-aa81-08860c2782d6",
      "metadata": {},
      "source": [
        "이제 잘라내지 않고 모든 weight-1 및 weight-2 Z형 관측값의 평균 기대값을 계산해 보겠습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "20991ea2-49a8-4258-9dd1-c064655674f1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Average of weight-1 expectation values is -0.32494128440366965\n",
            "Average of weight-2 expectation values is 0.32340917431192656\n"
          ]
        }
      ],
      "source": [
        "uncut_expvals = [\n",
        "    sampled_expectation_value(uncut_counts, obs) for obs in paulis\n",
        "]\n",
        "\n",
        "uncut_weight_1 = np.mean(uncut_expvals[:num_qubits])\n",
        "uncut_weight_2 = np.mean(uncut_expvals[num_qubits:])\n",
        "\n",
        "print(f\"Average of weight-1 expectation values is {uncut_weight_1}\")\n",
        "print(f\"Average of weight-2 expectation values is {uncut_weight_2}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed843ab6-9614-45a8-864a-932d897c0d22",
      "metadata": {},
      "source": [
        "<span id=\"visualize\" />\n",
        "\n",
        "### 시각화\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "94eccfea-feb6-4a1c-a8fd-b7491ddb3b7d",
      "metadata": {},
      "source": [
        "이제 주기적 체인 회로에 게이트 절단을 사용할 때 weight-1 및 weight-2 관측값에 대해 얻은 개선 사항을 시각화해 보겠습니다\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2ba8913f-ba35-409c-bc4c-5f28e3698f20",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/periodic-boundary-conditions-with-circuit-cutting/extracted-outputs/2ba8913f-ba35-409c-bc4c-5f28e3698f20-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "mpl.rcParams.update(mpl.rcParamsDefault)\n",
        "\n",
        "fig = plt.subplots(figsize=(12, 8), dpi=200)\n",
        "width = 0.25\n",
        "labels = [\"Weight-1\", \"Weight-2\"]\n",
        "x = np.arange(len(labels))\n",
        "\n",
        "ideal = [-1, 1]\n",
        "cut = [cut_weight_1, cut_weight_2]\n",
        "uncut = [uncut_weight_1, uncut_weight_2]\n",
        "\n",
        "br1 = np.arange(len(ideal))\n",
        "br2 = [x + width for x in br1]\n",
        "br3 = [x + width for x in br2]\n",
        "\n",
        "plt.bar(\n",
        "    br1, ideal, width=width, edgecolor=\"k\", label=\"Ideal\", color=\"#4589ff\"\n",
        ")\n",
        "plt.bar(br2, cut, width=width, edgecolor=\"k\", label=\"Cut\", color=\"#a56eff\")\n",
        "plt.bar(\n",
        "    br3, uncut, width=width, edgecolor=\"k\", label=\"Uncut\", color=\"#009d9a\"\n",
        ")\n",
        "\n",
        "plt.axhline(y=0, color=\"k\", linestyle=\"-\")\n",
        "\n",
        "plt.xticks([r + width for r in range(len(ideal))], labels, fontsize=14)\n",
        "plt.yticks(fontsize=14)\n",
        "\n",
        "plt.legend(fontsize=14)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "55de226b-1c87-48c8-b5e3-ba28e0640110",
      "metadata": {},
      "source": [
        "<span id=\"summary\" />\n",
        "\n",
        "### 요약\n",
        "\n",
        "요약하면, 109개의 큐비트로 구성된 주기적 1D 체인에 대한 weight-1 및 weight-2 Z 유형 관측값의 평균 기대값을 계산했습니다. 이를 위해 당사는 다음을 수행합니다\n",
        "\n",
        "* 1D 체인의 첫 번째 큐비트와 마지막 큐비트 사이에 장거리 연결을 추가하여 가상 커플링 맵을 생성하고 회로를 트랜스파일했습니다.\n",
        "  * 이 단계에서 트랜실레이션을 사용하면 절단 후 각 하위 실험을 개별적으로 트랜실링하는 오버헤드를 피할 수 있었습니다,\n",
        "  * 가상 커플링 맵을 사용하면 첫 번째 큐비트와 마지막 큐비트 사이의 2큐비트 연산을 위한 추가 스왑 게이트를 피할 수 있었습니다.\n",
        "* 게이트 절단을 통해 트랜스파일 회로에서 장거리 연결성을 제거했습니다.\n",
        "* 적절한 트랜스필레이션 패스를 적용하여 절단된 회로를 베이시스 게이트 세트로 변환했습니다.\n",
        "* `SamplerV2` 프리미티브를 사용하여 IBM 퀀텀 디바이스에서 절단 회로를 실행했습니다.\n",
        "* 는 절단된 회로의 결과를 재구성하여 기대값을 얻습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a556c909-8cac-4500-ab52-9146b47194bf",
      "metadata": {},
      "source": [
        "<span id=\"inference\" />\n",
        "\n",
        "### 추론\n",
        "\n",
        "결과에서 주기적 게이트를 줄임으로써 weight-1 $\\langle Z \\rangle$ 및 weight-2 $\\langle ZZ \\rangle$ 유형 관측소의 평균이 크게 향상되었음을 알 수 있습니다. 이 연구에는 오류 억제 또는 완화 기술이 포함되어 있지 않습니다. 관찰된 개선 사항은 전적으로 이 문제에 대해 게이트 절단을 적절히 사용했기 때문입니다. 완화 및 억제 기술을 사용하면 결과를 더욱 개선할 수 있었습니다.\n",
        "\n",
        "이 연구는 계산 성능을 개선하기 위해 게이트 절단을 효과적으로 사용한 사례를 보여줍니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6b013b53",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## 튜토리얼 설문조사\n",
        "\n",
        "이 튜토리얼에 대한 피드백을 제공하려면 간단한 설문조사에 참여해 주세요. 여러분의 인사이트는 콘텐츠 제공과 사용자 경험을 개선하는 데 도움이 됩니다.\n",
        "\n",
        "[설문조사 링크](https://your.feedback.ibm.com/jfe/form/SV_3fQQYAIjTxvIChg)\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": 120
  },
  "nbformat": 4,
  "nbformat_minor": 5
}