{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "f69d5853-e815-4754-894d-833017217572",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"큐비트 선택을 위한 실시간 벤치마킹\"\n",
        "description: \"QPU의 물리적 큐비트에 회로를 매핑할 때 큐비트 선택을 개선하기 위해 실시간 특성 분석 실험을 실행하고 백엔드 속성을 업데이트합니다.\"\n",
        "---\n",
        "\n",
        "{/* cspell:ignore sharex */}\n",
        "\n",
        "<span id=\"real-time-benchmarking-for-qubit-selection\" />\n",
        "\n",
        "# 큐비트 선택을 위한 실시간 벤치마킹\n",
        "\n",
        "*사용 예상 시간: Eagle r2 프로세서에서 4분(참고: 이는 예상치일 뿐입니다. 런타임은 다를 수 있습니다.)*\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "500dc8c9-a5d8-4ef1-932f-30e400d6bdde",
      "metadata": {},
      "source": [
        "<span id=\"background\" />\n",
        "\n",
        "## 배경\n",
        "\n",
        "이 튜토리얼에서는 실시간 특성화 실험을 실행하고 백엔드 속성을 업데이트하여 회로를 QPU의 물리적 큐비트에 매핑할 때 큐비트 선택을 개선하는 방법을 보여줍니다. QPU의 속성을 결정하는 데 사용되는 기본적인 특성화 실험과 Qiskit에서 이를 수행하는 방법, 그리고 이러한 실험을 기반으로 QPU를 나타내는 백엔드 객체에 저장된 속성을 업데이트하는 방법을 배우게 됩니다.\n",
        "\n",
        "QPU 보고 속성은 하루에 한 번 업데이트되지만, 시스템은 업데이트 간격보다 빠르게 변동될 수 있습니다. 이는 패스 매니저의 `Layout` 단계에서 큐비트 선택 루틴의 안정성에 영향을 미칠 수 있는데, 이는 QPU의 현재 상태를 나타내지 않는 보고된 프로퍼티를 사용하기 때문입니다. 따라서 특성화 실험에 약간의 QPU 시간을 할애한 다음 `Layout` 루틴에서 사용하는 QPU 속성을 업데이트하는 데 사용할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0babd413-d91f-4fd7-a0f5-bb46ae0bbf5b",
      "metadata": {},
      "source": [
        "<span id=\"requirements\" />\n",
        "\n",
        "## 요구사항\n",
        "\n",
        "이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:\n",
        "\n",
        "* Qiskit SDK v2.0 또는 이후 버전, [시각화](/docs/api/qiskit/visualization) 지원 기능 포함\n",
        "* Qiskit Runtime v0.40 이상 ( `pip install qiskit-ibm-runtime` )\n",
        "* 키스킷 실험 v0.12 이상 ( `pip install qiskit-experiments` )\n",
        "* Rustworkx 그래프 라이브러리 (`pip install rustworkx`)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3df52d5f-806a-4846-849e-633706a96d0b",
      "metadata": {},
      "source": [
        "<span id=\"setup\" />\n",
        "\n",
        "## 설정\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "4766c18a-ba45-456b-8b78-6b6f1d214586",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit_ibm_runtime import SamplerV2\n",
        "from qiskit.transpiler import generate_preset_pass_manager\n",
        "from qiskit.quantum_info import hellinger_fidelity\n",
        "from qiskit.transpiler import InstructionProperties\n",
        "\n",
        "\n",
        "from qiskit_experiments.library import (\n",
        "    T1,\n",
        "    T2Hahn,\n",
        "    LocalReadoutError,\n",
        "    StandardRB,\n",
        ")\n",
        "from qiskit_experiments.framework import BatchExperiment, ParallelExperiment\n",
        "\n",
        "from qiskit_ibm_runtime import QiskitRuntimeService\n",
        "from qiskit_ibm_runtime import Session\n",
        "\n",
        "from datetime import datetime\n",
        "from collections import defaultdict\n",
        "import numpy as np\n",
        "import rustworkx\n",
        "import matplotlib.pyplot as plt\n",
        "import copy"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "65d49ed2-0581-486e-9031-a08fa9bace92",
      "metadata": {},
      "source": [
        "<span id=\"step-1-map-classical-inputs-to-a-quantum-problem\" />\n",
        "\n",
        "## 1단계: 고전적 입력을 양자 문제에 매핑하기\n",
        "\n",
        "성능 차이를 벤치마킹하기 위해 다양한 길이의 선형 체인에 걸쳐 벨 상태를 준비하는 회로를 고려해 보겠습니다. 체인 끝에 있는 벨 상태의 충실도가 측정됩니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "64c25da9-a728-4ae4-a377-3078a1dc618d",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/64c25da9-a728-4ae4-a377-3078a1dc618d-1.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from qiskit import QuantumCircuit\n",
        "\n",
        "ideal_dist = {\"00\": 0.5, \"11\": 0.5}\n",
        "\n",
        "num_qubits_list = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 127]\n",
        "circuits = []\n",
        "for num_qubits in num_qubits_list:\n",
        "    circuit = QuantumCircuit(num_qubits, 2)\n",
        "    circuit.h(0)\n",
        "    for i in range(num_qubits - 1):\n",
        "        circuit.cx(i, i + 1)\n",
        "    circuit.barrier()\n",
        "    circuit.measure(0, 0)\n",
        "    circuit.measure(num_qubits - 1, 1)\n",
        "    circuits.append(circuit)\n",
        "\n",
        "circuits[-1].draw(output=\"mpl\", style=\"clifford\", fold=-1)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "16948f21-a39b-4444-bf02-5f81331825c4",
      "metadata": {},
      "source": [
        "<span id=\"set-up-backend-and-coupling-map\" />\n",
        "\n",
        "### 백엔드 및 결합도 맵 설정\n",
        "\n",
        "먼저 백엔드를 선택합니다\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "f968acca-9131-4f5d-aa74-70befcdda4f5",
      "metadata": {},
      "outputs": [],
      "source": [
        "# To run on hardware, select the backend with the fewest number of jobs in the queue\n",
        "service = QiskitRuntimeService()\n",
        "backend = service.least_busy(\n",
        "    operational=True, simulator=False, min_num_qubits=127\n",
        ")\n",
        "\n",
        "qubits = list(range(backend.num_qubits))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "90b0ddab-0d35-4784-8575-fe0505699197",
      "metadata": {},
      "source": [
        "그런 다음 커플 링 맵을 가져옵니다\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "86d84baa-bd5e-4faf-9582-4568ab4c4a4e",
      "metadata": {},
      "outputs": [],
      "source": [
        "coupling_graph = backend.coupling_map.graph.to_undirected(multigraph=False)\n",
        "\n",
        "# Get unidirectional coupling map\n",
        "one_dir_coupling_map = coupling_graph.edge_list()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d48b5ba2-74ec-4484-9ba6-b0681ae32919",
      "metadata": {},
      "source": [
        "가능한 한 많은 2큐비트 게이트를 동시에 벤치마킹하기 위해 커플링 맵을 `layered_coupling_map` 로 분리합니다. 이 오브젝트에는 각 레이어가 2큐비트 게이트를 동시에 실행할 수 있는 에지 목록인 레이어 목록이 포함되어 있습니다. 이를 커플링 맵의 가장자리 색칠이라고도 합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "62b36ded-ab4e-414e-b146-ff522786a871",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Get layered coupling map\n",
        "edge_coloring = rustworkx.graph_bipartite_edge_color(coupling_graph)\n",
        "layered_coupling_map = defaultdict(list)\n",
        "for edge_idx, color in edge_coloring.items():\n",
        "    layered_coupling_map[color].append(\n",
        "        coupling_graph.get_edge_endpoints_by_index(edge_idx)\n",
        "    )\n",
        "layered_coupling_map = [\n",
        "    sorted(layered_coupling_map[i])\n",
        "    for i in sorted(layered_coupling_map.keys())\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "875117af-8a2c-4aea-92d9-ffeee7ff37d5",
      "metadata": {},
      "source": [
        "<span id=\"characterization-experiments\" />\n",
        "\n",
        "### 특성화 실험\n",
        "\n",
        "일련의 실험을 통해 QPU에서 큐비트의 주요 속성을 특성화하는 데 사용됩니다. $T_1$, $T_2$, 판독 오류, 단일 큐비트 및 2큐비트 게이트 오류입니다. 이러한 속성이 무엇인지 간략하게 요약하고 이를 특성화하는 데 사용되는 [`qiskit-experiments`](https://qiskit-community.github.io/qiskit-experiments/index.html) 패키지의 실험을 참조해 보겠습니다.\n",
        "\n",
        "<span id=\"t1\" />\n",
        "\n",
        "#### T1\n",
        "\n",
        "$T_1$ 는 진폭 감쇠 디코히어런싱 프로세스로 인해 여기된 큐비트가 접지 상태로 떨어지는 데 걸리는 특징적인 시간입니다. [$T_1$ 실험에서는](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t1.html) 지연 후 여기된 큐비트를 측정합니다. 지연 시간이 길어질수록 큐비트가 지상 상태로 떨어질 가능성이 커집니다. 실험의 목표는 실험의 목표는 큐비트가 접지 상태로 향하는 감쇠 속도를 특성화하는 것입니다.\n",
        "\n",
        "<span id=\"t2\" />\n",
        "\n",
        "#### T2\n",
        "\n",
        "$T_2$ 은 단일 큐비트의 블로흐(Bloch) 벡터가 XY 평면에 투영되는 데 필요한 시간을 나타냅니다 벡터 투영이 초기 진폭의 약 37%( $\\frac{1}{e}$ )로 떨어지는 데 걸리는 시간을 나타냅니다 로 떨어지는 데 걸리는 시간을 나타냅니다. [$T_2$ 한 에코 실험을](https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/t2hahn.html) 통해 이 붕괴 속도를 추정할 수 있습니다.\n",
        "\n",
        "<span id=\"state-preparation-and-measurement-spam-error-characterization\" />\n",
        "\n",
        "#### 상태 준비 및 측정(SPAM) 오차 특성 분석\n",
        "\n",
        "[스팸 오류 특성화 실험에서는](https://qiskit-community.github.io/qiskit-experiments/manuals/measurement/readout_mitigation.html) 큐비트를 특정 상태( $\\vert 0 \\rangle$ 또는 $\\vert 1 \\rangle$ )로 준비하여 측정합니다. 준비된 상태와 다른 상태가 측정될 확률은 오류의 확률을 나타냅니다.\n",
        "\n",
        "<span id=\"single-qubit-and-two-qubit-randomized-benchmarking\" />\n",
        "\n",
        "#### 단일 큐비트 및 이중 큐비트 무작위 벤치마킹\n",
        "\n",
        "[무작위 벤치마킹(RB)](https://qiskit-community.github.io/qiskit-experiments/manuals/verification/randomized_benchmarking.html) 은 양자 프로세서의 오류율을 특성화하기 위해 널리 사용되는 프로토콜입니다 오류율을 특성화하는 데 널리 사용되는 프로토콜입니다. RB 실험은 주어진 큐비트에 무작위 클리포드 회로를 생성하여 회로에 의해 계산된 단수가 아이덴티티입니다. 회로를 실행한 후 오류(즉, 기저 상태와 다른 출력)가 발생한 샷 수를 세고 이 데이터에서 클리포드당 오류를 계산하여 양자 소자의 오류 추정치를 유추할 수 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "id": "9d57c42d-7a91-4e79-bc6c-6e579da929f8",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Create T1 experiments on all qubit in parallel\n",
        "t1_exp = ParallelExperiment(\n",
        "    [\n",
        "        T1(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create T2-Hahn experiments on all qubit in parallel\n",
        "t2_exp = ParallelExperiment(\n",
        "    [\n",
        "        T2Hahn(\n",
        "            physical_qubits=[qubit],\n",
        "            delays=[1e-6, 20e-6, 40e-6, 80e-6, 200e-6, 400e-6],\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create readout experiments on all qubit in parallel\n",
        "readout_exp = LocalReadoutError(qubits)\n",
        "\n",
        "# Create single-qubit RB experiments on all qubit in parallel\n",
        "singleq_rb_exp = ParallelExperiment(\n",
        "    [\n",
        "        StandardRB(\n",
        "            physical_qubits=[qubit], lengths=[10, 100, 500], num_samples=10\n",
        "        )\n",
        "        for qubit in qubits\n",
        "    ],\n",
        "    backend,\n",
        "    analysis=None,\n",
        ")\n",
        "\n",
        "# Create two-qubit RB experiments on the three layers of disjoint edges of the heavy-hex\n",
        "twoq_rb_exp_batched = BatchExperiment(\n",
        "    [\n",
        "        ParallelExperiment(\n",
        "            [\n",
        "                StandardRB(\n",
        "                    physical_qubits=pair,\n",
        "                    lengths=[10, 50, 100],\n",
        "                    num_samples=10,\n",
        "                )\n",
        "                for pair in layer\n",
        "            ],\n",
        "            backend,\n",
        "            analysis=None,\n",
        "        )\n",
        "        for layer in layered_coupling_map\n",
        "    ],\n",
        "    backend,\n",
        "    flatten_results=True,\n",
        "    analysis=None,\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cad4f8d3-c2d5-4bb5-92be-432a4573e14a",
      "metadata": {},
      "source": [
        "<span id=\"qpu-properties-over-time\" />\n",
        "\n",
        "### 시간에 따른 QPU 특성\n",
        "\n",
        "시간 경과에 따라 보고된 QPU 속성을 살펴보면(아래에서는 1주일을 기준으로 합니다), 하루 단위로 어떻게 변동될 수 있는지 알 수 있습니다. 하루에도 작은 변동이 발생할 수 있습니다. 이 시나리오에서는 보고된 속성(하루에 한 번 업데이트됨)이 QPU의 현재 상태를 정확하게 캡처하지 못합니다. 또한, 작업이 로컬에서 트랜스파일(현재 보고된 속성 사용)되어 제출되었지만 나중에(분 또는 일) 실행되는 경우, 트랜스파일 단계에서 큐비트 선택에 오래된 속성을 사용했을 위험이 있습니다. 이는 실행 시점에 QPU에 대한 최신 정보를 확보하는 것이 중요하다는 점을 강조합니다. 먼저 특정 시간 범위의 프로퍼티를 검색해 보겠습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "af1b6722-e77b-436a-bbcd-f272b95bb28c",
      "metadata": {},
      "outputs": [],
      "source": [
        "instruction_2q_name = \"cz\"  # set the name of the default 2q of the device\n",
        "errors_list = []\n",
        "for day_idx in range(10, 17):\n",
        "    calibrations_time = datetime(\n",
        "        year=2025, month=8, day=day_idx, hour=0, minute=0, second=0\n",
        "    )\n",
        "    targer_hist = backend.target_history(datetime=calibrations_time)\n",
        "\n",
        "    t1_dict, t2_dict = {}, {}\n",
        "    for qubit in range(targer_hist.num_qubits):\n",
        "        t1_dict[qubit] = targer_hist.qubit_properties[qubit].t1\n",
        "        t2_dict[qubit] = targer_hist.qubit_properties[qubit].t2\n",
        "\n",
        "    errors_dict = {\n",
        "        \"1q\": targer_hist[\"sx\"],\n",
        "        \"2q\": targer_hist[f\"{instruction_2q_name}\"],\n",
        "        \"spam\": targer_hist[\"measure\"],\n",
        "        \"t1\": t1_dict,\n",
        "        \"t2\": t2_dict,\n",
        "    }\n",
        "\n",
        "    errors_list.append(errors_dict)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac75df1b-f689-475c-94a2-d70d85b1f8ca",
      "metadata": {},
      "source": [
        "그런 다음 값을 플로팅해 보겠습니다\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "e0ba509d-e0e0-438b-aedf-5e01919c7d4f",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/e0ba509d-e0e0-438b-aedf-5e01919c7d4f-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig, axs = plt.subplots(5, 1, figsize=(10, 20), sharex=False)\n",
        "\n",
        "\n",
        "# Plot for T1 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t1s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t1_dict = errors_dict[\"t1\"]\n",
        "        try:\n",
        "            t1s.append(t1_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t1 data for qubit {qubit}\")\n",
        "\n",
        "    axs[0].plot(t1s)\n",
        "\n",
        "axs[0].set_title(\"T1\")\n",
        "axs[0].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[0].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot for T2 values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    t2s = []\n",
        "    for errors_dict in errors_list:\n",
        "        t2_dict = errors_dict[\"t2\"]\n",
        "        try:\n",
        "            t2s.append(t2_dict[qubit] / 1e-6)\n",
        "        except:\n",
        "            print(f\"missing t2 data for qubit {qubit}\")\n",
        "\n",
        "    axs[1].plot(t2s)\n",
        "\n",
        "axs[1].set_title(\"T2\")\n",
        "axs[1].set_ylabel(r\"Time ($\\mu s$)\")\n",
        "axs[1].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot SPAM values\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    spams = []\n",
        "    for errors_dict in errors_list:\n",
        "        spam_dict = errors_dict[\"spam\"]\n",
        "        spams.append(spam_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[2].plot(spams)\n",
        "\n",
        "axs[2].set_title(\"SPAM Errors\")\n",
        "axs[2].set_ylabel(\"Error Rate\")\n",
        "axs[2].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 1Q Gate Errors\n",
        "for qubit in range(targer_hist.num_qubits):\n",
        "    oneq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        oneq_gate_dict = errors_dict[\"1q\"]\n",
        "        oneq_gates.append(oneq_gate_dict[tuple([qubit])].error)\n",
        "\n",
        "    axs[3].plot(oneq_gates)\n",
        "\n",
        "axs[3].set_title(\"1Q Gate Errors\")\n",
        "axs[3].set_ylabel(\"Error Rate\")\n",
        "axs[3].set_xlabel(\"Days\")\n",
        "\n",
        "# Plot 2Q Gate Errors\n",
        "for pair in one_dir_coupling_map:\n",
        "    twoq_gates = []\n",
        "    for errors_dict in errors_list:\n",
        "        twoq_gate_dict = errors_dict[\"2q\"]\n",
        "        twoq_gates.append(twoq_gate_dict[pair].error)\n",
        "\n",
        "    axs[4].plot(twoq_gates)\n",
        "\n",
        "axs[4].set_title(\"2Q Gate Errors\")\n",
        "axs[4].set_ylabel(\"Error Rate\")\n",
        "axs[4].set_xlabel(\"Days\")\n",
        "\n",
        "plt.subplots_adjust(hspace=0.5)\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a3343934-9273-457c-900b-2004b3aa9d0c",
      "metadata": {},
      "source": [
        "며칠이 지나면 일부 큐비트 속성이 상당히 변경될 수 있음을 알 수 있습니다. 이는 실험에 가장 적합한 성능의 큐비트를 선택하기 위해 QPU 상태에 대한 최신 정보를 확보하는 것이 중요하다는 점을 강조합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0e53f0a5-7713-4915-ae47-c1aa0a4f17cd",
      "metadata": {},
      "source": [
        "<span id=\"step-2-optimize-problem-for-quantum-hardware-execution\" />\n",
        "\n",
        "## 2단계: 양자 하드웨어 실행을 위한 문제 최적화\n",
        "\n",
        "이 튜토리얼에서는 회로나 연산자에 대한 최적화를 수행하지 않습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "862789c2-d17e-43ed-8e1b-2f37d7628ed2",
      "metadata": {},
      "source": [
        "<span id=\"step-3-execute-using-qiskit-primitives\" />\n",
        "\n",
        "## 3단계: `Qiskit primitives` 명령어로 실행합니다\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d88f925f-3ffd-43de-ac1f-2c5ba3b8cd96",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-default-qubit-selection\" />\n",
        "\n",
        "### 기본 큐비트 선택으로 양자 회로 실행\n",
        "\n",
        "성능에 대한 참조 결과로 요청된 백엔드 속성으로 선택된 큐비트인 기본 큐비트를 사용하여 QPU에서 양자 회로를 실행합니다. `optimization_level = 3` 을 사용합니다.  이 설정에는 가장 진보된 트랜스파일레이션 최적화가 포함되어 있으며, 대상 속성(예: 연산 오류)을 사용하여 실행에 가장 적합한 큐비트를 선택합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "id": "5c0d09ad-2e6a-4067-8034-8df92a475ff1",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = generate_preset_pass_manager(target=backend.target, optimization_level=3)\n",
        "isa_circuits = pm.run(circuits)\n",
        "initial_qubits = [\n",
        "    [\n",
        "        idx\n",
        "        for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "        if qb._register.name != \"ancilla\"\n",
        "    ]\n",
        "    for circuit in isa_circuits\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6d4fb04c-a5cf-48e8-a759-dde64fd751ac",
      "metadata": {},
      "source": [
        "<span id=\"execute-a-quantum-circuit-with-real-time-qubit-selection\" />\n",
        "\n",
        "### 실시간 큐비트 선택을 통한 양자 회로 실행\n",
        "\n",
        "이 섹션에서는 최적의 결과를 얻기 위해 QPU의 큐비트 속성에 대한 최신 정보를 보유하는 것이 얼마나 중요한지 살펴보겠습니다. 먼저 전체 QPU 특성화 실험( $T_1$, $T_2$, SPAM, 단일 쿼비트 RB 및 2쿼비트 RB)을 수행한 다음 백엔드 속성을 업데이트하는 데 사용할 수 있습니다. 이를 통해 패스 관리자는 QPU에 대한 새로운 정보를 기반으로 실행할 큐비트를 선택할 수 있으므로 실행 성능이 향상될 수 있습니다. 둘째, 벨 쌍 회로를 실행하고 업데이트 QPU 속성으로 큐비트를 선택한 후 얻은 충실도를 기본 보고된 속성을 큐비트 선택에 사용할 때 이전에 얻은 충실도와 비교합니다.\n",
        "\n",
        "<Admonition type=\"caution\">\n",
        "  피팅 루틴이 측정된 데이터에 곡선을 맞출 수 없는 경우 일부 특성화 실험이 실패할 수 있다는 점에 유의하세요. 이러한 실험에서 경고가 표시되면 해당 실험을 검사하여 어떤 큐비트에서 어떤 특성화가 실패했는지 파악하고 실험의 매개 변수(예: $T_1$, $T_2$ 의 시간 또는 RB 실험의 수 길이)를 조정해 보세요.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "a8266467-9d60-411f-89dd-8cad6558f588",
      "metadata": {},
      "outputs": [],
      "source": [
        "# Prepare characterization experiments\n",
        "batches = [t1_exp, t2_exp, readout_exp, singleq_rb_exp, twoq_rb_exp_batched]\n",
        "batches_exp = BatchExperiment(batches, backend)  # , analysis=None)\n",
        "run_options = {\"shots\": int(1e3), \"dynamic\": False}\n",
        "\n",
        "with Session(backend=backend) as session:\n",
        "    sampler = SamplerV2(mode=session)\n",
        "\n",
        "    # Run characterization experiments\n",
        "    batches_exp_data = batches_exp.run(\n",
        "        sampler=sampler, **run_options\n",
        "    ).block_for_results()\n",
        "\n",
        "    EPG_sx_result_list = batches_exp_data.analysis_results(\"EPG_sx\")\n",
        "    EPG_sx_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_sx_result_list\n",
        "    ]\n",
        "    EPG_x_result_list = batches_exp_data.analysis_results(\"EPG_x\")\n",
        "    EPG_x_result_q_indices = [\n",
        "        result.device_components.index for result in EPG_x_result_list\n",
        "    ]\n",
        "    T1_result_list = batches_exp_data.analysis_results(\"T1\")\n",
        "    T1_result_q_indices = [\n",
        "        result.device_components.index for result in T1_result_list\n",
        "    ]\n",
        "\n",
        "    T2_result_list = batches_exp_data.analysis_results(\"T2\")\n",
        "    T2_result_q_indices = [\n",
        "        result.device_components.index for result in T2_result_list\n",
        "    ]\n",
        "\n",
        "    Readout_result_list = batches_exp_data.analysis_results(\n",
        "        \"Local Readout Mitigator\"\n",
        "    )\n",
        "\n",
        "    EPG_2q_result_list = batches_exp_data.analysis_results(\n",
        "        f\"EPG_{instruction_2q_name}\"\n",
        "    )\n",
        "\n",
        "    # Update target properties\n",
        "    target = copy.deepcopy(backend.target)\n",
        "    for i in range(target.num_qubits - 1):\n",
        "        qarg = (i,)\n",
        "\n",
        "        if qarg in EPG_sx_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"sx\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_sx_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        if qarg in EPG_x_result_q_indices:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=\"x\",\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_x_result_list[i].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "        err_mat = Readout_result_list.value.assignment_matrix(i)\n",
        "        readout_assignment_error = (\n",
        "            err_mat[0, 1] + err_mat[1, 0]\n",
        "        ) / 2  # average readout error\n",
        "        target.update_instruction_properties(\n",
        "            instruction=\"measure\",\n",
        "            qargs=qarg,\n",
        "            properties=InstructionProperties(error=readout_assignment_error),\n",
        "        )\n",
        "\n",
        "        if qarg in T1_result_q_indices:\n",
        "            target.qubit_properties[i].t1 = T1_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "        if qarg in T2_result_q_indices:\n",
        "            target.qubit_properties[i].t2 = T2_result_list[\n",
        "                i\n",
        "            ].value.nominal_value\n",
        "\n",
        "    for pair_idx, pair in enumerate(one_dir_coupling_map):\n",
        "        qarg = tuple(pair)\n",
        "        try:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg,\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "        except:\n",
        "            target.update_instruction_properties(\n",
        "                instruction=instruction_2q_name,\n",
        "                qargs=qarg[::-1],\n",
        "                properties=InstructionProperties(\n",
        "                    error=EPG_2q_result_list[pair_idx].value.nominal_value\n",
        "                ),\n",
        "            )\n",
        "\n",
        "    # transpile circuits to updated target\n",
        "    pm = generate_preset_pass_manager(target=target, optimization_level=3)\n",
        "    isa_circuit_updated = pm.run(circuits)\n",
        "    updated_qubits = [\n",
        "        [\n",
        "            idx\n",
        "            for idx, qb in circuit.layout.initial_layout.get_physical_bits().items()\n",
        "            if qb._register.name != \"ancilla\"\n",
        "        ]\n",
        "        for circuit in isa_circuit_updated\n",
        "    ]\n",
        "\n",
        "    n_trials = 3  # run multiple trials to see variations\n",
        "\n",
        "    # interleave circuits\n",
        "    interleaved_circuits = []\n",
        "    for original_circuit, updated_circuit in zip(\n",
        "        isa_circuits, isa_circuit_updated\n",
        "    ):\n",
        "        interleaved_circuits.append(original_circuit)\n",
        "        interleaved_circuits.append(updated_circuit)\n",
        "\n",
        "    # Run circuits\n",
        "    # Set simple error suppression/mitigation options\n",
        "    sampler.options.dynamical_decoupling.enable = True\n",
        "    sampler.options.dynamical_decoupling.sequence_type = \"XY4\"\n",
        "\n",
        "    job_interleaved = sampler.run(interleaved_circuits * n_trials)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "d72e021a-3ea6-4ed2-829f-e59ce8017ef4",
      "metadata": {},
      "source": [
        "<span id=\"step-4-post-process-and-return-result-in-desired-classical-format\" />\n",
        "\n",
        "## 4단계: 후처리 수행 및 원하는 클래식 형식으로 결과 반환\n",
        "\n",
        "마지막으로 두 가지 설정에서 얻은 벨 상태의 충실도를 비교해 보겠습니다:\n",
        "\n",
        "* `original`즉, 백엔드의 보고된 속성을 기반으로 트랜스파일러가 선택한 기본 큐비트를 사용합니다.\n",
        "* `updated`즉, 특성화 실험이 실행된 후 백엔드의 업데이트된 속성을 기반으로 선택된 큐비트를 사용합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "id": "5a19a5d1-5daf-4d65-9785-8f8724853821",
      "metadata": {},
      "outputs": [],
      "source": [
        "results = job_interleaved.result()\n",
        "all_fidelity_list, all_fidelity_updated_list = [], []\n",
        "for exp_idx in range(n_trials):\n",
        "    fidelity_list, fidelity_updated_list = [], []\n",
        "\n",
        "    for idx, num_qubits in enumerate(num_qubits_list):\n",
        "        pub_result_original = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx\n",
        "        ]\n",
        "        pub_result_updated = results[\n",
        "            2 * exp_idx * len(num_qubits_list) + 2 * idx + 1\n",
        "        ]\n",
        "\n",
        "        fid = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_original.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_list.append(fid)\n",
        "\n",
        "        fid_up = hellinger_fidelity(\n",
        "            ideal_dist, pub_result_updated.data.c.get_counts()\n",
        "        )\n",
        "        fidelity_updated_list.append(fid_up)\n",
        "    all_fidelity_list.append(fidelity_list)\n",
        "    all_fidelity_updated_list.append(fidelity_updated_list)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "id": "656ec97a-3fd9-4635-9a98-1c5589761689",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/tutorials/real-time-benchmarking-for-qubit-selection/extracted-outputs/656ec97a-3fd9-4635-9a98-1c5589761689-0.avif\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(8, 6))\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"original\",\n",
        "    color=\"b\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_list, '-.')\n",
        "plt.errorbar(\n",
        "    num_qubits_list,\n",
        "    np.mean(all_fidelity_updated_list, axis=0),\n",
        "    yerr=np.std(all_fidelity_updated_list, axis=0),\n",
        "    fmt=\"o-.\",\n",
        "    label=\"updated\",\n",
        "    color=\"r\",\n",
        ")\n",
        "# plt.plot(num_qubits_list, fidelity_updated_list, '-.')\n",
        "plt.xlabel(\"Chain length\")\n",
        "plt.xticks(num_qubits_list)\n",
        "plt.ylabel(\"Fidelity\")\n",
        "plt.title(\"Bell pair fidelity at the edge of N-qubits chain\")\n",
        "plt.legend()\n",
        "plt.grid(\n",
        "    alpha=0.2,\n",
        "    linestyle=\"-.\",\n",
        ")\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3705d85c-e11d-4a8f-bfae-20bd501124d8",
      "metadata": {},
      "source": [
        "실시간 특성화로 인해 모든 실행에서 성능이 향상되는 것은 아니며, 체인 길이가 길어지고 물리적 큐비트 선택의 자유가 줄어들면 업데이트된 디바이스 정보의 중요성이 줄어들게 됩니다. 그러나 디바이스의 성능을 파악하려면 디바이스 속성에 대한 새로운 데이터를 수집하는 것이 좋습니다. 때때로 일시적인 2단계 시스템이 일부 큐비트의 성능에 영향을 미칠 수 있습니다. 실시간 데이터는 이러한 이벤트가 발생하는 시점을 알려주고 이러한 경우 실험적 실패를 방지하는 데 도움이 됩니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "cd81b208-b13b-4988-854e-1741408f36f3",
      "metadata": {},
      "source": [
        "<Admonition type=\"note\" title=\"반응유도문안\">\n",
        "  이 방법을 실행에 적용하여 얼마나 많은 이점을 얻을 수 있는지 확인해 보세요! 다른 백엔드에서 얼마나 개선되었는지 확인해 볼 수도 있습니다.\n",
        "</Admonition>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "b94755c1-1c19-434a-96b9-b83922b5d63c",
      "metadata": {},
      "source": [
        "<span id=\"tutorial-survey\" />\n",
        "\n",
        "## 튜토리얼 설문조사\n",
        "\n",
        "이 튜토리얼에 대한 피드백을 제공하려면 간단한 설문조사에 참여해 주세요. 여러분의 인사이트는 콘텐츠 제공과 사용자 경험을 개선하는 데 도움이 됩니다.\n",
        "\n",
        "[설문조사 링크](https://your.feedback.ibm.com/jfe/form/SV_0w6FZ9QrWkKfTQq)\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "id": "a1b8767d",
      "source": "© IBM Corp., 2017-2026"
    }
  ],
  "metadata": {
    "hours": 2.5,
    "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"
    },
    "qpuSeconds": 240,
    "vscode": {
      "interpreter": {
        "hash": "72460b19b35c51a2148375679c8e282bc3cd3c5550c209f0a38b7d09cded82d9"
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 4
}