メインコンテンツへスキップ

カスタムトランスパイラーパスを作成する

パッケージバージョン

このページのコードは以下の要件を使用して開発されました。 これらのバージョン以降を使用することを推奨します。

qiskit[all]~=2.3.0

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

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit

背景:DAG表現

パスを構築する前に、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])
qc.rz(np.pi/2, qr[1]).c_if(cr, 2)
qc.draw(output='mpl')

ベル状態を準備し、測定結果に応じてR_Z回転を適用する回路。

qiskit.tools.visualization.dag_drawer()関数を使用して、この回路のDAGを表示します。グラフノードには3種類あります:qubit/clbitノード(緑)、操作ノード(青)、出力ノード(赤)。各エッジは2つのノード間のデータフロー(または依存関係)を示します。

from qiskit.converters import circuit_to_dag
from qiskit.tools.visualization import dag_drawer

dag = circuit_to_dag(qc)
dag_drawer(dag)

回路のDAGは方向性のあるエッジで接続されたノードから構成されています。これはqubitまたは古典ビット、操作、そしてデータがどのように流れるかを視覚的に表す方法です。

トランスパイラーパス

トランスパイラーパスはAnalysisPassまたはTransformationPassのどちらかに分類されます。パスは一般にDAGproperty_set(分析パスによって決定されたプロパティを格納するための辞書的なオブジェクト)を使用します。分析パスはDAGとそのproperty_setの両方を操作します。DAGを変更することはできませんが、property_setを変更することはできます。これは変換パスとは対照的で、変換パスはDAGを変更し、property_setを読み取る(ただし書き込みはできない)ことができます。たとえば、変換パスは回路をそのISAに変換したり、必要な場所にSWAPゲートを挿入するルーティングパスを実行したりします。

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

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

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

DAGから、op_nodes()メソッドを使用してすべてのノードを返します。DAGは、qubit上で中断なく実行されるノードのシーケンスであるランの収集にも使用できます。これらは、collect_1q_runsで1量子ビットのラン、collect_2q_runsで2量子ビットのラン、collect_runsで命令名がネームリストにあるノードのランとして収集できます。DAGCircuitにはグラフの検索とトラバースのための多くのメソッドがあります。よく使われるメソッドの1つはtopological_op_nodesで、依存関係の順序でノードを提供します。bfs_successorsなどの他のメソッドは、主にノードがDAG上の後続の操作とどのように相互作用するかを判断するために使用されます。

この例では、命令を表す各ノードを、ミニDAGとして構築されたサブ回路に置き換えます。ミニDAGには2量子ビットの量子レジスターが追加されます。操作はミニDAGにapply_operation_backを使用して追加され、ミニDAGの出力にInstructionを配置します(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トランスパイラーパスを使用する

以下のコードは、上で作成したパスを使用して回路をトランスパイルします。cxゲートとecrゲートを持つシンプルな回路を考えます。

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

前のコードセルの出力

カスタムパスを適用するには、PauliTwirlパスを使用してパスマネージャーを構築し、50個の回路に対して実行します。

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

各2量子ビットゲートは、2つのパウリの間に挟まれています。

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

前のコードセルの出力

qiskit.quantum_infoOperatorを使用すると、演算子は同じです:

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

次のステップ

推奨事項