Перейти до основного вмісту

Створи плагін транспілятора

Версії пакетів

Код на цій сторінці розроблено з використанням наступних залежностей. Рекомендуємо використовувати ці або новіші версії.

qiskit[all]~=2.3.0

Створення плагіна транспілятора — чудовий спосіб поділитися своїм кодом транспіляції з широкою спільнотою Qiskit, дозволяючи іншим користувачам скористатися функціональністю, яку ти розробив. Дякуємо за інтерес до розвитку спільноти Qiskit!

Перш ніж створювати плагін транспілятора, потрібно визначити, який тип плагіна підходить для твоєї ситуації. Існують три типи плагінів транспілятора:

  • Плагін стадії транспілятора. Обери цей варіант, якщо ти визначаєш менеджер проходів, який можна підставити замість однієї з 6 стадій заздалегідь налаштованого поетапного менеджера проходів.
  • Плагін синтезу унітарного оператора. Обери цей варіант, якщо твій код транспіляції приймає на вхід унітарну матрицю (представлену як масив Numpy) і повертає опис квантового Circuit, що реалізує цей унітарний оператор.
  • Плагін синтезу високорівневих об'єктів. Обери цей варіант, якщо твій код транспіляції приймає на вхід "високорівневий об'єкт" — наприклад, оператор Кліффорда або лінійну функцію — і повертає опис квантового Circuit, що реалізує цей об'єкт. Високорівневі об'єкти представлені підкласами класу Operation.

Після того як ти визначив тип плагіна, виконай ці кроки для його створення:

  1. Створи підклас відповідного абстрактного класу плагіна:
  2. Оголоси клас як точку входу setuptools у метаданих пакета — зазвичай це робиться шляхом редагування файлу pyproject.toml, setup.cfg або setup.py твого Python-пакета.

Кількість плагінів в одному пакеті необмежена, але кожен плагін повинен мати унікальне ім'я. Сам Qiskit SDK включає кілька плагінів, чиї імена також зарезервовані. Зарезервовані імена:

  • Плагіни стадій транспілятора: дивись цю таблицю.
  • Плагіни синтезу унітарного оператора: default, aqc, sk
  • Плагіни синтезу високорівневих об'єктів:
Клас операціїНазва операціїЗарезервовані імена
Cliffordclifforddefault, ag, bm, greedy, layers, lnn
LinearFunctionlinear_functiondefault, kms, pmh
PermutationGatepermutationdefault, kms, basic, acg, token_swapper

У наступних розділах ми наводимо приклади цих кроків для різних типів плагінів. У цих прикладах передбачається, що ми створюємо Python-пакет під назвою my_qiskit_plugin. Щоб дізнатися більше про створення Python-пакетів, можна ознайомитися з цим посібником на сайті Python.

Приклад: Створення плагіна стадії транспілятора

У цьому прикладі ми створюємо плагін стадії транспілятора для стадії layout (опис 6 стадій вбудованого конвеєра транспіляції Qiskit дивись у розділі Стадії транспілятора). Наш плагін просто запускає VF2Layout з кількістю спроб, що залежить від запитаного рівня оптимізації.

Спочатку ми створюємо підклас PassManagerStagePlugin. Потрібно реалізувати один метод — pass_manager. Цей метод приймає на вхід 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-пакета. Тут передбачається, що визначений нами клас доступний у модулі my_qiskit_plugin — наприклад, імпортований у файлі __init__.py модуля my_qiskit_plugin. Редагуємо файл pyproject.toml, setup.cfg або setup.py нашого пакета (залежно від того, який файл ти обрав для зберігання метаданих Python-проєкту):

