{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "7ed4867f",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"깊이 감소를 위한 회로 절단\"\n",
        "description: \"양자 회로의 게이트를 절단하여 회로 깊이를 줄이기 위한 Qiskit 패턴을 구축하십시오.\"\n",
        "---\n",
        "\n",
        "<span id=\"circuit-cutting-for-depth-reduction\" />\n",
        "\n",
        "# 깊이 감소를 위한 회로 절단\n",
        "\n",
        "*사용량 추정치: Eagle 프로세서에서 8분(참고: 이는 추정치일 뿐입니다. 런타임은 다를 수 있습니다.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "387c5e80",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## 배경\n",
        "\n",
        "이 튜토리얼에서는 양자 회로에서 게이트를 절단하여 회로 깊이를 줄이기 위한 `Qiskit pattern` 을 구축하는 방법을 보여줍니다. 회로 차단에 대한 자세한 내용은 [회로 차단 키스킷 애드온 문서를](https://qiskit.github.io/qiskit-addon-cutting/) 참조하세요.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a62460d9",
      "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.9.0 또는 이후 버전 (`pip install qiskit-addon-cutting`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a89fe306",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## 설정\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "c795c670",
      "metadata": {},
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "\n",
        "from qiskit.circuit.library import EfficientSU2\n",
        "from qiskit.quantum_info import PauliList, Statevector, SparsePauliOp\n",
        "from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager\n",
        "\n",
        "from qiskit_addon_cutting import (\n",
        "    cut_gates,\n",
        "    generate_cutting_experiments,\n",
        "    reconstruct_expectation_values,\n",
        ")\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "07f7f75d",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## 1단계: 고전적 입력을 양자 문제에 매핑하기\n",
        "\n",
        "[문서에](/docs/guides/intro-to-patterns) 설명된 네 단계를 사용하여 키스킷 패턴을 구현하겠습니다. 이 경우, 특정 깊이의 회로에서 기대값을 시뮬레이션하여 스왑 게이트를 생성하고 더 얕은 회로에서 하위 실험을 실행합니다. 게이트 절단은 2단계(멀리 떨어진 게이트를 분해하여 양자 실행을 위한 회로 최적화)와 4단계(원래 회로에서 기대값을 재구성하는 후처리)와 관련이 있습니다.\n",
        "첫 번째 단계에서는 키스킷 회로 라이브러리에서 회로를 생성하고 몇 가지 옵저버를 정의합니다.\n",
        "\n",
        "* 입력: 회로를 정의하는 기존 파라미터\n",
        "* 출력: 추상 회로 및 관측 가능\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "54ed0f13",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/54ed0f13-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "circuit = EfficientSU2(num_qubits=4, entanglement=\"circular\").decompose()\n",
        "circuit.assign_parameters([0.4] * len(circuit.parameters), inplace=True)\n",
        "observables = PauliList([\"ZZII\", \"IZZI\", \"IIZZ\", \"XIXI\", \"ZIZZ\", \"IXIX\"])\n",
        "circuit.draw(\"mpl\", scale=0.8, style=\"iqp\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "080a2a8b",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## 2단계: 양자 하드웨어 실행을 위한 문제 최적화\n",
        "\n",
        "* 입력: 추상 회로 및 관측값\n",
        "* 출력: 목표 회로 및 원거리 게이트를 절단하여 투명 회로 깊이를 줄이기 위해 생성된 관측 가능 항목\n",
        "\n",
        "큐비트 3과 0 사이의 게이트를 실행하기 위해 두 번의 스왑이 필요한 초기 레이아웃을 선택하고 큐비트를 초기 위치로 되돌리기 위해 또 다른 두 번의 스왑이 필요한 초기 레이아웃을 선택합니다. 저희는 사전 설정된 패스 관리자로 사용할 수 있는 최고 수준의 최적화인 `optimization_level=3` 을 선택합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b394da7a",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, min_num_qubits=circuit.num_qubits, simulator=False\n",
        ")\n",
        "\n",
        "pm = generate_preset_pass_manager(\n",
        "    optimization_level=3, initial_layout=[0, 1, 2, 3], backend=backend\n",
        ")\n",
        "transpiled_qc = pm.run(circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "68c00476",
      "metadata": {},
      "source": [
        "![스왑해야 하는 큐비트를 보여주는 커플링 맵](https://quantum.cloud.ibm.com/docs/images/tutorials/depth-reduction-with-circuit-cutting/swaps.avif)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "4fe4af43",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Transpiled circuit depth: 103\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/4fe4af43-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 4,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "print(f\"Transpiled circuit depth: {transpiled_qc.depth()}\")\n",
        "transpiled_qc.draw(\"mpl\", scale=0.4, idle_wires=False, style=\"iqp\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "babfbd0f",
      "metadata": {},
      "source": [
        "*먼 게이트를 찾아 잘라내라:* 우리는 먼 게이트(비국소 큐비트인 0과 3을 연결하는 게이트)를 해당 인덱스를 지정하여 객체로 `TwoQubitQPDGate` 대체할 것이다. `cut_gates` 지정된 인덱스의 게이트를 객체로 `TwoQubitQPDGate` 대체하고, 게이트 분해마다 하나씩 총 `QPDBasis` 인스턴스 목록을 반환합니다. 해당 `QPDBasis` 객체는 커트 게이트를 단일 큐비트 연산으로 분해하는 방법에 대한 정보를 포함합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "23e3d25e",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/23e3d25e-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Find the indices of the distant gates\n",
        "cut_indices = [\n",
        "    i\n",
        "    for i, instruction in enumerate(circuit.data)\n",
        "    if {circuit.find_bit(q)[0] for q in instruction.qubits} == {0, 3}\n",
        "]\n",
        "\n",
        "# Decompose distant CNOTs into TwoQubitQPDGate instances\n",
        "qpd_circuit, bases = cut_gates(circuit, cut_indices)\n",
        "\n",
        "qpd_circuit.draw(\"mpl\", scale=0.8)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "069eb942-e947-4eff-a250-9a99c5ec47f0",
      "metadata": {},
      "source": [
        "*백엔드에서 실행할 하위 실험 생성* : `generate_cutting_experiments` 는 `TwoQubitQPDGate` 인스턴스와 관측값을 포함하는 회로를 `PauliList` 로 받아들입니다.\n",
        "\n",
        "전체 크기 회로의 기대값을 시뮬레이션하기 위해 분해된 게이트의 공동 준확률 분포에서 많은 하위 실험을 생성한 다음 하나 이상의 백엔드에서 실행합니다. 분포에서 가져온 샘플 수는 `num_samples` 에 의해 제어되며, 각 고유 샘플에 대해 하나의 결합 계수가 주어집니다. 계수 계산 방법에 대한 자세한 내용은 [설명 자료를](https://qiskit.github.io/qiskit-addon-cutting/explanation/index.html) 참조하세요.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "83b1efed-bafa-48c4-bbf0-cf7eb9027ac5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Generate the subexperiments and sampling coefficients\n",
        "subexperiments, coefficients = generate_cutting_experiments(\n",
        "    circuits=qpd_circuit, observables=observables, num_samples=np.inf\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6929264d",
      "metadata": {},
      "source": [
        "*비교를 위해 멀리 떨어진 게이트를 절단하면 QPD 하위 실험이 더 얕아지는 것을 볼 수 있습니다* : 다음은 QPD 회로에서 임의로 선택한 하위 실험의 예입니다. 깊이가 절반 이상 줄었습니다. 더 깊은 회로의 기대값을 재구성하기 위해서는 이러한 확률적 하위 실험을 많이 생성하고 평가해야 합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "70e2f1b6",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Original circuit depth after transpile: 103\n",
            "QPD subexperiment depth after transpile: 46\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/depth-reduction-with-circuit-cutting/extracted-outputs/70e2f1b6-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Transpile the decomposed circuit to the same layout\n",
        "transpiled_qpd_circuit = pm.run(subexperiments[100])\n",
        "\n",
        "print(f\"Original circuit depth after transpile: {transpiled_qc.depth()}\")\n",
        "print(\n",
        "    f\"QPD subexperiment depth after transpile: {transpiled_qpd_circuit.depth()}\"\n",
        ")\n",
        "transpiled_qpd_circuit.draw(\n",
        "    \"mpl\", scale=0.6, style=\"iqp\", idle_wires=False, fold=-1\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "87368cb9",
      "metadata": {},
      "source": [
        "*반면에 절단은 추가 샘플링이 필요합니다*. 여기서는 3개의 CNOT 게이트를 절단하여 샘플링 오버헤드가 $9^3$ 로 발생했습니다. 회로 절단으로 인해 발생하는 샘플링 오버헤드에 대한 자세한 내용은 [회로 편직 툴박스 문서를](https://qiskit-extensions.github.io/circuit-knitting-toolbox/circuit_cutting/explanation/index.html) 참조하세요.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "2ab65bd4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Sampling overhead: 729.0\n"
          ]
        }
      ],
      "source": [
        "print(f\"Sampling overhead: {np.prod([basis.overhead for basis in bases])}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fd9a126c",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## 3단계: `Qiskit primitives` 명령어로 실행합니다\n",
        "\n",
        "Sampler 프리미티브를 사용하여 대상 회로(“하위 실험”)를 실행합니다.\n",
        "\n",
        "* 입력: 대상 회로\n",
        "* 출력: 준확률 분포\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "a437de20-2042-4e62-87a7-804058cff5db",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Transpile the subexperiments to the backend's instruction set architecture (ISA)\n",
        "isa_subexperiments = pm.run(subexperiments)\n",
        "\n",
        "# Set up the IBM Quantum Sampler primitive.  For a fake backend, this will use a local simulator.\n",
        "sampler = SamplerV2(backend)\n",
        "\n",
        "# Submit the subexperiments\n",
        "job = sampler.run(isa_subexperiments)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "ca53d638",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Retrieve the results\n",
        "results = job.result()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "32d35001",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "czypg1r6rr3g008mgp6g\n"
          ]
        }
      ],
      "source": [
        "print(job.job_id())"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f04d9134-651b-446e-93f4-aa0281786200",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## 4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환\n",
        "\n",
        "하위 실험 결과, 하위 관측 변수 및 샘플링 계수를 사용하여 원래 회로의 기대값을 재구성합니다.\n",
        "\n",
        "입력: 준확률 분포 출력: 재구성된 기대값\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "ace12f7f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Final reconstructed expectation value\n",
            "1.0751342773437473\n"
          ]
        }
      ],
      "source": [
        "reconstructed_expvals = reconstruct_expectation_values(\n",
        "    results,\n",
        "    coefficients,\n",
        "    observables,\n",
        ")\n",
        "# Reconstruct final expectation value\n",
        "final_expval = np.dot(reconstructed_expvals, [1] * len(observables))\n",
        "print(\"Final reconstructed expectation value\")\n",
        "print(final_expval)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "e6237a6f",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Ideal expectation value\n",
            "1.2283177520039992\n"
          ]
        }
      ],
      "source": [
        "ideal_expvals = [\n",
        "    Statevector(circuit).expectation_value(SparsePauliOp(observable))\n",
        "    for observable in observables\n",
        "]\n",
        "print(\"Ideal expectation value\")\n",
        "print(np.dot(ideal_expvals, [1] * len(observables)).real)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "e366d14e",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## 튜토리얼 설문조사\n",
        "\n",
        "이 튜토리얼에 대한 피드백을 제공하려면 간단한 설문조사에 참여해 주세요. 여러분의 인사이트는 콘텐츠 제공과 사용자 경험을 개선하는 데 도움이 됩니다.\n",
        "\n",
        "[설문조사 링크](https://your.feedback.ibm.com/jfe/form/SV_2ftYFf9t72yFNIO)\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": 480
  },
  "nbformat": 4,
  "nbformat_minor": 5
}