Skip to main content
IBM Quantum Platform

사용자 정의 트랜스파일러 패스 작성

  • 이 페이지의 코드는 다음 요구 사항을 사용하여 개발되었습니다. 다음 버전 이상을 사용하는 것이 좋습니다.

    qiskit[all]~=2.5.2
    

키스킷 SDK를 사용하면 사용자 지정 트랜스파일레이션 패스를 생성하여 PassManager 객체에서 실행하거나 StagedPassManager 에 추가할 수 있습니다. 여기서는 양자 회로에서 잡음이 많은 양자 게이트에서 폴리 회전을 수행하는 패스를 구축하는 데 중점을 두고 트랜스파일러 패스를 작성하는 방법을 보여드리겠습니다. 이 예제에서는 TransformationPass 유형의 패스로 조작되는 객체인 DAG를 사용합니다.

  • 패스를 구축하기 전에 키스킷에서 양자 회로의 내부 표현인 방향성 비순환 그래프(DAG )를 소개하는 것이 중요합니다(개요는 이 튜토리얼을 참조하세요). 다음 단계를 수행하려면 DAG 플로팅 함수용 graphviz 라이브러리를 설치합니다.

    Qiskit에서는 트랜스파일레이션 단계 내에서 회로가 DAG(방향성 비순환 그래프)를 사용하여 표현됩니다. 일반적으로 DAG는 정점 (노드라고도 함)과 특정 방향으로 정점 쌍을 연결하는 방향성 변으로 구성됩니다. 이 표현은 개별 DagNode 객체들로 구성된 객체들을 qiskit.dagcircuit.DAGCircuit 사용하여 저장됩니다. 이 표현 방식이 순수한 게이트 목록(즉, 네트리스트 )에 비해 갖는 장점은 연산 간 정보 흐름이 명시적으로 드러나 변환 결정을 내리기 쉽다는 점이다.

    이 예는 벨 상태를 준비하고 측정 결과에 따라 RZR_Z 회전을 적용하는 간단한 회로를 생성하여 DAG를 설명합니다.

    from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
    import numpy as np
    
    qr = QuantumRegister(3, 'qr')
    cr = ClassicalRegister(3, 'cr')
    qc = QuantumCircuit(qr, cr)
    
    qc.h(qr[0])
    qc.cx(qr[0], qr[1])
    qc.measure(qr[0], cr[0])
    with qc.if_test((cr, 2)):
        qc.rz(np.pi/2, qr[1])
    qc.draw(output='mpl')
    
    벨 상태를 준비하고 측정 결과에 따라 R_Z 회전을 적용하는 회로
    회로

    qiskit.tools.visualization.dag_drawer() 함수를 사용하여 이 회로의 DAG를 확인합니다. 그래프 노드에는 쿼비트/클릭비트 노드(녹색), 연산 노드(파란색), 출력 노드(빨간색)의 세 가지 종류가 있습니다. 각 에지는 두 노드 간의 데이터 흐름(또는 종속성)을 나타냅니다.

    from qiskit.converters import circuit_to_dag
    from qiskit.visualization import dag_drawer
    
    dag = circuit_to_dag(qc)
    dag_drawer(dag)
    회로의 DAG는 방향 에지로 연결된 노드로 구성됩니다. 큐비트 또는 클래식 비트, 연산 및 데이터 흐름 방식을 시각적으로 표현하는 방법입니다.
    DAG

트랜스파일러 통과

트랜스파일러 패스는 또는 AnalysisPass 로 분류됩니다 TransformationPass. 패스는 일반적으로 DAG 및 분석 패스에 의해 결정된 속성을 저장하기 위한 사전 property_set(dictionary)과 유사한 객체인 와 함께 작동합니다. 분석 패스는 DAG와 그 property_set. 모두에서 작동합니다. 그들은 DAG를 수정할 수는 없지만,. property_set을 수정할 수는 있습니다. 이는 DAG를 수정하는 변환 패스와는 대조적이며, DAG를 읽을 수는 있지만(쓰기는 불가능합니다) property_set. 예를 들어, 변환 단계에서는 회로를 해당 ISA로 변환하거나, 필요한 곳에 SWAP 게이트를 삽입하기 위해 배선 단계를 수행합니다.


트랜스파일러 PauliTwirl 패스 생성

다음 예제는 폴리 트위클을 추가하는 트랜스파일러 패스를 구성하는 예제입니다. 폴리 트위링은 큐비트가 잡음 채널을 경험하는 방식을 무작위로 변경하는 오류 억제 전략으로, 이 예에서는 2큐비트 게이트로 가정합니다(단일 큐비트 게이트보다 훨씬 오류가 발생하기 쉽기 때문입니다). 폴리 회전은 2큐비트 게이트의 작동에 영향을 미치지 않습니다. 2큐비트 게이트 이전 (왼쪽)에 적용된 것과 2큐비트 게이트 이후 (오른쪽)에 적용된 것이 상쇄되도록 선택됩니다. 이런 의미에서 두 큐비트 연산은 동일하지만 수행 방식이 다릅니다. 폴리 트월링의 한 가지 장점은 일관된 오류를 확률적 오류로 바꾸어 평균을 더 많이 내서 개선할 수 있다는 것입니다.

트랜스파일러 패스는 DAG에서 작동하므로 재정의해야 할 중요한 메서드는 DAG를 입력으로 받는 .run() 입니다. 그림과 같이 폴리스 쌍을 초기화하면 각 2쿼비트 게이트의 작동이 유지됩니다. 이는 도우미 메서드 build_twirl_set 를 사용하여 수행되며, 이 메서드는 각 2큐비트 폴리( pauli_basis(2) 에서 가져온 것)를 살펴보고 연산을 보존하는 다른 폴리를 찾습니다.