[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"

Дивись таблицю стадій плагінів транспілятора, де вказані точки входу та вимоги для кожної стадії транспілятора.

Щоб переконатися, що Qiskit успішно виявляє твій плагін, встанови пакет з плагіном і дотримуйся інструкцій у розділі Плагіни транспілятора для перегляду списку встановлених плагінів — переконайся, що твій плагін є в списку:

from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins

list_stage_plugins("layout")
['default', 'dense', 'sabre', 'trivial']

Якби наш приклад плагіна було встановлено, то назва my_layout з'явилася б у цьому списку.

Якщо ти хочеш взяти вбудовану стадію транспілятора за основу для свого плагіна, можна отримати менеджер проходів вбудованої стадії за допомогою PassManagerStagePluginManager. Наступний блок коду демонструє, як отримати вбудовану стадію оптимізації для рівня оптимізації 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 для синтезу Gate. Звісно, твій власний плагін робитиме щось цікавіше.

Клас UnitarySynthesisPlugin визначає інтерфейс і контракт для плагінів синтезу унітарного оператора. Основний метод — run, який приймає на вхід масив Numpy, що зберігає унітарну матрицю, і повертає DAGCircuit, що представляє Circuit, синтезований з цієї унітарної матриці. Крім методу 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 — наприклад, імпортований у файлі __init__.py модуля my_qiskit_plugin. Редагуємо файл pyproject.toml, setup.cfg або setup.py нашого пакета:

[project.entry-points."qiskit.unitary_synthesis"]
"my_unitary_synthesis" = "my_qiskit_plugin:MyUnitarySynthesisPlugin"

Як і раніше, якщо твій проєкт використовує setup.cfg або setup.py замість pyproject.toml, дивись документацію setuptools, щоб дізнатися, як адаптувати ці рядки для твоєї ситуації.

Щоб переконатися, що Qiskit успішно виявляє твій плагін, встанови пакет з плагіном і дотримуйся інструкцій у розділі Плагіни транспілятора для перегляду списку встановлених плагінів — переконайся, що твій плагін є в списку:

from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names

unitary_synthesis_plugin_names()
['aqc', 'clifford', 'default', 'gridsynth', 'sk']

Якби наш приклад плагіна було встановлено, то назва my_unitary_synthesis з'явилася б у цьому списку.

Щоб підтримати плагіни синтезу унітарного оператора, які надають кілька опцій, інтерфейс плагіна передбачає можливість передачі користувачем словника конфігурації у вільній формі. Він передаватиметься до методу run через іменований аргумент options. Якщо твій плагін має такі параметри конфігурації — обов'язково задокументуй їх.

Приклад: Створення плагіна синтезу високорівневих об'єктів

У цьому прикладі ми створимо плагін синтезу високорівневих об'єктів, який просто використовує вбудовану функцію synth_clifford_bm для синтезу оператора Кліффорда.

Клас HighLevelSynthesisPlugin визначає інтерфейс і контракт для плагінів синтезу високорівневих об'єктів. Основний метод — run. Позиційний аргумент high_level_object — це Operation, що представляє "високорівневий" об'єкт для синтезу. Наприклад, це може бути LinearFunction або Clifford. Присутні такі іменовані аргументи:

  • target вказує цільовий Backend, надаючи плагіну доступ до всієї специфічної для цілі інформації, наприклад карти зв'язності, підтримуваного набору Gate тощо.
  • coupling_map вказує лише карту зв'язності і використовується тільки тоді, коли target не задано.
  • qubits вказує список Qubit, на яких визначено високорівневий об'єкт — у разі, якщо синтез виконується на фізичному Circuit. Значення None означає, що розміщення ще не вибрано і фізичні Qubit в цілі або карті зв'язності, на яких виконується ця операція, ще не визначено.
  • options — словник конфігурації у вільній формі для специфічних опцій плагіна. Якщо твій плагін має такі параметри конфігурації — обов'язково задокументуй їх.

Метод run повертає QuantumCircuit, що представляє Circuit, синтезований з цього високорівневого об'єкта. Також допустимо повертати None, що означає нездатність плагіна синтезувати даний високорівневий об'єкт. Безпосередньо синтез високорівневих об'єктів виконується проходом транспілятора HighLevelSynthesis.

Крім методу 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

Цей плагін синтезує об'єкти типу Clifford, що мають не більше 3 Qubit, використовуючи метод synth_clifford_bm.

Тепер ми оголошуємо плагін, додавши точку входу до метаданих нашого Python-пакета. Тут передбачається, що визначений нами клас доступний у модулі my_qiskit_plugin — наприклад, імпортований у файлі __init__.py модуля my_qiskit_plugin. Редагуємо файл pyproject.toml, setup.cfg або setup.py нашого пакета:

[project.entry-points."qiskit.synthesis"]
"clifford.my_clifford_synthesis" = "my_qiskit_plugin:MyCliffordSynthesisPlugin"

name складається з двох частин, розділених крапкою (.):

  • Назва типу Operation, який синтезує плагін (у цьому випадку — clifford). Зверни увагу, що цей рядок відповідає атрибуту name класу Operation, а не назві самого класу.
  • Назва плагіна (у цьому випадку — special).

Як і раніше, якщо твій проєкт використовує setup.cfg або setup.py замість pyproject.toml, зверни увагу на документацію setuptools — там пояснено, як адаптувати ці рядки під твою ситуацію.

Щоб перевірити, що Qiskit успішно виявляє твій плагін, встанови пакет плагіна та дотримуйся інструкцій у розділі Transpiler plugins про перелік встановлених плагінів — переконайся, що твій плагін з'явився у списку:

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 з'явилася б у цьому списку.

Рекомендація