Transpilerプラグインの作成
パッケージのバージョン
このページのコードは、以下の要件を使用して開発されました。 これらのバージョン以上を使用することをお勧めします。
qiskit[all]~=2.3.0
Transpilerプラグインを作成することは、Transpilationコードをより広いQiskitコミュニティと共有し、他のユーザーが開発した機能を活用できるようにする素晴らしい方法です。Qiskitコミュニティへの貢献にご関心をお寄せいただきありがとうございます!
Transpilerプラグインを作成する前に、あなたの状況に適したプラグインの種類を決める必要があります。Transpilerプラグインには3種類あります:
- Transpilerステージプラグイン。プリセットのステージ付きパスマネージャーの6つのステージのいずれかに代替できるパスマネージャーを定義する場合に選択してください。
- ユニタリー合成プラグイン。TranspilationコードがユニタリーMatrix(Numpy配列として表現)を入力として受け取り、そのユニタリーを実装する量子Circuitの記述を出力する場合に選択してください。
- 高レベル合成プラグイン。TranspilationコードがClifford演算子や線形関数などの「高レベルオブジェクト」を入力として受け取り、その高レベルオブジェクトを実装する量子Circuitの記述を出力する場合に選択してください。高レベルオブジェクトはOperationクラスのサブクラスで表されます。
作成するプラグインの種類が決まったら、以下の手順に従ってプラグインを作成してください:
- 適切な抽象プラグインクラスのサブクラスを作成します:
- Transpilerステージプラグインの場合はPassManagerStagePlugin、
- ユニタリー合成プラグインの場合はUnitarySynthesisPlugin、
- 高レベル合成プラグインの場合はHighLevelSynthesisPlugin。
- Pythonパッケージのメタデータ(通常は
pyproject.toml、setup.cfg、またはsetup.pyファイルを編集)で、クラスをsetuptoolsエントリーポイントとして公開します。
1つのパッケージが定義できるプラグインの数に制限はありませんが、各プラグインは固有の名前を持つ必要があります。Qiskit SDK自体にもいくつかのプラグインが含まれており、それらの名前は予約されています。予約済みの名前は以下の通りです:
- Transpilerステージプラグイン:この表を参照してください。
- ユニタリー合成プラグイン:
default、aqc、sk - 高レベル合成プラグイン:
| 演算クラス | 演算名 | 予約済みの名前 |
|---|---|---|
| Clifford | clifford | default, ag, bm, greedy, layers, lnn |
| LinearFunction | linear_function | default, kms, pmh |
| PermutationGate | permutation | default, kms, basic, acg, token_swapper |
以降のセクションでは、さまざまな種類のプラグインについてこれらの手順の例を示します。これらの例では、my_qiskit_pluginというPythonパッケージを作成していると仮定しています。Pythonパッケージの作成については、Pythonウェブサイトのこのチュートリアルを参照してください。
例:Transpilerステージプラグインの作成
この例では、layoutステージ用のTranspilerステージプラグインを作成します(Qiskitの組み込みTranspilationパイプラインの6つのステージの説明についてはTranspilerステージを参照してください)。
このプラグインは、要求された最適化レベルに応じたトライアル数でVF2Layoutを実行するだけです。
まず、PassManagerStagePluginのサブクラスを作成します。実装する必要があるメソッドはpass_managerと呼ばれる1つのメソッドです。このメソッドはPassManagerConfigを入力として受け取り、定義するパスマネージャーを返します。PassManagerConfigオブジェクトには、ターゲットBackendに関する情報(カップリングマップや基底Gateなど)が格納されています。
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
# This import is needed for python versions prior to 3.10
from __future__ import annotations
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import VF2Layout
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import common
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePlugin,
)
class MyLayoutPlugin(PassManagerStagePlugin):
def pass_manager(
self,
pass_manager_config: PassManagerConfig,
optimization_level: int | None = None,
) -> PassManager:
layout_pm = PassManager(
[
VF2Layout(
coupling_map=pass_manager_config.coupling_map,
properties=pass_manager_config.backend_properties,
max_trials=optimization_level * 10 + 1,
target=pass_manager_config.target,
)
]
)
layout_pm += common.generate_embed_passmanager(
pass_manager_config.coupling_map
)
return layout_pm
次に、PythonパッケージのメタデータにエントリーポイントとしてプラグインをJで公開します。
ここでは、定義したクラスがmy_qiskit_pluginモジュールで公開されていると仮定します(例えば、my_qiskit_pluginモジュールの__init__.pyファイルでインポートされている場合など)。
パッケージのpyproject.toml、setup.cfg、またはsetup.pyファイルを編集します(Pythonプロジェクトのメタデータを格納するために選択したファイルの種類に応じて):
- pyproject.toml
- setup.cfg
- setup.py
[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"
[options.entry_points]
qiskit.transpiler.layout =
my_layout = my_qiskit_plugin:MyLayoutPlugin
from setuptools import setup
setup(
# ...,
entry_points={
'qiskit.transpiler.layout': [
'my_layout = my_qiskit_plugin:MyLayoutPlugin',
]
}
)
各Transpilerステージのエントリーポイントと期待される動作については、Transpilerプラグインステージの表を参照してください。
プラグインがQiskitによって正常に検出されているかどうかを確認するには、プラグインパッケージをインストールし、Transpilerプラグインのインストール済みプラグインの一覧表示手順に従って、プラグインが一覧に表示されていることを確認してください:
from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins
list_stage_plugins("layout")
['default', 'dense', 'sabre', 'trivial']
サンプルプラグインがインストールされている場合、my_layoutという名前がこの一覧に表示されます。
Transpilerステージプラグインの出発点として組み込みのTranspilerステージを使用したい場合は、PassManagerStagePluginManagerを使用して組み込みTranspilerステージのパスマネージャーを取得できます。以下のコードセルでは、最適化レベル3の組み込み最適化ステージを取得する方法を示します。
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePluginManager,
)
# Initialize the plugin manager
plugin_manager = PassManagerStagePluginManager()
# Here we create a pass manager config to use as an example.
# Instead, you should use the pass manager config that you already received as input
# to the pass_manager method of your PassManagerStagePlugin.
pass_manager_config = PassManagerConfig()
# Obtain the desired built-in transpiler stage
optimization = plugin_manager.get_passmanager_stage(
"optimization", "default", pass_manager_config, optimization_level=3
)
例:ユニタリー合成プラグインの作成
この例では、組み込みのUnitarySynthesis TranspilationパスをそのままGateの合成に使用するユニタリー合成プラグインを作成します。もちろん、実際のプラグインではこれよりも興味深い処理を行うことになります。
UnitarySynthesisPluginクラスは、ユニタリー合成プラグインのインターフェースと規約を定義します。主要なメソッドは
runで、
ユニタリーMatrixを格納するNumpy配列を入力として受け取り、
そのユニタリーMatrixから合成されたCircuitを表すDAGCircuitを返します。
runメソッドに加えて、定義する必要があるいくつかのプロパティメソッドがあります。
必要なすべてのプロパティのドキュメントについてはUnitarySynthesisPluginを参照してください。
UnitarySynthesisPluginサブクラスを作成しましょう:
import numpy as np
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.converters import circuit_to_dag
from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.quantum_info import Operator
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.transpiler.passes.synthesis.plugin import UnitarySynthesisPlugin
class MyUnitarySynthesisPlugin(UnitarySynthesisPlugin):
@property
def supports_basis_gates(self):
# Returns True if the plugin can target a list of basis gates
return True
@property
def supports_coupling_map(self):
# Returns True if the plugin can synthesize for a given coupling map
return False
@property
def supports_natural_direction(self):
# Returns True if the plugin supports a toggle for considering
# directionality of 2-qubit gates
return False
@property
def supports_pulse_optimize(self):
# Returns True if the plugin can optimize pulses during synthesis
return False
@property
def supports_gate_lengths(self):
# Returns True if the plugin can accept information about gate lengths
return False
@property
def supports_gate_errors(self):
# Returns True if the plugin can accept information about gate errors
return False
@property
def supports_gate_lengths_by_qubit(self):
# Returns True if the plugin can accept information about gate lengths
# (The format of the input differs from supports_gate_lengths)
return False
@property
def supports_gate_errors_by_qubit(self):
# Returns True if the plugin can accept information about gate errors
# (The format of the input differs from supports_gate_errors)
return False
@property
def min_qubits(self):
# Returns the minimum number of qubits the plugin supports
return None
@property
def max_qubits(self):
# Returns the maximum number of qubits the plugin supports
return None
@property
def supported_bases(self):
# Returns a dictionary of supported bases for synthesis
return None
def run(self, unitary: np.ndarray, **options) -> DAGCircuit:
basis_gates = options["basis_gates"]
synth_pass = UnitarySynthesis(basis_gates, min_qubits=3)
qubits = QuantumRegister(3)
circuit = QuantumCircuit(qubits)
circuit.append(Operator(unitary).to_instruction(), qubits)
dag_circuit = synth_pass.run(circuit_to_dag(circuit))
return dag_circuit
runメソッドで利用可能な入力があなたの目的に不十分な場合は、要件を説明したissueを開いてください。追加のオプション入力の追加など、プラグインインターフェースへの変更は、既存のプラグインに変更を要求しないよう、後方互換性のある方法で行われます。
supports_で始まるすべてのメソッドは、インターフェースの一部としてUnitarySynthesisPlugin派生クラス上で予約されています。抽象クラスで定義されていないカスタムのsupports_*メソッドをサブクラスで定義しないでください。
次に、Pythonパッケージのメタデータにエントリーポイントとしてプラグインを公開します。
ここでは、定義したクラスがmy_qiskit_pluginモジュールで公開 されていると仮定します(例えば、my_qiskit_pluginモジュールの__init__.pyファイルでインポートされている場合など)。
パッケージのpyproject.toml、setup.cfg、またはsetup.pyファイルを編集します:
- pyproject.toml
- setup.cfg
- setup.py
[project.entry-points."qiskit.unitary_synthesis"]
"my_unitary_synthesis" = "my_qiskit_plugin:MyUnitarySynthesisPlugin"
[options.entry_points]
qiskit.unitary_synthesis =
my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin
from setuptools import setup
setup(
# ...,
entry_points={
'qiskit.unitary_synthesis': [
'my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin',
]
}
)
前述の通り、プロジェクトがpyproject.tomlの代わりにsetup.cfgまたはsetup.pyを使用している場合は、これらの行をあなたの状況に合わせて適応する方法についてsetuptoolsのドキュメントを参照してください。
プラグインがQiskitによって正常に検出されているかどうかを確認するには、プラグインパッケージをインストールし、Transpilerプラグインのインストール済みプラグインの一覧表示手順に従って、プラグインが一覧に表示されていることを確認してください:
from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names
unitary_synthesis_plugin_names()
['aqc', 'clifford', 'default', 'gridsynth', 'sk']
サンプルプラグインがインストールされている場合、my_unitary_synthesisという名前がこの一覧に表示されます。
複数のオプションを公開するユニタリー合成プラグインに対応するため、
プラグインインターフェースにはユーザーが自由形式の
設定辞書を提供するオプションがあります。これはoptionsキーワード引数を通じてrunメソッドに渡されます。プラグインにこのような設定オプションがある場合は、それらを明確にドキュメント化する必要があります。
例:高レベル合成プラグインの作成
この例では、組み込みのsynth_clifford_bm関数をそのまま使用してClifford演算子を合成する高レベル合成プラグインを作成します。
HighLevelSynthesisPluginクラスは、高レベル合成プラグインのインターフェースと規約を定義します。主要なメソッドはrunです。
位置引数high_level_objectは、合成される「高レベル」オブジェクトを表すOperationです。例えば、
LinearFunctionや
Cliffordなどが該当します。
以下のキーワード引数が存在します:
targetはターゲットBackendを指定し、カップリングマップ、サポートされているGateセットなど、ターゲット固有のすべての情報にプラグインがアクセスできるようにします。coupling_mapはカップリングマップのみを指定し、targetが指定されていない場合にのみ使用されます。qubitsは高レベルオブジェクトが定義されているQubitのリストを指定します。物理Circuitで合成が行われる場合に使用されます。Noneの値は、レイアウトがまだ選択されておらず、このオペレーションが動作するターゲットまたはカップリングマップの物理Qubitがまだ決定されていないことを示します。optionsは、プラグイン固有のオプションのための自由形式の設定辞書です。プラグインにこのような設定オプションがある場合は、それらを明確にドキュメント化する必要があります。
runメソッドは、その高レベルオブジェクトから合成されたCircuitを表すQuantumCircuitを返します。
Noneを返すことも許可されており、その場合はプラグインが指定された高レベルオブジェクトを合成できないことを示します。
高レベルオブジェクトの実際の合成は、
HighLevelSynthesis
Transpilerパスによって実行されます。
runメソ ッドに加えて、定義する必要があるいくつかのプロパティメソッドがあります。
必要なすべてのプロパティのドキュメントについてはHighLevelSynthesisPluginを参照してください。
HighLevelSynthesisPluginサブクラスを定義しましょう:
from qiskit.synthesis import synth_clifford_bm
from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin
class MyCliffordSynthesisPlugin(HighLevelSynthesisPlugin):
def run(
self,
high_level_object,
coupling_map=None,
target=None,
qubits=None,
**options,
) -> QuantumCircuit:
if high_level_object.num_qubits <= 3:
return synth_clifford_bm(high_level_object)
else:
return None
このプラグインは、最大3 QubitのClifford型のオブジェクトをsynth_clifford_bmメソッドを使用して合成します。
次に、Pythonパッケージのメタデータにエントリーポイントとしてプラグインを公開します。
ここでは、定義したクラスがmy_qiskit_pluginモジュールで公開されていると仮定します(例えば、my_qiskit_pluginモジュールの__init__.pyファイルでインポートされている場合など)。
パッケージのpyproject.toml、setup.cfg、またはsetup.pyファイルを編集します:
- pyproject.toml
- setup.cfg
- setup.py
[project.entry-points."qiskit.synthesis"]
"clifford.my_clifford_synthesis" = "my_qiskit_plugin:MyCliffordSynthesisPlugin"
[options.entry_points]
qiskit.synthesis =
clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin
from setuptools import setup
setup(
# ...,
entry_points={
'qiskit.synthesis': [
'clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin',
]
}
)
nameはドット(.)で区切られた2つの部分で構成されます:
- プラグインが合成するOperationの型の名前(この場合は
clifford)。この文字列はOperationクラスのname属性に対応し、クラス自体の名前ではないことに注意してください。 - プラグインの名前(この場合は
special)。
前述の通り、プロジェクトがpyproject.tomlの代わりにsetup.cfgまたはsetup.pyを使用している場合は、これらの行をあなたの状況に合 わせて適応する方法についてsetuptoolsのドキュメントを参照してください。
プラグインがQiskitによって正常に検出されているかどうかを確認するには、プラグインパッケージをインストールし、Transpilerプラグインのインストール済みプラグインの一覧表示手順に従って、プラグインが一覧に表示されていることを確認してください:
from qiskit.transpiler.passes.synthesis import (
high_level_synthesis_plugin_names,
)
high_level_synthesis_plugin_names("clifford")
['ag', 'bm', 'default', 'greedy', 'layers', 'lnn', 'rb_default']
サンプルプラグインがインストールされている場合、my_clifford_synthesisという名前がこの一覧に表示されます。
- プラグインをQiskitエコシステムに登録しましょう!
- 量子Circuitのトランスパイルと実行の例についてはチュートリアルをご覧ください。