{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "710d86a8-3243-4133-b7bc-093306598512",
      "metadata": {},
      "source": [
        "---\n",
        "title: \"사용자 정의 트랜스파일러 패스 작성\"\n",
        "description: \"Qiskit을 사용하여 DAG 회로 표현을 포함한 자체 트랜스파일러 패스 작성 방법을 알아보세요\"\n",
        "---\n",
        "\n",
        "<span id=\"write-a-custom-transpiler-pass\" />\n",
        "\n",
        "# 사용자 정의 트랜스파일러 패스 작성\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5a66bd4b-56b1-4f33-b1bf-b43dbc7c03a7",
      "metadata": {
        "tags": [
          "version-info"
        ]
      },
      "source": [
        "{/*\n",
        "  DO NOT EDIT THIS CELL!!!\n",
        "  This cell's content is generated automatically by a script. Anything you add\n",
        "  here will be removed next time the notebook is run. To add new content, create\n",
        "  a new cell before or after this one.\n",
        "  */}\n",
        "\n",
        "<Accordion>\n",
        "  <AccordionItem title=\"패키지 버전\">\n",
        "    이 페이지의 코드는 다음 요구 사항을 사용하여 개발되었습니다.\n",
        "    다음 버전 이상을 사용하는 것이 좋습니다.\n",
        "\n",
        "    ```\n",
        "    qiskit[all]~=2.5.2\n",
        "    ```\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "32806873-3c88-48fc-94ad-9d20e0f378f8",
      "metadata": {},
      "source": [
        "키스킷 SDK를 사용하면 사용자 지정 트랜스파일레이션 패스를 생성하여 `PassManager` 객체에서 실행하거나 `StagedPassManager` 에 추가할 수 있습니다. 여기서는 양자 회로에서 잡음이 많은 양자 게이트에서 [폴리 회전을](https://arxiv.org/abs/quant-ph/0606161) 수행하는 패스를 구축하는 데 중점을 두고 트랜스파일러 패스를 작성하는 방법을 보여드리겠습니다. 이 예제에서는 `TransformationPass` 유형의 패스로 조작되는 객체인 DAG를 사용합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c49a2046-39b0-4e63-aa63-911845ffd315",
      "metadata": {},
      "source": [
        "<Accordion>\n",
        "  <AccordionItem title=\"배경: DAG 표현\">\n",
        "    패스를 구축하기 전에 키스킷에서 양자 회로의 내부 표현인 [방향성 비순환 그래프(DAG](../api/qiskit/qiskit.dagcircuit.DAGCircuit) )를 소개하는 것이 중요합니다(개요는 [이 튜토리얼을](https://qiskit.org/ecosystem/rustworkx/tutorial/dags.html) 참조하세요).  다음 단계를 수행하려면 DAG 플로팅 함수용 [`graphviz` 라이브러리를](https://graphviz.org/download/) 설치합니다.\n",
        "\n",
        "    Qiskit에서는 트랜스파일레이션 단계 내에서 회로가 DAG(방향성 비순환 그래프)를 사용하여 표현됩니다.  일반적으로 DAG는 *정점* (노드라고도 함)과 특정 방향으로 정점 쌍을 연결하는 방향성 *변으로* 구성됩니다.  이 표현은 개별 `DagNode` 객체들로 구성된 객체들을 `qiskit.dagcircuit.DAGCircuit` 사용하여 저장됩니다.  이 표현 방식이 순수한 게이트 목록(즉, *네트리스트* )에 비해 갖는 장점은 연산 간 정보 흐름이 명시적으로 드러나 변환 결정을 내리기 쉽다는 점이다.\n",
        "\n",
        "    이 예는 벨 상태를 준비하고 측정 결과에 따라 $R_Z$ 회전을 적용하는 간단한 회로를 생성하여 DAG를 설명합니다.\n",
        "\n",
        "    ```python\n",
        "    from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit\n",
        "    import numpy as np\n",
        "\n",
        "    qr = QuantumRegister(3, 'qr')\n",
        "    cr = ClassicalRegister(3, 'cr')\n",
        "    qc = QuantumCircuit(qr, cr)\n",
        "\n",
        "    qc.h(qr[0])\n",
        "    qc.cx(qr[0], qr[1])\n",
        "    qc.measure(qr[0], cr[0])\n",
        "    with qc.if_test((cr, 2)):\n",
        "        qc.rz(np.pi/2, qr[1])\n",
        "    qc.draw(output='mpl')\n",
        "\n",
        "    ```\n",
        "\n",
        "    ![벨 상태를 준비하고 측정 결과에 따라 R\\_Z 회전을 적용하는 회로](https://quantum.cloud.ibm.com/docs/images/guides/custom-transpiler-pass/DAG_circ.avif \"회로\")\n",
        "\n",
        "    `qiskit.tools.visualization.dag_drawer()` 함수를 사용하여 이 회로의 DAG를 확인합니다.  그래프 노드에는 쿼비트/클릭비트 노드(녹색), 연산 노드(파란색), 출력 노드(빨간색)의 세 가지 종류가 있습니다.  각 에지는 두 노드 간의 데이터 흐름(또는 종속성)을 나타냅니다.\n",
        "\n",
        "    ```python\n",
        "    from qiskit.converters import circuit_to_dag\n",
        "    from qiskit.visualization import dag_drawer\n",
        "\n",
        "    dag = circuit_to_dag(qc)\n",
        "    dag_drawer(dag)\n",
        "    ```\n",
        "\n",
        "    ![회로의 DAG는 방향 에지로 연결된 노드로 구성됩니다. 큐비트 또는 클래식 비트, 연산 및 데이터 흐름 방식을 시각적으로 표현하는 방법입니다.](https://quantum.cloud.ibm.com/docs/images/guides/custom-transpiler-pass/DAG.avif \"DAG\")\n",
        "  </AccordionItem>\n",
        "</Accordion>\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7e2e2bc6-c900-4d3b-a948-3f5d154cdea2",
      "metadata": {},
      "source": [
        "<span id=\"transpiler-passes\" />\n",
        "\n",
        "## 트랜스파일러 통과\n",
        "\n",
        "트랜스파일러 패스는 또는 [`AnalysisPass`](../api/qiskit/qiskit.transpiler.AnalysisPass) 로 분류됩니다 [`TransformationPass`](../api/qiskit/qiskit.transpiler.TransformationPass). 패스는 일반적으로 [DAG](../api/qiskit/qiskit.dagcircuit.DAGCircuit) 및 분석 패스에 의해 결정된 속성을 저장하기 위한 사전 `property_set`(dictionary)과 유사한 객체인 와 함께 작동합니다. 분석 패스는 DAG와 그 `property_set`. 모두에서 작동합니다.  그들은 DAG를 수정할 수는 없지만,. `property_set`을 수정할 수는 있습니다.  이는 DAG를 수정하는 변환 패스와는 대조적이며, DAG를 읽을 수는 있지만(쓰기는 불가능합니다) `property_set`.  예를 들어, 변환 단계에서는 회로를 [해당](/docs/guides/transpile#instruction-set-architecture) ISA로 변환하거나, 필요한 곳에 SWAP 게이트를 삽입하기 위해 배선 단계를 수행합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f8ead728-b2b7-47b9-b3b1-f3578b594ffe",
      "metadata": {},
      "source": [
        "<span id=\"create-a-paulitwirl-transpiler-pass\" />\n",
        "\n",
        "## 트랜스파일러 `PauliTwirl` 패스 생성\n",
        "\n",
        "다음 예제는 폴리 트위클을 추가하는 트랜스파일러 패스를 구성하는 예제입니다. [폴리 트위링은](https://arxiv.org/abs/quant-ph/0606161) 큐비트가 잡음 채널을 경험하는 방식을 무작위로 변경하는 오류 억제 전략으로, 이 예에서는 2큐비트 게이트로 가정합니다(단일 큐비트 게이트보다 훨씬 오류가 발생하기 쉽기 때문입니다). 폴리 회전은 2큐비트 게이트의 작동에 영향을 미치지 않습니다. 2큐비트 게이트 *이전* (왼쪽)에 적용된 것과 2큐비트 게이트 *이후* (오른쪽)에 적용된 것이 상쇄되도록 선택됩니다. 이런 의미에서 두 큐비트 연산은 동일하지만 수행 방식이 다릅니다. 폴리 트월링의 한 가지 장점은 일관된 오류를 확률적 오류로 바꾸어 평균을 더 많이 내서 개선할 수 있다는 것입니다.\n",
        "\n",
        "트랜스파일러 패스는 [DAG에서](../api/qiskit/qiskit.dagcircuit.DAGCircuit) 작동하므로 재정의해야 할 중요한 메서드는 DAG를 입력으로 받는 `.run()` 입니다. 그림과 같이 폴리스 쌍을 초기화하면 각 2쿼비트 게이트의 작동이 유지됩니다. 이는 도우미 메서드 `build_twirl_set` 를 사용하여 수행되며, 이 메서드는 각 2큐비트 폴리( `pauli_basis(2)` 에서 가져온 것)를 살펴보고 연산을 보존하는 다른 폴리를 찾습니다.\n",
        "\n",
        "DAG에서 `op_nodes()` 메서드를 사용하여 모든 노드를 반환합니다. DAG는 큐비트에서 중단 없이 실행되는 노드의 시퀀스인 런을 수집하는 데에도 사용할 수 있습니다. 단일 큐비트 실행은 `collect_1q_runs`, 2큐비트 실행은 `collect_2q_runs`, 명령어 이름이 네임리스트에 있는 노드 실행은 `collect_runs` 로 수집할 수 있습니다.  `DAGCircuit` 에는 그래프를 검색하고 탐색하는 다양한 방법이 있습니다.  일반적으로 사용되는 방법 중 하나는 종속성 순서대로 노드를 제공하는 `topological_op_nodes` 입니다. `bfs_successors` 같은 다른 메서드는 주로 노드가 DAG의 후속 작업과 상호 작용하는 방식을 결정하는 데 사용됩니다.\n",
        "\n",
        "이 예제에서는 명령어를 나타내는 각 노드를 미니 DAG로 구축된 서브회로로 대체하고자 합니다. 미니 DAG에는 2쿼비트 양자 레지스터가 추가되었습니다.  `apply_operation_back` 을 사용하여 미니 DAG에 연산을 추가하면 `Instruction` 이 미니 DAG의 출력에 배치됩니다(반면 `apply_operation_front` 은 미니 DAG의 입력에 배치됩니다). 그런 다음 노드는 `substitute_node_with_dag` 를 사용하여 미니 DAG로 대체되고, 이 프로세스는 DAG의 `CXGate` 및 `ECRGate` 의 각 인스턴스( IBM® 백엔드의 2쿼비트 기반 게이트에 해당)에 걸쳐 계속됩니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "80546e59-dcd8-4b61-88b5-00b220409f6d",
      "metadata": {},
      "outputs": [],
      "source": [
        "from qiskit.dagcircuit import DAGCircuit\n",
        "from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate\n",
        "from qiskit.circuit.library import CXGate, ECRGate\n",
        "from qiskit.transpiler import PassManager\n",
        "from qiskit.transpiler.basepasses import TransformationPass\n",
        "from qiskit.quantum_info import Operator, pauli_basis\n",
        "\n",
        "import numpy as np\n",
        "\n",
        "from typing import Iterable, Optional"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "6bb6ff72-43dd-4f8c-a67c-b43af2548b2a",
      "metadata": {},
      "outputs": [],
      "source": [
        "class PauliTwirl(TransformationPass):\n",
        "    \"\"\"Add Pauli twirls to two-qubit gates.\"\"\"\n",
        "\n",
        "    def __init__(\n",
        "        self,\n",
        "        gates_to_twirl: Optional[Iterable[Gate]] = None,\n",
        "    ):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            gates_to_twirl: Names of gates to twirl. The default behavior is to twirl all\n",
        "                two-qubit basis gates, `cx` and `ecr` for IBM backends.\n",
        "        \"\"\"\n",
        "        if gates_to_twirl is None:\n",
        "            gates_to_twirl = [CXGate(), ECRGate()]\n",
        "        self.gates_to_twirl = gates_to_twirl\n",
        "        self.build_twirl_set()\n",
        "        super().__init__()\n",
        "\n",
        "    def build_twirl_set(self):\n",
        "        \"\"\"\n",
        "        Build a set of Paulis to twirl for each gate and store internally as .twirl_set.\n",
        "        \"\"\"\n",
        "        self.twirl_set = {}\n",
        "\n",
        "        # iterate through gates to be twirled\n",
        "        for twirl_gate in self.gates_to_twirl:\n",
        "            twirl_list = []\n",
        "\n",
        "            # iterate through Paulis on left of gate to twirl\n",
        "            for pauli_left in pauli_basis(2):\n",
        "                # iterate through Paulis on right of gate to twirl\n",
        "                for pauli_right in pauli_basis(2):\n",
        "                    # save pairs that produce identical operation as gate to twirl\n",
        "                    if (Operator(pauli_left) @ Operator(twirl_gate)).equiv(\n",
        "                        Operator(twirl_gate) @ pauli_right\n",
        "                    ):\n",
        "                        twirl_list.append((pauli_left, pauli_right))\n",
        "\n",
        "            self.twirl_set[twirl_gate.name] = twirl_list\n",
        "\n",
        "    def run(\n",
        "        self,\n",
        "        dag: DAGCircuit,\n",
        "    ) -> DAGCircuit:\n",
        "        # collect all nodes in DAG and proceed if it is to be twirled\n",
        "        twirling_gate_classes = tuple(\n",
        "            gate.base_class for gate in self.gates_to_twirl\n",
        "        )\n",
        "        for node in dag.op_nodes():\n",
        "            if not isinstance(node.op, twirling_gate_classes):\n",
        "                continue\n",
        "\n",
        "            # random integer to select Pauli twirl pair\n",
        "            pauli_index = np.random.randint(\n",
        "                0, len(self.twirl_set[node.op.name])\n",
        "            )\n",
        "            twirl_pair = self.twirl_set[node.op.name][pauli_index]\n",
        "\n",
        "            # instantiate mini_dag and attach quantum register\n",
        "            mini_dag = DAGCircuit()\n",
        "            register = QuantumRegister(2)\n",
        "            mini_dag.add_qreg(register)\n",
        "\n",
        "            # apply left Pauli, gate to twirl, and right Pauli to empty mini-DAG\n",
        "            mini_dag.apply_operation_back(\n",
        "                twirl_pair[0].to_instruction(), [register[0], register[1]]\n",
        "            )\n",
        "            mini_dag.apply_operation_back(node.op, [register[0], register[1]])\n",
        "            mini_dag.apply_operation_back(\n",
        "                twirl_pair[1].to_instruction(), [register[0], register[1]]\n",
        "            )\n",
        "\n",
        "            # substitute gate to twirl node with twirling mini-DAG\n",
        "            dag.substitute_node_with_dag(node, mini_dag)\n",
        "\n",
        "        return dag"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "da136beb-d1b2-4f68-8274-386e80a49860",
      "metadata": {},
      "source": [
        "<span id=\"use-the-paulitwirl-transpiler-pass\" />\n",
        "\n",
        "## 트랜스파일러 `PauliTwirl` 패스를 사용하십시오\n",
        "\n",
        "다음 코드는 위에서 생성한 패스를 사용하여 회로를 트랜스파일합니다. 단순 회로에 `cx` 및 `ecr` 게이트가 있다고 가정하자.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "9123905d-b4cb-4ae9-9695-4ad77e70bdab",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-transpiler-pass/extracted-outputs/9123905d-b4cb-4ae9-9695-4ad77e70bdab-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "qc = QuantumCircuit(3)\n",
        "qc.cx(0, 1)\n",
        "qc.ecr(1, 2)\n",
        "qc.ecr(1, 0)\n",
        "qc.cx(2, 1)\n",
        "qc.draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "f112c2af-c99a-4733-8118-4786e2dfdfd3",
      "metadata": {},
      "source": [
        "사용자 지정 패스를 적용하려면 `PauliTwirl` 패스를 사용하여 패스 관리자를 빌드하고 50개 회로에서 실행합니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "ea884cd2-2446-4b2d-a0f7-82ef574b11ee",
      "metadata": {},
      "outputs": [],
      "source": [
        "pm = PassManager([PauliTwirl()])\n",
        "twirled_qcs = [pm.run(qc) for _ in range(50)]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3d4d921e-1a43-4f9a-bc30-6c64092d8f6b",
      "metadata": {},
      "source": [
        "이제 각 2큐비트 게이트는 두 개의 폴리 사이에 끼어 있습니다.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "e2515cf3-f8d9-4281-9673-d5a955d7aab9",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "<Image src=\"/docs/images/guides/custom-transpiler-pass/extracted-outputs/e2515cf3-f8d9-4281-9673-d5a955d7aab9-0.svg\" alt=\"Output of the previous code cell\" />"
            ]
          },
          "execution_count": 5,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "twirled_qcs[-1].draw(\"mpl\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "41cccfba-8b65-401c-9d30-66396a3fe3a1",
      "metadata": {},
      "source": [
        "`qiskit.quantum_info` 에서 `Operator` 를 사용하는 경우 연산자는 동일합니다:\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "0f8b4ca1-4205-4b71-941d-0852eebbe917",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "np.True_"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "np.all([Operator(twirled_qc).equiv(qc) for twirled_qc in twirled_qcs])"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "0d680854-13f8-4ccd-9be1-595edb4f942c",
      "metadata": {},
      "source": [
        "<span id=\"next-steps\" />\n",
        "\n",
        "## 다음 단계\n",
        "\n",
        "<Admonition type=\"tip\" title=\"권장사항\">\n",
        "  * 직접 패스를 작성하는 대신 `generate_preset_passmanager` 함수를 사용하는 방법을 알아보려면 [번역 기본 설정 및 구성 옵션](defaults-and-configuration-options) 항목부터 시작하세요.\n",
        "  * [Compare 트랜스파일러 설정](/docs/guides/circuit-transpilation-settings) 가이드를 시도해 보세요.\n",
        "  * [트랜스파일러 API](/docs/api/qiskit/transpiler) 문서를 검토하십시오.\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"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}