Skip to main content
IBM Quantum Platform

カスタム・トランスパイラー・パスを書く

  • このページのコードは、以下の要件に基づいて開発された。 これらのバージョンまたは新しいバージョンの使用をお勧めします。

    qiskit[all]~=2.5.1
    

Qiskit SDKでは、カスタムトランスピレーションパスを作成し、 PassManager オブジェクト内で実行したり、 StagedPassManager に追加したりすることができます。 ここでは、量子回路のノイズの多い量子ゲートに対してパウリ・ツワーリングを実行するパスの作成に焦点を当て、トランスパイラ・パスの書き方を示します。 この例ではDAGを使用している。DAGは TransformationPass 型のパスで操作されるオブジェクトである。

  • パスを構築する前に、Qiskitの量子回路の内部表現である有向無サイクルグラフ(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 回転を加える回路
    回路

    この回路のDAGを表示するには、 qiskit.tools.visualization.dag_drawer() 。 グラフのノードには3種類ある:量子ビット/軌道ノード(緑)、演算ノード(青)、出力ノード(赤)。 各エッジは、2つのノード間のデータの流れ(または依存関係)を示す。

    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と連携して動作します。 分析パスは、DAGとその property_set.の両方で機能します。 彼らはDAGを変更することはできませんが、.を変更することはできます property_set。 これに対し、トランスフォーメーション・パスはDAGを変更するものであり、DAGを読み取ることはできるが(書き込みはできない) property_set。 例えば、変換パスでは回路をそのISA に変換したり、ルーティングパスを実行して必要な場所にSWAPゲートを挿入したりします。


トランスパイラパス PauliTwirl を作成する

以下の例では、パウリ・ツワールを追加するトランスパイラー・パスを構築している。 パウリ・ツワーリングは、量子ビットがどのようにノイズの多いチャネルを経験するかをランダムにするエラー抑制戦略であり、この例では2量子ビットゲートを想定している(1量子ビットゲートよりもはるかにエラーが発生しやすいため)。 パウリの回転は2量子ビットのゲートの動作には影響しない。 それらは、2量子ビットゲートの (左側)に適用されたものが、2量子ビットゲートの (右側)に適用されたものによって打ち消されるように選ばれる。 この意味で、2量子ビットの演算は同じだが、その方法は異なる。 パウリ・ツワーリングの利点のひとつは、コヒーレント誤差を確率的誤差に変えることで、より平均化することで改善できることだ。

Transpilerは DAGに作用するので、オーバーライドする重要なメソッドは .run()、DAGを入力として受け取る。 このようにポーリスのペアを初期化することで、各2量子ビットゲートの動作が保持される。 これはヘルパーメソッド build_twirl_set を使って行われる。ヘルパーメソッドは、( pauli_basis(2) から得られるような)各2量子ビットパウリを調べ、演算を保持するもう1つのパウリを見つける。

DAG から、 op_nodes() メソッドを使用して、そのすべてのノードを返す。 DAGは、量子ビット上で途切れることなく実行されるノードのシーケンスであるランを収集するためにも使用できる。 これらは、 collect_1q_runs で1量子ビットラン、 collect_2q_runs で2量子ビットラン、 collect_runs で命令名がネームリストにあるノードのランとして収集できる。 DAGCircuit には、グラフを検索したり走査したりするための多くのメソッドがある。 よく使われる方法の1つは、 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 を使用する

以下のコードは、上記で作成したパスを使用して回路をトランスパイルします。 と ecr``cx ゲートからなる単純な回路を考えよ。

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量子ビットのゲートは現在、2つのポーリに挟まれている。

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

Output:

Output of the previous code cell

qiskit.quantum_infoOperator

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

Output:

np.True_

次のステップ

推奨事項
このページは役に立ちましたか?
バグや誤字の報告、またはコンテンツの要求はGitHubで行ってください。