DAG에서 op_nodes() 메서드를 사용하여 모든 노드를 반환합니다. DAG는 큐비트에서 중단 없이 실행되는 노드의 시퀀스인 런을 수집하는 데에도 사용할 수 있습니다. 단일 큐비트 실행은 collect_1q_runs, 2큐비트 실행은 collect_2q_runs, 명령어 이름이 네임리스트에 있는 노드 실행은 collect_runs 로 수집할 수 있습니다. DAGCircuit 에는 그래프를 검색하고 탐색하는 다양한 방법이 있습니다. 일반적으로 사용되는 방법 중 하나는 종속성 순서대로 노드를 제공하는 topological_op_nodes 입니다. bfs_successors 같은 다른 메서드는 주로 노드가 DAG의 후속 작업과 상호 작용하는 방식을 결정하는 데 사용됩니다.

이 예제에서는 명령어를 나타내는 각 노드를 미니 DAG로 구축된 서브회로로 대체하고자 합니다. 미니 DAG에는 2쿼비트 양자 레지스터가 추가되었습니다. apply_operation_back 을 사용하여 미니 DAG에 연산을 추가하면 Instruction 이 미니 DAG의 출력에 배치됩니다(반면 apply_operation_front 은 미니 DAG의 입력에 배치됩니다). 그런 다음 노드는 substitute_node_with_dag 를 사용하여 미니 DAG로 대체되고, 이 프로세스는 DAG의 CXGateECRGate 의 각 인스턴스( IBM® 백엔드의 2쿼비트 기반 게이트에 해당)에 걸쳐 계속됩니다.

from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate
from qiskit.circuit.library import CXGate, ECRGate
from qiskit.transpiler import PassManager
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.quantum_info import Operator, pauli_basis

import numpy as np

from typing import Iterable, Optional
class PauliTwirl(TransformationPass):
    """Add Pauli twirls to two-qubit gates."""

    def __init__(
        self,
        gates_to_twirl: Optional[Iterable[Gate]] = None,
    ):
        """
        Args:
            gates_to_twirl: Names of gates to twirl. The default behavior is to twirl all
                two-qubit basis gates, `cx` and `ecr` for IBM backends.
        """
        if gates_to_twirl is None:
            gates_to_twirl = [CXGate(), ECRGate()]
        self.gates_to_twirl = gates_to_twirl
        self.build_twirl_set()
        super().__init__()

    def build_twirl_set(self):
        """
        Build a set of Paulis to twirl for each gate and store internally as .twirl_set.
        """
        self.twirl_set = {}

        # iterate through gates to be twirled
        for twirl_gate in self.gates_to_twirl:
            twirl_list = []

            # iterate through Paulis on left of gate to twirl
            for pauli_left in pauli_basis(2):
                # iterate through Paulis on right of gate to twirl
                for pauli_right in pauli_basis(2):
                    # save pairs that produce identical operation as gate to twirl
                    if (Operator(pauli_left) @ Operator(twirl_gate)).equiv(
                        Operator(twirl_gate) @ pauli_right
                    ):
                        twirl_list.append((pauli_left, pauli_right))

            self.twirl_set[twirl_gate.name] = twirl_list

    def run(
        self,
        dag: DAGCircuit,
    ) -> DAGCircuit:
        # collect all nodes in DAG and proceed if it is to be twirled
        twirling_gate_classes = tuple(
            gate.base_class for gate in self.gates_to_twirl
        )
        for node in dag.op_nodes():
            if not isinstance(node.op, twirling_gate_classes):
                continue

            # random integer to select Pauli twirl pair
            pauli_index = np.random.randint(
                0, len(self.twirl_set[node.op.name])
            )
            twirl_pair = self.twirl_set[node.op.name][pauli_index]

            # instantiate mini_dag and attach quantum register
            mini_dag = DAGCircuit()
            register = QuantumRegister(2)
            mini_dag.add_qreg(register)

            # apply left Pauli, gate to twirl, and right Pauli to empty mini-DAG
            mini_dag.apply_operation_back(
                twirl_pair[0].to_instruction(), [register[0], register[1]]
            )
            mini_dag.apply_operation_back(node.op, [register[0], register[1]])
            mini_dag.apply_operation_back(
                twirl_pair[1].to_instruction(), [register[0], register[1]]
            )

            # substitute gate to twirl node with twirling mini-DAG
            dag.substitute_node_with_dag(node, mini_dag)

        return dag

트랜스파일러 PauliTwirl 패스를 사용하십시오

다음 코드는 위에서 생성한 패스를 사용하여 회로를 트랜스파일합니다. 단순 회로에 cxecr 게이트가 있다고 가정하자.

qc = QuantumCircuit(3)
qc.cx(0, 1)
qc.ecr(1, 2)
qc.ecr(1, 0)
qc.cx(2, 1)
qc.draw("mpl")

Output:

Output of the previous code cell

사용자 지정 패스를 적용하려면 PauliTwirl 패스를 사용하여 패스 관리자를 빌드하고 50개 회로에서 실행합니다.

pm = PassManager([PauliTwirl()])
twirled_qcs = [pm.run(qc) for _ in range(50)]

이제 각 2큐비트 게이트는 두 개의 폴리 사이에 끼어 있습니다.

twirled_qcs[-1].draw("mpl")

Output:

Output of the previous code cell

qiskit.quantum_info 에서 Operator 를 사용하는 경우 연산자는 동일합니다:

np.all([Operator(twirled_qc).equiv(qc) for twirled_qc in twirled_qcs])

Output:

np.True_

다음 단계

권장사항
이 페이지가 도움이 되었습니까?
GitHub에서 버그, 오타를 보고하거나 컨텐츠를 요청하십시오.