주 콘텐츠로 건너뛰기

Transpiler 플러그인 만들기

패키지 버전

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

qiskit[all]~=2.3.0

Transpiler 플러그인을 만드는 것은 트랜스파일 코드를 더 넓은 Qiskit 커뮤니티와 공유하는 좋은 방법으로, 다른 사용자들이 여러분이 개발한 기능을 활용할 수 있게 해줍니다. Qiskit 커뮤니티에 기여해 주셔서 감사합니다!

Transpiler 플러그인을 만들기 전에, 어떤 종류의 플러그인이 상황에 적합한지 결정해야 합니다. Transpiler 플러그인에는 세 가지 종류가 있습니다:

  • Transpiler 스테이지 플러그인: 사전 설정된 스테이지 패스 매니저의 6가지 스테이지 중 하나를 대체할 수 있는 패스 매니저를 정의하는 경우에 선택하세요.
  • 유니터리 합성 플러그인: 트랜스파일 코드가 유니터리 행렬(Numpy 배열로 표현)을 입력으로 받아 해당 유니터리를 구현하는 양자 Circuit 설명을 출력하는 경우에 선택하세요.
  • 고수준 합성 플러그인: 트랜스파일 코드가 Clifford 연산자나 선형 함수와 같은 "고수준 객체"를 입력으로 받아 해당 고수준 객체를 구현하는 양자 Circuit 설명을 출력하는 경우에 선택하세요. 고수준 객체는 Operation 클래스의 서브클래스로 표현됩니다.

어떤 종류의 플러그인을 만들지 결정했다면, 다음 단계에 따라 플러그인을 만드세요:

  1. 적절한 추상 플러그인 클래스의 서브클래스를 만드세요:
  2. 패키지 메타데이터에서 클래스를 setuptools 엔트리 포인트로 노출하세요. 일반적으로 Python 패키지의 pyproject.toml, setup.cfg, 또는 setup.py 파일을 편집하면 됩니다.

하나의 패키지가 정의할 수 있는 플러그인 수에는 제한이 없지만, 각 플러그인은 고유한 이름을 가져야 합니다. Qiskit SDK 자체에도 여러 플러그인이 포함되어 있으며, 해당 플러그인들의 이름은 예약되어 있습니다. 예약된 이름은 다음과 같습니다:

  • Transpiler 스테이지 플러그인: 이 표를 참고하세요.
  • 유니터리 합성 플러그인: default, aqc, sk
  • 고수준 합성 플러그인:
연산 클래스연산 이름예약된 이름
Cliffordclifforddefault, ag, bm, greedy, layers, lnn
LinearFunctionlinear_functiondefault, kms, pmh
PermutationGatepermutationdefault, kms, basic, acg, token_swapper

다음 섹션에서는 각기 다른 유형의 플러그인에 대한 이러한 단계의 예시를 살펴봐요. 이 예시에서는 my_qiskit_plugin이라는 Python 패키지를 만드는 것을 가정합니다. Python 패키지 만들기에 대한 정보는 Python 웹사이트의 이 튜토리얼을 참고하세요.

예시: Transpiler 스테이지 플러그인 만들기

이 예시에서는 layout 스테이지에 대한 Transpiler 스테이지 플러그인을 만듭니다(Qiskit의 내장 트랜스파일 파이프라인의 6가지 스테이지에 대한 설명은 Transpiler 스테이지를 참고하세요). 우리의 플러그인은 요청된 최적화 수준에 따라 달라지는 시도 횟수로 VF2Layout을 실행합니다.

먼저, PassManagerStagePlugin의 서브클래스를 만듭니다. 구현해야 할 메서드는 pass_manager라고 하는 하나의 메서드입니다. 이 메서드는 PassManagerConfig를 입력으로 받아 우리가 정의하는 패스 매니저를 반환합니다. PassManagerConfig 객체는 커플링 맵 및 기저 Gate와 같은 대상 Backend에 대한 정보를 저장합니다.

# 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이라는 모듈에 노출되어 있다고 가정합니다. 예를 들어 my_qiskit_plugin 모듈의 __init__.py 파일에서 임포트된 경우입니다. 패키지의 pyproject.toml, setup.cfg, 또는 setup.py 파일을 편집합니다(Python 프로젝트 메타데이터를 저장하기로 선택한 파일 종류에 따라 다릅니다):

[project.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 Transpiler 패스를 사용하여 Gate를 합성하는 유니터리 합성 플러그인을 만들어 보겠습니다. 물론, 실제 플러그인은 이보다 훨씬 흥미로운 작업을 수행할 것입니다.

UnitarySynthesisPlugin 클래스는 유니터리 합성 플러그인의 인터페이스와 계약을 정의합니다. 주요 메서드는 run으로, 유니터리 행렬을 저장하는 Numpy 배열을 입력으로 받아 해당 유니터리 행렬에서 합성된 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 메서드에서 제공되는 입력이 목적에 충분하지 않다고 생각되신다면, 요구 사항을 설명하는 이슈를 등록해 주세요. 추가적인 선택적 입력 추가와 같은 플러그인 인터페이스 변경은 기존 플러그인에 변경이 필요하지 않도록 하위 호환성을 유지하는 방식으로 이루어집니다.

참고

supports_ 접두사가 붙은 모든 메서드는 인터페이스의 일부로서 UnitarySynthesisPlugin 파생 클래스에 예약되어 있습니다. 추상 클래스에 정의되지 않은 커스텀 supports_* 메서드를 서브클래스에 정의해서는 안 됩니다.

이제 Python 패키지 메타데이터에 엔트리 포인트를 추가하여 플러그인을 노출합니다. 여기서는 정의한 클래스가 my_qiskit_plugin이라는 모듈에 노출되어 있다고 가정합니다. 예를 들어, my_qiskit_plugin 모듈의 __init__.py 파일에서 임포트되는 방식입니다. 패키지의 pyproject.toml, setup.cfg, 또는 setup.py 파일을 편집합니다.

[project.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

이 플러그인은 synth_clifford_bm 메서드를 사용하여 최대 3개의 Qubit을 가진 Clifford 타입의 객체를 합성합니다.

이제 Python 패키지 메타데이터에 엔트리 포인트를 추가하여 플러그인을 노출합니다. 여기서는 정의한 클래스가 my_qiskit_plugin이라는 모듈에 노출되어 있다고 가정합니다. 예를 들어, my_qiskit_plugin 모듈의 __init__.py 파일에서 임포트되는 방식입니다. 패키지의 pyproject.toml, setup.cfg, 또는 setup.py 파일을 편집합니다.

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

name은 점(.)으로 구분된 두 부분으로 구성됩니다.

  • 플러그인이 합성하는 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라는 이름이 나타납니다.

권장 사항