{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "meta-header",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"Qiskit AI 기반 트랜스파일러 소개\"\n",
        "description: \"AI 기반 트랜스파일러가 미러 회로를 사용하는 표준 트랜스파일링과 어떻게 다른지 알아보세요.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore fontsize fontweight steelblue AITI */}\n",
        "\n",
        "<span id=\"qiskit-ai-powered-transpiler-introduction\" />\n",
        "\n",
        "# Qiskit AI 기반 트랜스파일러 소개\n",
        "\n",
        "*예상 소요 시간: IBM Heron에서 5분 (참고: 이는 단지 예상치일 뿐입니다.) (실행 시간은 다를 수 있습니다.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "learning-outcomes",
      "metadata": {},
      "source": [
        "<span id=\"learning-outcomes\" />\n",
        "\n",
        "## 학습 성과\n",
        "\n",
        "* 표준 트랜스파일러를 그대로 대체할 수 있는 AI 기반 트랜스파일러(`generate_ai_pass_manager`) 사용 방법\n",
        "* AI 기반 트랜스파일러와 기본 트랜스파일러를 2-큐비트 깊이, 게이트 수, 트랜스파일링 시간 측면에서 비교한 결과\n",
        "* 미러 회로를 활용하여 하드웨어 실행을 통해 트랜스파일링 품질을 평가하는 방법\n",
        "\n",
        "<span id=\"prerequisites\" />\n",
        "\n",
        "## 전제조건\n",
        "\n",
        "* [회로 변환](/docs/guides/transpile)\n",
        "* [사전 설정된 패스 관리자 구성](/docs/guides/transpile-with-pass-managers)\n",
        "* [AI 기반 트랜스파일러가 통과함](/docs/guides/ai-transpiler-passes)\n",
        "\n",
        "<span id=\"background\" />\n",
        "\n",
        "## 배경\n",
        "\n",
        "**Qiskit의 AI 기반 트랜스파일러는** SABRE와 같은 기존의 휴리스틱 방식보다 더 짧고 하드웨어 효율이 높은 회로를 생성할 수 있는 머신러닝 기반 트랜스파일링 단계를 도입했습니다. 회로 길이가 짧을수록 노이즈가 덜 축적되므로, 실제 양자 하드웨어에서 결과의 품질이 직접적으로 향상됩니다.\n",
        "\n",
        "이 튜토리얼에서는 두 가지 트랜스파일링 전략을 비교해 보겠습니다:\n",
        "\n",
        "| 전략       | API                                                                            |\n",
        "| -------- | ------------------------------------------------------------------------------ |\n",
        "| **기본값**  | `generate_preset_pass_manager(optimization_level=3, ...)`                      |\n",
        "| **인공지능** | `generate_ai_pass_manager(optimization_level=1, ai_optimization_level=3, ...)` |\n",
        "\n",
        "각 전략에 대해 세 가지 지표, 즉 **2-큐비트 게이트 깊이**, **총 게이트 수**, **트랜스필레이션 실행 시간을** 측정합니다.\n",
        "\n",
        "<span id=\"ai-powered-transpiler-benchmarks\" />\n",
        "\n",
        "### AI 기반 트랜스파일러 벤치마크\n",
        "\n",
        "벤치마크 테스트 결과, AI 기반 트랜스파일러는 표준 Qiskit 트랜스파일러에 비해 일관되게 더 얕고 품질이 높은 회로를 생성해 냈다. [`generate_preset_pass_manager`](/docs/api/qiskit/qiskit.transpiler.generate_preset_pass_manager)이 테스트에서는.로 구성된 Qiskit의 기본 패스 매니저 전략을 사용했습니다. 이 기본 전략은 대개 효과적이지만, 규모가 크거나 더 복잡한 회로에서는 제대로 작동하지 않을 수 있습니다. 반면, AI 기반 패스는 IBM Quantum® 하드웨어의 ‘헤비-헥스(heavy-hex)’ 토폴로지로 트랜스파일링할 때, 대규모 회로(100 큐비트 이상)의 경우 2-큐비트 게이트 수를 평균 24% 줄이고 회로 깊이를 36% 감소시키는 성과를 거두었다. 이 벤치마크에 대한 자세한 내용은 이 [블로그](https://www.ibm.com/quantum/blog/qiskit-performance) 를 참조하십시오.\n",
        "\n",
        "![AI 기반 트랜스파일러 벤치마크](https://quantum.cloud.ibm.com/docs/images/tutorials/ai-transpiler-introduction/ai-transpiler-benchmarks.avif)\n",
        "\n",
        "이 튜토리얼에서는 AI 패스의 주요 장점과 기존 방식과의 비교점을 살펴봅니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "requirements",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## 요구사항\n",
        "\n",
        "이 튜토리얼을 시작하기 전에 다음 항목이 설치되어 있는지 확인하십시오:\n",
        "\n",
        "* Qiskit SDK v2.0 또는 그 이후 버전이며, [시각화 기능을](/docs/api/qiskit/visualization) 지원합니다\n",
        "* Qiskit Runtime `pip install qiskit-ibm-runtime`v0.22 이상\n",
        "* AI 로컬 모드를 지원하는 Qiskit IBM 트랜스파일러 (`pip install 'qiskit-ibm-transpiler[ai-local-mode]'`)\n",
        "* Qiskit Aer (`pip install qiskit-aer`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "setup-header",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## 설정\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "setup-code",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "from qiskit.circuit.random import random_circuit\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2\n",
        "from qiskit_ibm_transpiler import generate_ai_pass_manager\n",
        "from qiskit_aer import AerSimulator\n",
        "from qiskit_aer.noise import NoiseModel, depolarizing_error\n",
        "import matplotlib.pyplot as plt\n",
        "from statistics import mean, stdev\n",
        "import time\n",
        "import logging\n",
        "\n",
        "seed = 42\n",
        "\n",
        "\n",
        "def transpile_with_metrics(pass_manager, circuit):\n",
        "    \"\"\"Transpile a circuit and return the result along with key metrics.\"\"\"\n",
        "    start = time.time()\n",
        "    qc_out = pass_manager.run(circuit)\n",
        "    elapsed = time.time() - start\n",
        "\n",
        "    depth_2q = qc_out.depth(lambda x: x.operation.num_qubits == 2)\n",
        "    gate_count = qc_out.size()\n",
        "\n",
        "    return qc_out, {\n",
        "        \"depth_2q\": depth_2q,\n",
        "        \"gate_count\": gate_count,\n",
        "        \"time_s\": round(elapsed, 3),\n",
        "    }\n",
        "\n",
        "\n",
        "def remap_to_contiguous(tqc):\n",
        "    \"\"\"Remap a transpiled circuit to use contiguous qubit indices.\n",
        "\n",
        "    Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)\n",
        "    on a large backend. This remaps them to 0, 1, 2, ... so Aer only\n",
        "    simulates the active qubits.\"\"\"\n",
        "    active = sorted(\n",
        "        {tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}\n",
        "    )\n",
        "    qubit_map = {old: new for new, old in enumerate(active)}\n",
        "    new_qc = QuantumCircuit(len(active))\n",
        "    for inst in tqc.data:\n",
        "        old_indices = [tqc.find_bit(q).index for q in inst.qubits]\n",
        "        new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])\n",
        "    return new_qc\n",
        "\n",
        "\n",
        "def build_mirror_circuit(tqc, simulate=True):\n",
        "    \"\"\"Build a mirror circuit: U followed by U-dagger, with measurements.\n",
        "\n",
        "    The expected output is always |0...0>, so measuring the survival\n",
        "    probability reveals how much noise each transpilation strategy adds.\n",
        "\n",
        "    Args:\n",
        "        tqc: A transpiled circuit.\n",
        "        simulate: If True (default), remap to contiguous qubits so Aer\n",
        "            only simulates the active qubits. If False, keep the full\n",
        "            physical layout for hardware execution.\"\"\"\n",
        "    if simulate:\n",
        "        tqc = remap_to_contiguous(tqc)\n",
        "    mirror = tqc.compose(tqc.inverse())\n",
        "    mirror.measure_all()\n",
        "    return mirror\n",
        "\n",
        "\n",
        "def print_summary(results):\n",
        "    \"\"\"Print a summary of each metric as mean +/- stdev across all circuits,\n",
        "    along with the mean percentage improvement of AI over Default.\"\"\"\n",
        "    metrics = [\n",
        "        (\"Depth 2Q\", \"Depth 2Q (Default)\", \"Depth 2Q (AI)\"),\n",
        "        (\"Gate Count\", \"Gate Count (Default)\", \"Gate Count (AI)\"),\n",
        "        (\"Time (s)\", \"Time (Default)\", \"Time (AI)\"),\n",
        "    ]\n",
        "    header = (\n",
        "        f\"{'Metric':<12}{'Default (mean +/- std)':>24}\"\n",
        "        f\"{'AI (mean +/- std)':>22}{'AI % improvement':>22}\"\n",
        "    )\n",
        "    print(header)\n",
        "    print(\"-\" * len(header))\n",
        "    for label, col_def, col_ai in metrics:\n",
        "        defaults = [r[col_def] for r in results]\n",
        "        ais = [r[col_ai] for r in results]\n",
        "        pct = [(d - a) / d * 100 for d, a in zip(defaults, ais)]\n",
        "        default_str = f\"{mean(defaults):.1f} +/- {stdev(defaults):.1f}\"\n",
        "        ai_str = f\"{mean(ais):.1f} +/- {stdev(ais):.1f}\"\n",
        "        pct_str = f\"{mean(pct):+.1f}% +/- {stdev(pct):.1f}%\"\n",
        "        print(f\"{label:<12}{default_str:>24}{ai_str:>22}{pct_str:>22}\")\n",
        "\n",
        "\n",
        "def plot_metrics_and_pct(results, title_prefix):\n",
        "    \"\"\"Plot metric comparisons and percentage improvement of AI over Default.\"\"\"\n",
        "    qubits = [r[\"Qubits\"] for r in results]\n",
        "    metrics = [\n",
        "        (\"Depth 2Q (Default)\", \"Depth 2Q (AI)\", \"Two-Qubit Depth\"),\n",
        "        (\"Gate Count (Default)\", \"Gate Count (AI)\", \"Gate Count\"),\n",
        "        (\"Time (Default)\", \"Time (AI)\", \"Transpilation Time\"),\n",
        "    ]\n",
        "\n",
        "    # Row 1: raw metric comparison\n",
        "    fig, axs = plt.subplots(1, 3, figsize=(21, 5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: Metric Comparison\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "    for ax, (col_def, col_ai, label) in zip(axs, metrics):\n",
        "        ax.plot(qubits, [r[col_def] for r in results], \"o-\", label=\"Default\")\n",
        "        ax.plot(qubits, [r[col_ai] for r in results], \"s-\", label=\"AI\")\n",
        "        ax.set_title(label)\n",
        "        ax.set_xlabel(\"Number of Qubits\")\n",
        "        ax.set_ylabel(label)\n",
        "        ax.legend()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "    # Row 2: percentage improvement\n",
        "    fig, axs = plt.subplots(1, 3, figsize=(21, 5))\n",
        "    fig.suptitle(\n",
        "        f\"{title_prefix}: % Improvement of AI over Default\",\n",
        "        fontsize=15,\n",
        "        fontweight=\"bold\",\n",
        "        y=1.02,\n",
        "    )\n",
        "    for ax, (col_def, col_ai, label) in zip(axs, metrics):\n",
        "        pct = [(r[col_def] - r[col_ai]) / r[col_def] * 100 for r in results]\n",
        "        ax.axhline(\n",
        "            0, color=\"#1f77b4\", linewidth=2, label=\"Default (baseline)\"\n",
        "        )\n",
        "        ax.plot(qubits, pct, \"s-\", color=\"#ff7f0e\", label=\"AI\")\n",
        "        ax.fill_between(qubits, 0, pct, alpha=0.15, color=\"#ff7f0e\")\n",
        "        ax.set_title(label)\n",
        "        ax.set_xlabel(\"Number of Qubits\")\n",
        "        ax.set_ylabel(\"% Improvement\")\n",
        "        ax.legend()\n",
        "    plt.tight_layout()\n",
        "    plt.show()\n",
        "\n",
        "\n",
        "# Suppress verbose AI-powered transpiler logs\n",
        "logging.getLogger(\n",
        "    \"qiskit_ibm_transpiler.wrappers.ai_local_synthesis\"\n",
        ").setLevel(logging.WARNING)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-header",
      "metadata": {},
      "source": [
        "<span id=\"small-scale-simulator-example\" />\n",
        "\n",
        "## 소규모 시뮬레이터 예시\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step1-header",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "### 1단계: 고전적 입력을 양자 문제에 매핑하기\n",
        "\n",
        "우리는 큐비트 수가 6개에서 25개 사이인, 깊이가 4인 임의의 회로 20개를 생성합니다. 이 회로들은 트랜스파일링 전략을 비교하기 위한 테스트 케이스로 사용될 것입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "sim-step1-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created 20 circuits with qubit counts from 6 to 25\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step1-code-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 2,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "num_circuits_sim = 20\n",
        "depth_sim = 4\n",
        "qubit_range_sim = list(range(6, 26))\n",
        "\n",
        "circuits_sim = [\n",
        "    # We have only two qubit gates, as those test how well the\n",
        "    # transpiler can optimize the circuit.\n",
        "    random_circuit(\n",
        "        num_qubits=n,\n",
        "        depth=depth_sim,\n",
        "        max_operands=2,\n",
        "        num_operand_distribution={2: 1},\n",
        "        seed=seed + i,\n",
        "    )\n",
        "    for i, n in enumerate(qubit_range_sim)\n",
        "]\n",
        "\n",
        "print(\n",
        "    f\"Created {len(circuits_sim)} circuits with qubit counts \"\n",
        "    f\"from {qubit_range_sim[0]} to {qubit_range_sim[-1]}\"\n",
        ")\n",
        "circuits_sim[0].draw(output=\"mpl\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-header",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "### 2단계: 양자 하드웨어 실행을 위한 문제 최적화\n",
        "\n",
        "선택한 백엔드에 대한 기본(SABRE) 패스 관리자를 생성합니다. 두 변환 전략 모두 백엔드의 전체 결합 맵을 대상으로 합니다. 시뮬레이션 단계에서 각 트랜스파일된 회로를 활성 큐비트만으로 재표기하기 `remap_to_contiguous` 때문에, 이후의 국소 시뮬레이션은 처리 가능한 수준을 유지하게 되며, 이에 따라 Aer는 전체 디바이스 대신 해당 큐비트들만 시뮬레이션하게 됩니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "sim-step2-backend",
      "metadata": {},
      "outputs": [],
      "source": [
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    min_num_qubits=100, operational=True, simulator=False\n",
        ")\n",
        "\n",
        "\n",
        "pm_default_sim = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    seed_transpiler=seed,\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "sim-step2-transpile",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "bc06de12e386408abf67166416c2d1a0",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "Fetching 4 files:   0%|          | 0/4 [00:00<?, ?it/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement\n",
            "--------------------------------------------------------------------------------\n",
            "Depth 2Q               33.0 +/- 12.9          26.4 +/- 8.0      +15.8% +/- 17.6%\n",
            "Gate Count           522.0 +/- 266.0       560.5 +/- 279.1        -9.0% +/- 9.0%\n",
            "Time (s)                 0.0 +/- 0.0           0.2 +/- 0.1    -893.6% +/- 362.9%\n"
          ]
        }
      ],
      "source": [
        "results_sim = []\n",
        "\n",
        "for i, qc in enumerate(circuits_sim):\n",
        "    n = qubit_range_sim[i]\n",
        "\n",
        "    qc_default, m_default = transpile_with_metrics(pm_default_sim, qc)\n",
        "\n",
        "    # Create a fresh AI pass manager each iteration to avoid stale layout state\n",
        "    pm_ai = generate_ai_pass_manager(\n",
        "        optimization_level=1,\n",
        "        ai_optimization_level=3,\n",
        "        backend=backend,\n",
        "    )\n",
        "    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)\n",
        "\n",
        "    results_sim.append(\n",
        "        {\n",
        "            \"Qubits\": n,\n",
        "            \"Depth 2Q (Default)\": m_default[\"depth_2q\"],\n",
        "            \"Depth 2Q (AI)\": m_ai[\"depth_2q\"],\n",
        "            \"Gate Count (Default)\": m_default[\"gate_count\"],\n",
        "            \"Gate Count (AI)\": m_ai[\"gate_count\"],\n",
        "            \"Time (Default)\": m_default[\"time_s\"],\n",
        "            \"Time (AI)\": m_ai[\"time_s\"],\n",
        "        }\n",
        "    )\n",
        "\n",
        "print_summary(results_sim)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-table-note",
      "metadata": {},
      "source": [
        "이 요약 표에는 전체 20개 회로에 대한 각 지표의 평균과 표준편차, 그리고 기본 설정 대비 AI 기반 트랜스파일러의 평균 개선율이 백분율로 표시되어 있습니다. 양수 값은 AI 기반 트랜스파일러가 더 나은 결과를 산출했음을 나타내며, 음수 값은 기본 설정이 더 나았음을 나타냅니다.\n",
        "\n",
        "이 소규모 예시의 경우, AI 기반 트랜스파일러는 평균적으로 2-큐비트 깊이를 약 16% 줄이는 성과를 보였으나, 그 대가로 게이트 수가 약 9% 증가했습니다. 이는 두 전략 중 하나를 선택할 때 고려해야 할 핵심적인 상충 관계를 잘 보여줍니다. 즉, AI 기반 트랜스파일러는 깊이 감소(2-큐비트 게이트의 연속적인 레이어 수 감소)를 우선시하는 반면, 기본 트랜스파일러(SABRE)는 총 게이트 수 최소화(SWAP 삽입 횟수 감소)를 우선시합니다. 사용 목적에 따라 한 지표가 다른 지표보다 더 중요할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "sim-step2-plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step2-plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step2-plot-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_metrics_and_pct(results_sim, \"Small-Scale Random Circuits\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step2-analysis",
      "metadata": {},
      "source": [
        "**2-큐비트 깊이:** AI 기반 트랜스파일러는 일반적으로 2-큐비트 깊이가 더 낮은 회로를 생성합니다. 깊이는 AI 라우팅 모델이 최적화하도록 훈련된 주요 지표 중 하나이며, 대부분의 회로 규모에서 그 개선 효과가 뚜렷하게 나타나지만, 개별 회로에서는 SABRE가 이를 따라잡거나 능가하기도 합니다.\n",
        "\n",
        "**게이트 수:** 이 규모에서는 양측의 결과가 박빙을 이루고 있으며, 전반적으로 SABRE가 근소한 우위를 점하고 있습니다. SABRE의 라우팅 휴리스틱은 삽입되는 SWAP 게이트의 수를 최소화하도록 설계되어 있어, 이를 통해 게이트 수를 직접적으로 줄일 수 있습니다. 회로 크기가 작을 때는 그 차이가 미미합니다.\n",
        "\n",
        "**트랜스파일레이션 시간:** SABRE의 실행 시간은 큐비트 수와 관계없이 거의 일정하므로, 이 규모에서는 회로 크기가 트랜스파일레이션 시간에 미치는 영향이 미미합니다. SABRE의 핵심 라우팅 로직은 고도로 최적화되어 있습니다(대부분 Rust로 구현됨). AI 기반 트랜스파일러는 처리 시간이 눈에 띄게 더 오래 걸리며 회로 크기에 따라 처리 시간이 증가하지만, 절대적인 처리 시간은 대화형 사용에 있어서는 여전히 합리적인 수준입니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step3-header",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "### 3단계: `Qiskit primitives` 명령어로 실행합니다\n",
        "\n",
        "트랜스필레이션이 회로 정확도에 미치는 영향을 평가하기 위해, 10-큐비트 사례에 기반한 미러 회로를 구축하고, 간단한 노이즈 모델을 적용하여 Aer 시뮬레이터에서 실행해 보십시오. 미러 회로의 예상 출력은 항상 모두 0으로 구성된 비트열이므로, $|0\\rangle^{\\otimes n}$ 가 측정될 확률은 각 트랜스필레이션 전략이 충실도를 얼마나 잘 보존하는지를 보여줍니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "sim-step3-code",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default: depth 84, gates 280\n",
            "AI:      depth 91, gates 343\n"
          ]
        }
      ],
      "source": [
        "# Use the 10-qubit circuit (index where qubits == 10)\n",
        "idx_10q = qubit_range_sim.index(10)\n",
        "\n",
        "qc_10q = circuits_sim[idx_10q]\n",
        "qc_default_10q, _ = transpile_with_metrics(pm_default_sim, qc_10q)\n",
        "\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    optimization_level=1,\n",
        "    ai_optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "qc_ai_10q, _ = transpile_with_metrics(pm_ai, qc_10q)\n",
        "\n",
        "tqc_methods = {\n",
        "    \"Default\": qc_default_10q,\n",
        "    \"AI\": qc_ai_10q,\n",
        "}\n",
        "\n",
        "print(\n",
        "    f\"Default: depth {qc_default_10q.depth()}, gates {qc_default_10q.size()}\"\n",
        ")\n",
        "print(f\"AI:      depth {qc_ai_10q.depth()}, gates {qc_ai_10q.size()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "sim-step3-run",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default   P(|00...0>) = 0.8460  (8460/10000)\n",
            "AI        P(|00...0>) = 0.8121  (8121/10000)\n"
          ]
        }
      ],
      "source": [
        "# Build a simple depolarizing noise model\n",
        "noise_model = NoiseModel()\n",
        "noise_model.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(0.001, 1),\n",
        "    [\"sx\", \"x\", \"rz\"],  # ~0.1% per 1Q gate\n",
        ")\n",
        "noise_model.add_all_qubit_quantum_error(\n",
        "    depolarizing_error(0.01, 2),\n",
        "    [\"cx\", \"ecr\"],  # ~1% per 2Q gate\n",
        ")\n",
        "\n",
        "aer_sim = AerSimulator(noise_model=noise_model)\n",
        "\n",
        "shots = 10000\n",
        "survival_probs = {}\n",
        "\n",
        "for method, tqc in tqc_methods.items():\n",
        "    mirror = build_mirror_circuit(tqc, simulate=True)\n",
        "\n",
        "    sampler = SamplerV2(mode=aer_sim)\n",
        "    job = sampler.run([mirror], shots=shots)\n",
        "    counts = job.result()[0].data.meas.get_counts()\n",
        "\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    survival = counts.get(all_zeros, 0) / shots\n",
        "    survival_probs[method] = survival\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {survival:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots})\"\n",
        "    )"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step3-analysis",
      "metadata": {},
      "source": [
        "우리는 간단한 탈분극 잡음 모델을 적용하여 두 미러 회로를 모두 Aer 시뮬레이터에서 실행했습니다. ‘생존 확률’은 모든 비트가 0인 비트열을 반환하는 샷의 비율로 정의되며, 각 트랜스필레이션 전략이 얼마나 많은 노이즈를 유발하는지를 정량화합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step4-header",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "### 4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환\n",
        "\n",
        "두 실행 결과에서 모두 0으로만 구성된 비트열이 관측될 확률을 구합니다. 생존 확률이 높을수록 충실도가 더 높다는 것을 의미하며, 이는 변환 과정에서 발생한 노이즈가 적었음을 뜻합니다. 아래 그래프는 보충값인 1 - P(|0...0>)를 나타내며, 하단 막대가 낮을수록 충실도가 높음을 의미하므로 오차의 미세한 차이도 더 쉽게 확인할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "sim-step4-code",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/sim-step4-code-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.\n",
        "# A lower bar means the transpilation introduced less noise.\n",
        "error_probs = {method: 1 - p for method, p in survival_probs.items()}\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.bar(\n",
        "    error_probs.keys(),\n",
        "    error_probs.values(),\n",
        "    color=[\"steelblue\", \"coral\"],\n",
        ")\n",
        "ax.set_ylabel(\"1 - P(|0...0>)\")\n",
        "ax.set_title(\"Mirror Circuit Error (10-qubit, Aer Simulator)\")\n",
        "ax.set_ylim(0, 1)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "sim-step4-analysis",
      "metadata": {},
      "source": [
        "이 경우, 기본 트랜스파일러는 이 특정 10-큐비트 인스턴스에 대해 더 얕고 크기도 더 작은 회로를 생성했기 때문에, 더 높은 충실도를 보일 것으로 예상됩니다. 회로별 결과는 제각각입니다. 위의 요약 표에서 볼 수 있듯이, AI 기반 트랜스파일러의 장점은 모든 개별 회로에서 나타나는 것이 아니라, 평균적으로 더 낮은 2-큐비트 깊이에서 나타납니다. 어떤 전략이 더 높은 충실도를 제공하는지는 각 측정 지표 간의 차이 크기, 하드웨어의 노이즈 특성, 그리고 회로 구조에 따라 달라집니다. 균일한 탈분극 잡음 모델 하에서는, 게이트 총 수가 깊이 자체보다 누적 오류에 더 직접적인 영향을 미치는 경우가 많습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-header",
      "metadata": {},
      "source": [
        "<span id=\"large-scale-hardware-example\" />\n",
        "\n",
        "## 대규모 하드웨어 예시\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-steps-header",
      "metadata": {},
      "source": [
        "<span id=\"steps-1-4\" />\n",
        "\n",
        "### 1\\~4단계\n",
        "\n",
        "여기서는 이러한 모든 세부 사항들을 더 큰 규모에서 명확한 워크플로로 통합한 뒤, 이를 실제 양자 하드웨어에서 실행합니다.\n",
        "\n",
        "아래 코드는 퀀텀 비트 수가 26에서 50 사이인, 깊이가 8인 임의의 회로 25개를 생성합니다. 그런 다음 이 회로들은 두 가지 전략 모두를 적용하여 트랜스파일링되며, 동일한 메트릭이 수집됩니다. 그런 다음 26-큐비트 사례에 기반하여 미러 회로를 구축하고, 이를 실제 백엔드에 제출합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "hw-step1",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Created 25 circuits with qubit counts from 26 to 50\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 1-------------------------\n",
        "num_circuits_hw = 25\n",
        "depth_hw = 8\n",
        "qubit_range_hw = list(range(26, 51))\n",
        "\n",
        "circuits_hw = [\n",
        "    # We have only two qubit gates, as those test how well the\n",
        "    # transpiler can optimize the circuit.\n",
        "    random_circuit(\n",
        "        num_qubits=n,\n",
        "        depth=depth_hw,\n",
        "        max_operands=2,\n",
        "        num_operand_distribution={2: 1},\n",
        "        seed=seed + i,\n",
        "    )\n",
        "    for i, n in enumerate(qubit_range_hw)\n",
        "]\n",
        "\n",
        "print(\n",
        "    f\"Created {len(circuits_hw)} circuits with qubit counts \"\n",
        "    f\"from {qubit_range_hw[0]} to {qubit_range_hw[-1]}\"\n",
        ")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "hw-step2",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement\n",
            "--------------------------------------------------------------------------------\n",
            "Depth 2Q              217.4 +/- 50.4        191.0 +/- 35.6      +10.9% +/- 10.7%\n",
            "Gate Count         4513.3 +/- 1394.3     5227.1 +/- 1536.4       -16.4% +/- 5.8%\n",
            "Time (s)                 0.1 +/- 0.0           3.5 +/- 1.5   -3588.2% +/- 643.6%\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 2-------------------------\n",
        "pm_default = generate_preset_pass_manager(\n",
        "    optimization_level=3,\n",
        "    backend=backend,\n",
        "    seed_transpiler=seed,\n",
        ")\n",
        "\n",
        "results_hw = []\n",
        "\n",
        "for i, qc in enumerate(circuits_hw):\n",
        "    n = qubit_range_hw[i]\n",
        "\n",
        "    qc_default, m_default = transpile_with_metrics(pm_default, qc)\n",
        "\n",
        "    # Create a fresh AI pass manager each iteration to avoid stale layout state\n",
        "    pm_ai = generate_ai_pass_manager(\n",
        "        optimization_level=1,\n",
        "        ai_optimization_level=3,\n",
        "        backend=backend,\n",
        "    )\n",
        "    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)\n",
        "\n",
        "    results_hw.append(\n",
        "        {\n",
        "            \"Qubits\": n,\n",
        "            \"Depth 2Q (Default)\": m_default[\"depth_2q\"],\n",
        "            \"Depth 2Q (AI)\": m_ai[\"depth_2q\"],\n",
        "            \"Gate Count (Default)\": m_default[\"gate_count\"],\n",
        "            \"Gate Count (AI)\": m_ai[\"gate_count\"],\n",
        "            \"Time (Default)\": m_default[\"time_s\"],\n",
        "            \"Time (AI)\": m_ai[\"time_s\"],\n",
        "        }\n",
        "    )\n",
        "\n",
        "print_summary(results_hw)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "hw-step2-plot",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step2-plot-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step2-plot-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plot_metrics_and_pct(results_hw, \"Large-Scale Random Circuits\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "hw-step3",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Mirror circuit (Default): depth 1577, gates 9672\n",
            "Mirror circuit (AI):      depth 1235, gates 11092\n",
            "Job submitted: d8gt7vm6983c73dqbg0g\n"
          ]
        }
      ],
      "source": [
        "# -------------------------Step 3-------------------------\n",
        "# Build mirror circuits from the 26-qubit case\n",
        "idx_26q = qubit_range_hw.index(26)\n",
        "\n",
        "qc_26q = circuits_hw[idx_26q]\n",
        "qc_default_26q, _ = transpile_with_metrics(pm_default, qc_26q)\n",
        "\n",
        "pm_ai = generate_ai_pass_manager(\n",
        "    optimization_level=1,\n",
        "    ai_optimization_level=3,\n",
        "    backend=backend,\n",
        ")\n",
        "qc_ai_26q, _ = transpile_with_metrics(pm_ai, qc_26q)\n",
        "\n",
        "mirror_default_hw = build_mirror_circuit(qc_default_26q, simulate=False)\n",
        "mirror_ai_hw = build_mirror_circuit(qc_ai_26q, simulate=False)\n",
        "\n",
        "# Re-transpile to basis gates (the inverse can introduce gates like sxdg)\n",
        "pm_basis = generate_preset_pass_manager(\n",
        "    optimization_level=0,\n",
        "    backend=backend,\n",
        ")\n",
        "mirror_default_hw = pm_basis.run(mirror_default_hw)\n",
        "mirror_ai_hw = pm_basis.run(mirror_ai_hw)\n",
        "\n",
        "print(\n",
        "    f\"Mirror circuit (Default): depth {mirror_default_hw.depth()}, \"\n",
        "    f\"gates {mirror_default_hw.size()}\"\n",
        ")\n",
        "print(\n",
        "    f\"Mirror circuit (AI):      depth {mirror_ai_hw.depth()}, \"\n",
        "    f\"gates {mirror_ai_hw.size()}\"\n",
        ")\n",
        "\n",
        "# Submit to real hardware\n",
        "sampler_hw = SamplerV2(mode=backend)\n",
        "sampler_hw.options.environment.job_tags = [\"TUT_AITI\"]\n",
        "\n",
        "shots_hw = 500000\n",
        "job_hw = sampler_hw.run([mirror_default_hw, mirror_ai_hw], shots=shots_hw)\n",
        "print(f\"Job submitted: {job_hw.job_id()}\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "id": "hw-step4",
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Default   P(|00...0>) = 0.0005  (239/500000)\n",
            "AI        P(|00...0>) = 0.0050  (2516/500000)\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/ai-transpiler-introduction/extracted-outputs/hw-step4-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# -------------------------Step 4-------------------------\n",
        "result_hw = job_hw.result()\n",
        "\n",
        "survival_probs_hw = {}\n",
        "for i, method in enumerate([\"Default\", \"AI\"]):\n",
        "    counts = result_hw[i].data.meas.get_counts()\n",
        "    mirror = [mirror_default_hw, mirror_ai_hw][i]\n",
        "    all_zeros = \"0\" * mirror.num_qubits\n",
        "    survival = counts.get(all_zeros, 0) / shots_hw\n",
        "    survival_probs_hw[method] = survival\n",
        "    print(\n",
        "        f\"{method:8s}  P(|00...0>) = {survival:.4f}  \"\n",
        "        f\"({counts.get(all_zeros, 0)}/{shots_hw})\"\n",
        "    )\n",
        "\n",
        "# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.\n",
        "# A lower bar means the transpilation introduced less noise.\n",
        "error_probs_hw = {method: 1 - p for method, p in survival_probs_hw.items()}\n",
        "\n",
        "fig, ax = plt.subplots(figsize=(6, 4))\n",
        "ax.bar(\n",
        "    error_probs_hw.keys(),\n",
        "    error_probs_hw.values(),\n",
        "    color=[\"steelblue\", \"coral\"],\n",
        ")\n",
        "ax.set_ylabel(\"1 - P(|0...0>)\")\n",
        "ax.set_title(f\"Mirror Circuit Error (26-qubit, {backend.name})\")\n",
        "ax.set_ylim(0, 1)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "hw-analysis",
      "metadata": {},
      "source": [
        "<span id=\"analysis-of-results\" />\n",
        "\n",
        "### 결과 분석\n",
        "\n",
        "이 대규모 분석 결과는 소규모 사례에서 관찰된 경향을, 이제 더 까다로운 규모에서 다시 한 번 뒷받침해 주고 있다.\n",
        "\n",
        "**2-큐비트 깊이:** AI 기반 트랜스파일러는 모든 회로 크기 범위에서 2-큐비트 깊이를 눈에 띄게 낮추는 성과를 지속적으로 보여주고 있습니다. 깊이 최적화는 AI 라우팅 모델이 학습되는 주요 목표 중 하나이며, 큐비트 수가 많아질수록 휴리스틱 기법으로 라우팅 문제를 해결하기가 더 어려워지기 때문에 이 모델의 장점이 더욱 두드러집니다.\n",
        "\n",
        "**게이트 수:** 기본 트랜스파일러(SABRE)는 이 범위 내의 모든 회로 규모에서 일관되게 게이트 수가 더 적은 회로를 생성합니다. SABRE의 휴리스틱은 게이트 수를 최소화하도록 특별히 설계되었으며, 이 규모에서는 그 장점이 명확하고 일관되게 나타납니다.\n",
        "\n",
        "**전사 시간:** 규모가 커질수록 전사 시간의 격차가 더 벌어진다. SABRE는 거의 일정한 수준을 유지하는 반면, AI 기반 트랜스파일러의 실행 시간은 더 가파르게 증가합니다. 그럼에도 불구하고, AI 기반 트랜스파일러 런타임은 대부분의 워크플로우에서 여전히 실용적입니다.\n",
        "\n",
        "**미러 회로의 정확도:** 두 방법 모두 이 규모에서는 생존 확률이 1%를 훨씬 밑도는 결과를 보여, 활용 가능한 신호가 거의 남지 않습니다. 총 게이트 수가 약 10,000개에 달하고 2-큐비트 연산 깊이가 1,000을 초과함에 따라, 미러 회로 전반에 걸쳐 누적된 탈분극 잡음이 신호의 대부분을 압도한다. 이는 미러 회로 방식의 주요 한계를 잘 보여준다. 이 방식은 간단하고 고전적 시뮬레이션이 필요하지 않지만, 대규모 또는 심층 회로에서는 확장성이 떨어지는데, 이러한 회로에서는 두 방법 모두 노이즈 플로어에 근접하게 되며, 남아 있는 미약한 신호는 누적된 오차에 의해 지배되기 때문이다.\n",
        "\n",
        "이러한 결과는 AI 기반 트랜스파일러의 효과성을 입증해 주지만, 그 한계점도 유념해야 한다. 현재 AI 합성 방식은 특정 결합 맵에서만 사용할 수 있어, 그 적용 범위가 제한될 수 있습니다. 다양한 시나리오에서 이 제약 조건의 적용 여부를 평가할 때는 이를 고려해야 합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "next-steps",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## 다음 단계\n",
        "\n",
        "<Admonition type=\"tip\" title=\"권장사항\">\n",
        "  이 글이 흥미로웠다면, 다음 자료도 참고해 보시기 바랍니다:\n",
        "\n",
        "  * [SABRE를 활용한 트랜스파일레이션 최적화](/docs/tutorials/transpilation-optimizations-with-sabre)\n",
        "  * [해밀턴 연산 시뮬레이션 회로의 구성 방법](/docs/tutorials/compilation-methods-for-hamiltonian-simulation-circuits)\n",
        "</Admonition>\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,
    "qpuSeconds": 300
  },
  "nbformat": 4,
  "nbformat_minor": 5
}