트랜스파일러
qiskit.transpiler
개요
회로 변환/편집의 개념에 이미 익숙하다면 건너뛰고 넘어갈 수 있습니다:
- 사전 구성된 패스 관리자에 대한 설명입니다. 여기에는 스테이지에 대한 설명과 사용 가능한 모든 기본 제공 플러그인에 대한 설명이 포함됩니다.
- 커스텀 트랜스파일러 패스 빌드에 대한 문서입니다.
변환은 특정 양자 디바이스의 토폴로지와 일치하도록 주어진 입력 회로를 다시 작성하거나 양자 시스템에서 실행하기 위해 회로를 최적화하는 과정입니다.
대부분의 회로는 주어진 대상 장치와 호환되도록 일련의 변환을 거쳐야 하며, 결과물에 대한 노이즈의 영향을 줄이기 위해 회로를 최적화해야 합니다. 하드웨어 제약 조건에 맞게 양자 회로를 다시 작성하고 성능을 최적화하는 일은 결코 간단하지 않습니다. 재작성 도구 체인의 논리 흐름은 선형적일 필요는 없으며 반복적인 하위 루프, 조건부 분기 및 기타 복잡한 동작을 가질 수 있습니다. 즉, 표준 컴파일 흐름은 아래와 같은 구조를 따릅니다:
QuantumCircuitQiskit은 트랜스파일러 스택 전반에 걸쳐 회로의 트리 기반 표현 대신 그래프 기반 DAGCircuit 중간 표현(IR)을 사용합니다. DAGCircuit트랜스파일러 파이프라인은 객체로 PassManager , 이 객체의 PassManager.run() 메서드는 IR을 QuantumCircuit 입력으로 받아 이를 IR로 변환한 다음, 해당 IR에 일련의 처리를 적용한 후 최종적으로 객체를 QuantumCircuit 반환합니다. PropertySetAnalysisPass패스는 회로에 대한 속성을 계산하여 상태 기반 객체에 저장하는 ‘패스’이거나, 특정 단일 목표를 달성하기 위해 IR을 수정하는 ‘패스’ TransformationPass중 하나입니다. 파이프라인을 “단계”로 나뉘어 있다고 생각할 수 있으며, 각 단계는 하나의 고수준 변환을 담당합니다.
generate_preset_pass_manager()Qiskit은 함수를 사용하여 기본 트랜스파일레이션 파이프라인 빌더를 제공합니다. 이는 선택한 optimization_level 값(0에서 3까지, 양쪽 끝값 포함)에 따라 완전한 트랜스파일링을 위한 올바르게 구성된 파이프라인을 반환합니다. 매우 전문적인 내용을 찾고 계신 게 아니라면, 이곳이 여러분이 원하시는 시작점이 될 것이 거의 확실합니다. 트랜스파일링의 예시는 다음과 같습니다:
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
# Any abstract circuit you want:
abstract = QuantumCircuit(2)
abstract.h(0)
abstract.cx(0, 1)
# Any method you like to retrieve the backend you want to run on:
backend = QiskitRuntimeService().backend("some-backend")
# Create the pass manager for the transpilation ...
pm = generate_preset_pass_manager(backend=backend)
# ... and use it (as many times as you like).
physical = pm.run(abstract)내결함성을 위한 초기 실험에서, 대상 기저가 Clifford+T 게이트로 구성될 경우 함수 generate_preset_pass_manager() 와 transpile() 는 특화된 트랜스파일레이션 파이프라인을 호출합니다. 자세한 내용은 를 참조하십시오 generate_preset_clifford_t_pass_manager() . Clifford+T 파이프라인을 세부적으로 구성할 때는 후자를 사용하는 것이 좋습니다. "approximation_degree"예를 들어, 의 합성 정확도는 을 generate_preset_pass_manager() 통해 "unitary_synthesis_method" 설정할 수 없으며, 오직 을 통해서만 전역적으로 설정할 수 있습니다. 그러나 이 문제에 generate_preset_clifford_t_pass_manager() 대해서는 드러내고 "rz_synthesis_config" 있다.
예를 들어,
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import QFTGate
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import get_clifford_gate_names
# Any abstract circuit you want:
abstract = QuantumCircuit(4)
abstract.append(QFTGate(4), [0, 1, 2, 3])
# Use all Clifford+T basis gates
basis_gates = get_clifford_gate_names() + ["t", "tdg"]
# Create and run the pass manager
pm = generate_preset_pass_manager(basis_gates=basis_gates)
transpiled = pm.run(abstract)대부분의 사용 사례에서는 이 정도면 충분합니다. 그러나 키스킷의 모든 트랜스파일러 인프라는 확장성과 구성이 매우 뛰어납니다. 이 페이지의 나머지 부분에서는 트랜스파일러 스택의 저수준 기능을 활용하는 방법에 대해 자세히 설명합니다.
사전 설정된 패스 관리자
이 함수는 generate_preset_pass_manager() “사전 설정된 패스 관리자”를 생성합니다. PassManager이것들은 모두 의 인스턴스이므로, 메서드에 PassManager.run() 를 QuantumCircuit 전달하여 사용됩니다. StagedPassManager더 구체적으로 말하자면, 사전 설정된 패스 관리자들은 의 인스턴스이며, 이를 통해 프리 스테이지 및 포스트 스테이지 훅을 포함하여 트랜스파일링의 개별 단계를 보다 유연하게 구성할 수 있습니다.
사전 설정 패스 관리자에는 최대 6개의 명명된 스테이지가 있습니다. 실행 순서대로 아래에 요약되어 있으며 다음 하위 섹션에서 더 자세한 정보를 확인할 수 있습니다.
init
추상 회로 최적화, 다중 큐비트 연산을 1큐비트 및 2큐비트 연산으로 축소. 자세한 내용은 초기화 단계를 참조하세요.
layout
가상 큐비트와 물리적 큐비트의 초기 매핑을 선택하고, 명시적 부수자를 포함하도록 회로를 확장합니다. 이 단계는 때때로 routing 을 포함하기도 합니다. 자세한 내용은 레이아웃 단계를 참조하세요.
routing
Target회로에 게이트를 삽입하여 회로가 의 연결 제약 조건을 충족하도록 하십시오. 삽입된 게이트는 아직 대상 ISA와 일치할 필요가 없으므로, 대개 단순한 swap 명령어에 불과합니다. 해당 layout 단계가 제 역할을 제대로 수행할 경우, 이 단계는 때때로 생략되기도 합니다. 자세한 내용은 ‘라우팅 단계 ’를 참조하십시오.
translation
Target회로 내의 모든 게이트를 의 ISA와 일치하는 것으로 변환하십시오. 자세한 내용은 ‘번역 단계 ’를 참조하십시오.
optimization
낮은 수준의 하드웨어 인식 최적화. init 단계의 추상적인 최적화와 달리 이 단계는 실제 회로에서 작동합니다. 자세한 내용은 최적화 단계를 참조하세요.
scheduling
삽입 Delay 명령어를 삽입하여 회로의 벽시계 타이밍을 명시적으로 만들 수 있습니다. 여기에는 동적 디커플링과 같은 하드웨어 인식 온라인 오류 감소 기술도 포함될 수 있는데, 이는 벽시계 타이밍을 파악하는 데 의존합니다. 자세한 내용은 예약 단계를 참조하세요.
사전 설정된 트랜스파일러 파이프라인은 optimization_level 을 설정하여 높은 수준에서 구성할 수도 있습니다. 이 값은 0에서 3까지의 정수로, 하드웨어에 맞게 회로를 최적화하는 데 투입되는 상대적인 노력을 나타냅니다. 레벨 0은 불필요한 모든 최적화를 비활성화하고 회로를 실행 가능하게 만드는 데 필요한 변환만 사용합니다. 반면에 레벨 3에서는 모든 범위의 최적화 기법을 사용할 수 있으며, 이 중 일부는 컴파일 시간이 매우 많이 소요될 수 있습니다. 기존 컴파일러와 마찬가지로 최적화 레벨 3이 항상 최상의 결과를 보장하는 것은 아닙니다. 키스킷은 컴파일 시간과 예상되는 최적화 양 사이의 절충안으로 최적화 레벨 2를 기본값으로 설정합니다.
generate_preset_pass_manager()최적화 수준은 기본적으로 특정 단계에 어떤 구현체가 사용될지를 결정하지만, 에 명시적인 <stage>_method="<choice>" 인수를 전달하여 이를 재정의할 수 있습니다.
사전 설정된 파이프라인의 재현성
양자 컴파일링은 종종 복잡도 측면에서 비다항식인 것으로 알려진 문제를 해결하는 과정을 수반하며, 따라서 전역 최적화를 통해 해결하기는 매우 어렵습니다. 이러한 경우에는 확률적 알고리즘이나 휴리스틱 알고리즘이 더 적합한 경우가 많다. 하지만 이로 인해 재현성 문제가 발생한다.
사전 설정 패스 관리자에는 거의 항상 확률적, 휴리스틱 기반 패스가 포함되어 있습니다. 컴파일의 재현성을 보장해야 하는 경우 생성기 함수의 seed_transpiler 인수에 알려진 정수를 전달하세요.
Qiskit에 내장된 모든 플러그인은 분석 결과를 생성하고, (만약 있다면) 무작위화 과정에 시드가 지정된 경우 결정론적인 방식으로 해당 값을 DAGCircuit 수정해야 하며, 이를 통해 나중에 컴파일을 반복할 수 있도록 해야 합니다. 이에 대해서는 다음과 같은 제한 사항이 있습니다:
- 확률적 구성 요소가 포함된 모든 내장 패스는 무작위화를 시드하는 방법을 제공해야 하며, 시드된 경우 결정론적 출력 규칙을 준수해야 합니다.
- 확률적 요소가 없는 모든 내장 패스는 동일한 입력에 대해 결정론적 출력 규칙을 준수해야 한다. 효율성을 위해 캐시를 유지하는 것은 허용되지만, 동일한 입력 집합을 사용할 경우, 해당 패스가 여러 번 호출되더라도 반환값은 동일해야 합니다. 단, 패스의 통제 범위를 벗어난 요인이 입력 중 하나를 그 자리에서 변경하는 경우는 예외입니다(예를 들어, 사전 설정된 패스 관리자에서는 기본적으로 를
SessionEquivalenceLibraryBasisTranslator사용하며, 동치 라이브러리에 새로운 항목이 추가되어 결과가 달라지는 것은 버그가 아닙니다).PropertySet“출력”이란 패스가 추후 사용을 위해 작성하는 모든 것을 의미합니다. 여기에는 패스에서 명시적으로return반환하는 값뿐만 아니라,.에서 나중에 사용될 목적으로 지정된 속성도 포함됩니다. - 패스의 출력은 패스에 사용할 수 있는 스레드 수에 관계없이 주어진 키스킷 버전과 고정된 환경에 대해 주어진 머신에서 결정론적이어야 합니다. 많은 기본 제공 키스킷 패스는 스레드 동시성을 사용하며, 스레드 수에 따라 동작이 달라지는 것은 허용되지 않습니다.
- 기본 Python 환경의 일부가 변경되거나(예: 종속 패키지 업데이트) 시스템 수학 라이브러리가 변경되는 경우(예: BLAS의 다른 구현을 사용할 수 있는 경우) 고정 시드의 패스 출력은 동일할 필요가 없습니다.
- 고정 시드의 패스 출력은 운영 체제 간에 동일할 필요는 없습니다(일반적으로 운영 체제별 차이의 근본 원인은 시스템 수학 라이브러리의 구현에 있습니다).
- 사용 가능한 CPU 명령어가 다른 두 머신 간에 고정 시드에 대한 패스의 출력이 동일할 필요는 없습니다. 코어 수학 커널의 구현이 서로 다른 경우, 예를 들어 두 개의 개별 부동 소수점 곱하기 및 더하기 명령어에 서로 다른 반올림 특성을 갖는 융합 곱하기-추가 명령어를 사용하는 경우 다른 동작이 발생할 수 있을 것으로 예상됩니다.
- 사용자가 이 동작을 특별히 선택 해제하지 않는 한 고정 시드에 대한 패스의 출력은 사용하도록 허용된 스레드 수에 관계없이 동일해야 합니다. 예를 들어 사전 설정된 패스매니저에서는 허용된 스레드가 하나이거나 스레드가 트라이얼보다 많더라도 기본적으로 동일한 수의 트라이얼을 실행해야 하지만, 이 동작은
QISKIT_SABRE_ALL_THREADS환경 변수를 설정하여 명시적으로 재정의하여 스레드 수에 민감해지도록 선택할 수 있습니다. - 위의 모든 규칙은
PYTHONHASHSEED을 명시적으로 설정하지 않은 경우에도 별도의 Python 통역사 세션 간에도 적용됩니다.
일반적으로, Qiskit의 DAGCircuit 사용자는 내장된 Qiskit 패스(필요한 경우 시드가 설정된)가 고정된 입력값으로 실행된 후에는 모든 DAGCircuit() 메서드의 정확한 출력이 결정론적이라고 가정할 수 있어야 합니다. 여기에는 순서에 대해 어떠한 보장도 하지 않는 메서드의 출력 순서도 포함됩니다. 비록 그 의미론이나 정확한 순서는 신뢰할 수 없더라도, 고정된 입력에 대한 출력 순서의 결정성은 신뢰할 수 있습니다.
트랜스파일러 패스를 결정론적으로 만드는 방법에 대한 논의는 무작위성과 결정론 문서를 참조하세요.
사전 설정된 스테이지 구현 선택
키스킷에는 위 단계의 여러 구현이 포함되어 있으며, 더 많은 단계를 별도의 "플러그인"으로 설치할 수 있습니다. 어떤 스테이지 구현을 사용할지 제어하려면 두 함수의 <stage>_method 키워드 인수에 해당 이름을 전달합니다(예: translation_method="translator"). 스테이지에 이러한 외부 플러그인을 구현하는 방법에 대한 자세한 내용은 qiskit.transpiler.preset_passmanagers.plugin.
예를 들어, 레이아웃에 trivial 메서드와 라우팅에 sabre 메서드를 명시적으로 사용하는 최적화 수준 1의 프리셋 패스 관리자를 생성하려면 이렇게 하면 됩니다:
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2
# Whatever backend you like:
backend = GenericBackendV2(num_qubits=5)
pass_manager = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
layout_method="trivial",
routing_method="sabre",
)각 단계에서 사용할 수 있는 기본 제공 플러그인 세트는 Qiskit의 공개 API의 일부이며, 모든 안정성 보장의 적용을 받습니다. 여기에는 해당 메서드의 고수준 논리적 효과도 포함됩니다(예를 들어, routing_method="sabre" 는 항상 Sabre에서 파생된 알고리즘을 사용합니다). 그러나 무대를 나타내는 이 PassManager 기능의 정확한 내부 구조는 공개되어 있지 않습니다. 마이너 버전 간에 패스의 순서가 변경되거나 새로운 패스가 도입될 수도 있습니다.
스테이지가 있는 경우 "default" 이라는 메서드가 가장 많이 변경될 수 있습니다. 키스킷은 일반적으로 메이저 버전과 마이너 버전의 경계에서 기본 메서드의 알고리즘을 완전히 변경하지만, 휴리스틱의 균형을 재조정하고 마이너 버전 간에 기본 메서드에 새로운 패스를 추가할 수 있습니다.
StagedPassManager의 generate_preset_pass_manager() 출력이 이므로, 생성 후 패스 매니저를 수정하여 완전히 사용자 정의된 스테이지 구현을 제공할 수도 있습니다. 예를 들어, 동적 분리(pass PadDynamicalDecoupling 사용)를 활용하여 사용자 정의 스케줄링 단계를 실행하고, 라우팅 전에 초기 논리 최적화도 추가하려면 (이전 예제를 바탕으로) 다음과 같이 하면 됩니다:
import numpy as np
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.circuit import library as lib
from qiskit.transpiler import PassManager, generate_preset_pass_manager
from qiskit.transpiler.passes import (
ALAPScheduleAnalysis,
InverseCancellation,
PadDynamicalDecoupling,
)
backend = GenericBackendV2(num_qubits=5)
dd_sequence = [lib.XGate(), lib.XGate()]
scheduling_pm = PassManager(
[
ALAPScheduleAnalysis(target=backend.target),
PadDynamicalDecoupling(target=backend.target, dd_sequence=dd_sequence),
]
)
inverse_gate_list = [
lib.CXGate(),
lib.HGate(),
(lib.RXGate(np.pi / 4), lib.RXGate(-np.pi / 4)),
(lib.PhaseGate(np.pi / 4), lib.PhaseGate(-np.pi / 4)),
(lib.TGate(), lib.TdgGate()),
]
logical_opt = PassManager([InverseCancellation(inverse_gate_list)])
pass_manager = generate_preset_pass_manager(optimization_level=0)
# Add pre-layout stage to run extra logical optimization
pass_manager.pre_layout = logical_opt
# Set scheduling stage to custom pass manager
pass_manager.scheduling = scheduling_pm이제 스테이지드 패스 매니저가 메서드를 run() 통해 실행되면, 스테이지 layout 전에 패스 매니저가 logical_opt 호출되며, 해당 scheduling 스테이지에는 기본값 대신 이 scheduling_pm 패스 매니저가 사용됩니다.
사전 설정 패스 관리자를 위한 사용자 지정 스테이지를 구성하는 경우, 다음의 하위 수준 도우미 함수 중 일부가 유용할 수 있습니다 qiskit.transpiler.preset_passmanagers 의 저수준 도우미 함수가 유용할 수 있습니다.
초기화 단계
초기화 단계에 대한 더 높은 수준의 사용자 대상 설명은 IBM 퀀텀 가이드에서 확인할 수 있습니다.
이 init 단계는 추상 회로에 대한 고수준의 논리적 최적화를 수행하고, 다중 큐비트(3개 이상) 연산을 일련의 1큐비트 및 2큐비트 연산으로 변환하는 역할을 담당합니다. 이번 실행은 첫 번째 단계 실행이므로, 입력으로 완전히 추상화된 회로가 주어집니다. AnnotatedOperation이 init 스테이지는 사용자 정의 게이트는 물론, 다음과 같은 모든 고수준 추상 회로 설명 객체를 처리할 수 있어야 합니다.
init 단계의 출력은 1큐비트 및 2큐비트 연산만 포함하는 추상 회로입니다.
스테이지 플러그인을 작성할 때 init 의 엔트리 포인트는 qiskit.transpiler.init 입니다. 기본 제공 플러그인은 다음과 같습니다:
방법 | 요약 |
|---|---|
| default | 멀티-쿼비트 연산의 언롤링 및 추상 최적화 기능이 내장되어 있습니다. |
내장 default 플러그인
최적화 수준 0에서는 추상적 최적화가 수행되지 않습니다. 기본 플러그인은 3개 이상의 큐비트를 사용하는 연산을 해당 계층적 definition 필드에 접근하여 단순히 “펼쳐” 냅니다.
최적화 레벨 1 이상에서 기본 플러그인은 두 개의 연속된 cx 게이트와 같은 인접한 역 게이트의 단순 취소도 수행합니다.
최적화 레벨 2와 3에서는 기본 플러그인을 통해 훨씬 더 광범위한 추상적 최적화가 가능합니다. 여기에는 다음과 같은 혜택이 포함됩니다.
ElidePermutations“가상 순열 생략”(참조), 여기서 명시적인 순열을 유도하는 연산은 제거되고, 대신 가상 큐비트의 재매핑을 통해 수행된다.- IR의 정류 구조를 분석하여 상쇄할 수 있는 게이트 쌍을 찾습니다.
- 분리 가능한 일련의 1큐비트 연산으로 표현할 수 있는 2큐비트 연산의 수치 분할입니다.
- 측정 직전의 작은 각도 폴리 회전 및 대각선 연산과 같이 인식할 수 없는 연산을 제거합니다.
레이아웃 단계
IBM 퀀텀 가이드의 레이아웃 단계에 대한 더 높은 수준의 사용자 대상 설명입니다.
레이아웃 단계는 입력 회로의 가상 큐비트와 타겟의 하드웨어 큐비트 간에 초기 매핑을 생성하는 역할을 합니다. 여기에는 명시적 부호로 입력 회로를 확장하여 대상의 큐비트 수만큼의 큐비트를 갖도록 하고, 모든 연산을 하드웨어 큐비트 측면에서 다시 작성하는 것이 포함됩니다. 다른 툴킷이나 문헌에서도 이 문제를 '배치' 문제라고 부르는 것을 볼 수 있습니다.
PropertySet레이아웃 단계에서는 파이프라인의 에서 및 original_qubit_indices 속성을 layout 설정해야 합니다.
transpile()레이아웃 단계에 사용되는 모든 내장 플러그인은 또는 의 generate_preset_pass_manager() 인수를 initial_layout 사용하여 명시적으로 선택한 레이아웃을 우선적으로 적용합니다.
회로의 특정 지점에서 현재 활성화된 입력 회로의 '가상' 큐비트와 백엔드의 하드웨어 큐비트 간의 매핑을 식별할 수 있습니다. 하드웨어 큐비트는 주어진 지점에서 하나의 가상 큐비트만 나타낼 수 있지만, 매핑은 회로가 진행되는 동안 달라질 수 있습니다. 원칙적으로 가상 큐비트 상태의 수명을 단축할 수 있다면 일부 가상 큐비트는 회로 실행의 모든 지점에서 매핑되지 않을 수 있지만, 현재 키스킷의 내장 파이프라인은 이를 사용하지 않습니다.
레이아웃 단계는 회로를 통해 대상의 연결성이 끝까지 유지되는지, 모든 작업이 대상에서 직접 실행될 수 있는지 확인할 책임이 없으며, 이는 각각 라우팅 및 번역 단계의 책임입니다.
초기 레이아웃의 선택은 출력 회로의 품질에 영향을 미치는 가장 중요한 요소 중 하나입니다. 레이아웃 단계는 기본 파이프라인에서 계산 비용이 가장 많이 드는 단계로, 레이아웃을 위한 기본 플러그인은 여러 가지 알고리즘을 시도하기도 합니다( 기본 제공 기본 플러그인에서 자세히 설명합니다).
레이아웃 단계에서 이상적인 상황은 모든 작업이 회로의 Target 연결 제약 조건을 준수하여 라우팅 단계가 필요하지 않을 정도의 “완벽한” 레이아웃을 찾는 것입니다. 일반적으로 임의의 입력 회로에 대해서는 이것이 불가능하지만, 가능한 경우에는 이 VF2Layout 패스를 사용하여 유효한 초기 레이아웃을 찾을 수 있습니다. 여러 개의 완벽한 레이아웃이 발견될 경우, 예상 오류율을 바탕으로 한 점수 산정 휴리스틱을 사용하여 어떤 레이아웃을 사용할지 결정합니다.
모든 내장 플러그인에서 인수 initial_layout 를 generate_preset_pass_manager() 전달하면, 개별 “선택” 로직을 건너뛰고 지정된 레이아웃이 그대로 사용됩니다. 모든 내장 플러그인은 보조 회로 할당을 포함하여 회로를 기기의 전체 너비에 맞춰 삽입하는 기능도 지원합니다.
레이아웃 플러그인을 직접 작성하는 경우 다음과 같은 이점을 얻을 수 있습니다 generate_embed_passmanager() 는 레이아웃 애플리케이션의 "임베딩" 단계를 자동화하는 데 유용합니다.
스테이지 플러그인을 작성할 때 layout 의 엔트리 포인트는 qiskit.transpiler.layout 입니다. 기본 제공 플러그인은 다음과 같습니다:
방법 | 요약 |
|---|---|
| default | 최적화 수준이 가장 높을 때는 완벽한 레이아웃을 찾은 다음 세이버 기반 레이아웃과 라우팅을 결합한 패스를 시도합니다. |
| 밀집형 | 초기 큐비트로 사용할 백엔드의 가장 밀도가 높은 하위 그래프(큐비트 링크 도 기준)를 찾습니다. |
| 단순 | 가상 큐비트 0을 물리적 큐비트 0에 매핑하는 식입니다. |
| 세이버 | 키스킷의 향상된 세이버 레이아웃 알고리즘을 사용합니다. |
모든 최적화 수준에서 기본 레이아웃 방법은 default 이지만, 이 단계의 구조는 수준에 따라 크게 달라집니다.
내장 default 플러그인
여러 가지 레이아웃 기법을 융합한 것입니다.
최적화 수준 0에서는 사소한 레이아웃이 선택됩니다.
최적화 수준 0 이상에서는 2단계 프로세스가 있습니다:
- 먼저, 를
VF2Layout사용하여 “완벽한” 레이아웃을 찾아보세요. 동형사상 평가기에 대한 호출 횟수의 상한은 최적화 수준이 높아질수록 증가합니다. 거대하고 복잡한 대상의 경우, 완벽한 레이아웃이 존재한다고 해도 이를 반드시 찾을 수 있다는 보장은 없지만, 최적화 수준이 높아질수록 그 확률은 높아집니다. - 완벽한 레이아웃을 찾을 수 없는 경우, 를
SabreLayout사용하여 초기 레이아웃을 선택하고, 최적화 수준이 높아짐에 따라 초기 레이아웃 시도 횟수, 스왑 맵 시도 횟수 및 전진-후진 반복 횟수를 점차 늘립니다.
또한 최적화 레벨 1은 이전 버전과의 호환성을 위해 VF2-based 버전 이전의 사소한 레이아웃도 시도합니다.
내장 dense 플러그인
레이아웃을 선택하기 위해 패스를 DenseLayout 사용합니다. 이 단계에서는 대상 연결성 그래프 전체 중에서 가장 밀도가 높은 연결된 부분 그래프를 찾아내며, 여기서 “가장 밀도가 높다”는 것은 사용 가능한 연결 수가 가장 많은 하드웨어 큐비트가 우선적으로 선택됨을 의미합니다. 가상 큐비트와 하드웨어 큐비트 간의 매핑은 차수가 가장 높은 가상 큐비트를 차수가 가장 높은 하드웨어 큐비트에 할당함으로써 완료됩니다.
이는 초기 레이아웃을 선택하는 데 사용되는 비교적 저렴한 휴리스틱 기법이지만, 일반적으로 Sabre 기반 방법보다 출력 품질이 훨씬 떨어집니다. 기본 레이아웃 플러그인은 Sabre 알고리즘의 초기값을 설정하기 위해 사용자가 DenseLayout 선택한 초기 매핑을 초기 레이아웃 중 하나로 사용합니다.
내장 trivial 플러그인
레이아웃을 선택하기 위해 패스를 TrivialLayout 사용합니다. 이것은 가장 간단한 할당 방식으로, 각 가상 큐비트가 동일한 인덱스를 가진 하드웨어 큐비트에 할당되므로, 가상 큐비트 0은 하드웨어 큐비트 0에 매핑되고, 그 외의 경우도 마찬가지입니다.
이 방법은 들어오는 "추상" 회로가 이미 디바이스에서 전체 폭으로 존재하고, 그 연산이 물리적 연산에 해당하며, 트랜스파일러가 물리적 QuantumCircuit.
내장 sabre 플러그인
를 SabreLayout 사용하여 초기 레이아웃을 선택하며, Qiskit에서 수정된 Sabre 라우팅 알고리즘을 서브루틴으로 활용하여 후보 회로를 정방향 및 역방향으로 모두 스왑 매핑합니다.
요약하면, 원래 Sabre 알고리즘의 레이아웃 구성 요소는 임의로 초기 레이아웃을 선택한 다음 회로에서 라우팅을 실행하고, 회로를 반전하고, 반전된 회로에서 이전의 "최종" 가상-하드웨어 할당을 초기 상태로 하여 라우팅을 실행함으로써 "개선"을 시도합니다. 구성된 최적화 수준에 따라 이 작업을 얼마나 많이 반복할지, 얼마나 다양한 초기 레이아웃을 시도할지 결정됩니다.
0 이외의 최적화 수준에서 기본 단계와 가장 큰 차이점은 이 플러그인은 세이버 기반 알고리즘만 실행한다는 점입니다. 완벽한 레이아웃을 찾으려 하거나 사소한 레이아웃을 시도하지 않습니다.
라우팅 단계
IBM 퀀텀 가이드에서 라우팅 단계에 대한 보다 높은 수준의 사용자 대상 설명을 확인하세요.
라우팅 단계에서는 회로의 가상 연결 그래프가 대상의 하드웨어 연결 그래프와 호환되는지 확인합니다. 간단히 말해서 라우팅 단계는 회로의 모든 2큐비트 게이트가 대상 ISA에서 정의된 2큐비트 연산을 가진 하드웨어 큐비트에 매핑되도록 합니다. 다른 툴킷이나 문헌에서 이 문제를 "매핑" 또는 "스왑 매핑" 문제라고도 합니다.
라우팅 알고리즘은 일반적으로 회로에 swap 게이트를 삽입하고 회로 실행 과정에서 큐비트의 가상-하드웨어 매핑을 수정하여 이를 수행합니다.
배선 단계에서는 회로 내의 모든 게이트가 대상 ISA에 대해 유효한지 확인할 필요가 없습니다. SwapGate예를 들어, 라우팅 플러그인은 회로에 리터럴 swap 게이트를 남겨둘 수 있는데, 이는 해당 회로에 게이트가 Target 포함되어 있지 않더라도 마찬가지입니다. 그러나 회로에서 게이트가 적용되는 임의의 하드웨어 큐비트 쌍에 대해, 해당 Target 회로에 정의된 2-큐비트 게이트가 적어도 하나는 존재해야 한다.
라우팅이 수행된 경우, 라우팅 단계에서는 PropertySet 의 final_layout 및 virtual_permutation_layout 속성을 설정해야 합니다.
Qiskit의 모든 내장 라우팅 단계는 라우팅 후 추가로 pass 단계를 VF2PostLayout 실행합니다. 오류율이 더 낮은 큐비트를 찾을 수 있다면, 이로 인해 초기 레이아웃이 재할당될 수도 있습니다. 이 패스는 기본 레이아웃 플러그인이 사용하는 클래스와 VF2Layout 매우 유사하지만, 에서는 VF2PostLayout 대상 토폴로지에 회로 토폴로지와 일치하는 동형 유도 부분 그래프가 적어도 하나 존재함을 보장할 수 있다는 점이 다릅니다.
키스킷의 내장 라우팅 플러그인은 모두 일반적으로 정의된 2큐비트 링크가 있는 모든 큐비트 쌍에는 해당 두 큐비트에 대해 정의된 범용 게이트 세트가 있다고 가정합니다. 하드웨어가 반드시 이를 존중할 필요는 없지만(예를 들어, 정의된 2-큐비트 게이트가 swap 뿐이라면 cx 과 같은 얽힘 연산은 실현할 수 없습니다), 키스킷은 아직 이러한 가능성을 고려하지 않습니다.
삽입에 필요한 최소 교환 횟수를 구하는 문제는 비다항식 시간 문제인 것으로 알려져 있다. 이는 시도하기에는 비용이 너무 많이 든다는 뜻이므로, Qiskit의 내장 알고리즘 중 상당수는 확률적이며, 컴파일마다 결과 간에 큰 차이가 나타날 수 있습니다. transpile()재현성이 필요하다면, 의 generate_preset_pass_manager() 인수를 seed_transpiler 또는 로 설정해야 합니다.
스테이지 플러그인을 작성할 때 routing 의 엔트리 포인트는 qiskit.transpiler.routing 입니다. 기본 제공 플러그인은 다음과 같습니다:
방법 | 요약 |
|---|---|
| default | 키스킷에서 선택한 기본 라우팅 방법을 사용합니다. |
| 세이버 | 기본값. 키스킷의 수정된 세이버 라우팅 알고리즘을 사용하여 맵을 교환합니다. |
| 없음 | 라우팅을 비활성화합니다. 라우팅이 필요한 경우 오류를 발생시킵니다. |
| 기본 | 한 번에 하나의 작업을 라우팅하는 욕심쟁이 스왑 삽입. |
| 룩어헤드 | 휴리스틱 프루닝을 사용한 범위 우선 검색을 통해 게이트를 실행 가능한 스왑을 찾습니다. |
내장 default 플러그인
라우팅에는 Qiskit에서 선택한 기본 방법을 사용합니다. VF2PostLayoutQiskit 2.0 기준, 선택된 알고리즘은 내장된 Sabre 플러그인과 동일하지만, 실제로는 대개 내장된 기본 레이아웃 단계 플러그인이 Sabre 기반 라우팅 알고리즘을 실행하며, 라우팅 단계는 오직.을 실행하는 데만 사용됩니다.
내장 none 플러그인
라우팅을 완전히 비활성화하는 데 사용되는 더미 플러그인입니다. 이 기능은 하드웨어 구성 실험이나 부분 컴파일과 같은 특수한 경우에 유용할 수 있습니다.
내장 basic 플러그인
BasisSwap greedy 스왑 삽입 알고리즘을 사용합니다. 이는 개념적으로 매우 간단합니다. 토폴로지 순서대로 각 연산에 대해 장치에서 연결을 실행하는 데 필요한 최단 경로 스왑을 삽입하면 됩니다.
최적화 수준은 라우팅 후 초기 레이아웃을 개선하기 위해 해당 VF2PostLayout 단계에서 수행하는 작업량에만 영향을 미칩니다.
이 방법은 일반적으로 출력 품질이 좋지 않습니다.
내장 lookahead 플러그인
이 LookaheadSwap 알고리즘을 사용하여 경로를 결정합니다. 이는 본질적으로 스왑 네트워크를 생성하기 위한 폭우선 탐색(breadth-first search)으로, 탐색 중인 트리는 각 깊이에서 소수의 스왑 후보로만 추려집니다.
이 알고리즘은 "세이버" 플러그인의 basic 휴리스틱과 유사하지만, 각 스왑의 다음 효과도 작은 깊이로 고려한다는 점이 다릅니다.
최적화 수준은 검색 깊이, 깊이별 가지치기 정도, 그리고 초기 레이아웃을 사후 최적화하기 VF2PostLayout 위해 수행되는 작업량에 영향을 미칩니다.
실제로 '세이버' 플러그인은 몇 배 더 빠르게 실행되며 더 나은 결과물을 만들어냅니다.
내장 sabre 플러그인
이 SabreSwap 알고리즘을 사용하여 경로를 결정합니다. 이 코드는 Qiskit에서 개선한 기존 Sabre 라우팅 알고리즘을 사용합니다.
이 라우팅 알고리즘은 스레드 병렬 처리로 실행되어 여러 가지 라우팅 가능성을 고려하여 삽입된 스왑 수를 최소화하는 라우팅 알고리즘을 선택합니다.
최적화 수준은 전체 라우팅 과정에서 시도되는 서로 다른 확률적 시드(stochastic seed)의 수와, 초기 레이아웃을 사후 최적화하기 VF2PostLayout 위해 수행되는 작업량에 영향을 미칩니다.
이것은 거의 항상 가장 성능이 좋은 내장 플러그인이며, 라우팅이 필요한 모든 경우에 키스킷이 기본적으로 사용하는 플러그인입니다.
번역 단계
번역 단계에 대한 더 높은 수준의 사용자 대상 설명은 IBM 퀀텀 가이드에서 확인할 수 있습니다.
변환 단계는 회로의 모든 게이트를 대상 ISA에서 지원하는 게이트로 다시 작성하는 작업을 담당합니다. 예를 들어, 하드웨어 큐비트 0과 1에 cx 요청이 들어왔지만 ISA에는 해당 큐비트에 대한 cz 연산만 포함된 경우, 변환 단계에서는 cz 및 사용 가능한 1-큐비트 게이트를 사용하여 cx 게이트를 표현하는 방법을 찾아야 합니다.
번역 단계는 최적화 단계로 들어가기 전에 호출됩니다. 최적화 플러그인(키스킷의 내장 플러그인 포함)은 최적화 루프가 비 ISA 게이트를 포함하는 회로를 반환하는 경우, 최적화 루프 이후의 "수정" 단계로 변환 단계를 사용할 수도 있습니다. 최적화 루프는 '2큐비트 게이트의 수'와 같은 속성을 최소화하는 데만 관심을 가질 수 있으며, 그 출력은 로컬 등가 게이트의 관점에서 남기므로 변환 단계에서 대상 최적화 속성에 영향을 주지 않고 쉽게 재작성할 수 있습니다. 이렇게 하면 두 단계 간에 우려 사항을 쉽게 분리할 수 있습니다. 일부 최적화 플러그인은 출력이 더 엄격할 수 있으므로 번역 단계에 대한 후속 조치가 더 이상 필요하지 않을 수 있습니다.
스테이지 플러그인을 작성할 때 translation 의 엔트리 포인트는 qiskit.transpiler.translation 입니다. 기본 제공 플러그인은 다음과 같습니다:
방법 | 요약 |
|---|---|
| default | 키스킷에서 선택한 기본 번역 방법을 사용합니다. |
| 변환기 | 알려진 등식을 사용하여 대상 기준으로 게이트를 기호적으로 변환합니다. |
| 합성 | 1큐비트 및 2큐비트 게이트의 각 실행을 매트릭스 표현으로 수집하고 거기서부터 재합성합니다. |
내장 default 플러그인
키스킷에서 선택한 기본 번역 방법을 사용합니다. 키스킷( 2.0 ) 기준으로는 내장 번역기 플러그인과 동일하지만, 모든 대상에 대해 또는 특정 대상 클래스에 대해서만 2.x 시리즈 중에 선택한 알고리즘이 변경될 수 있습니다.
내장 synthesis 플러그인
동일한 큐비트에 대한 게이트 실행들을 행렬 형태로 모은 다음, (구성된 unitary_synthesis_method)을 사용하여 pass를 UnitarySynthesis 통해 재합성합니다. 이는 대체로 높은 최적화 수준에서 나타나는 최적화 루프 자체와 유사합니다.
행렬로 수집하는 것은 일반적으로 행렬을 사용하지 않는 번역보다 비용이 더 많이 들지만 원칙적으로 번역 품질이 더 좋을 수 있습니다. 실제로는 대상 ISA에 맞는 합성 알고리즘이 필요하므로 이 방법은 다른 방법보다 덜 일반적입니다. 이미 키스킷에 있는 합성 루틴과 일치하는 간단한 ISA를 대상으로 할 때 더 높은 품질의 결과를 생성할 수 있습니다.
이 방법을 사용하는 경우 최적화 루프가 필요하지 않을 수 있습니다.
최적화 수준은 이 플러그인에 영향을 미치지 않습니다.
내장 translator 플러그인
이 BasisTranslator 알고리즘을 사용하여 게이트를 대상 기저로 기호적으로 변환합니다. SessionEquivalenceLibrary대략적으로 말하자면, 이 과정은 회로에서 요청한 게이트 집합에서 시작하여, 주어진 EquivalenceLibrary (일반적으로 )의 규칙을 활용하여 ISA로 나아가는 방식입니다.
이것이 기본 번역 방법입니다.
최적화 수준은 이 플러그인에 영향을 미치지 않습니다.
최적화 단계
퀀텀 가이드( IBM )에서 최적화 단계에 대한 보다 높은 수준의 사용자 대상 설명을 확인할 수 있습니다.
최적화 단계는 낮은 수준의 하드웨어 인식 최적화를 위한 단계입니다. 초기화 단계와 달리 이 단계의 입력은 이미 ISA와 호환되는 회로이므로 낮은 수준의 최적화 플러그인을 특정 ISA에 맞게 조정할 수 있습니다.
최적화 플러그인에 대한 요구 사항은 ISA에서 지원하는 회로를 입력으로 받아 ISA에서 지원하는 회로를 반환한다는 점 외에는 거의 없습니다. DoWhileController최적화 플러그인에는 종종 루프(예: )가 포함되어 있으며, 구성된 변환 단계를 수정 파이프라인으로 포함할 수도 있습니다.
키스킷에 내장된 최적화 플러그인은 일반적이며, 오류가 수정되지 않은 대부분의 실제 ISA에 잘 적용됩니다. 내장 플러그인은 연속적으로 매개변수화된 단일 큐비트 게이트가 없는 ISA에는 적합하지 않습니다.
스테이지 플러그인을 작성할 때 optimization 의 엔트리 포인트는 qiskit.transpiler.optimization 입니다. 기본 제공 플러그인은 다음과 같습니다:
방법 | 요약 |
|---|---|
| default | 기본 최적화 패스 세트입니다. 이는 최적화 수준에 따라 크게 달라집니다. |
내장 default 플러그인
이는 최적화 수준에 따라 크게 달라집니다.
이 파이프라인의 세부 사항은 Qiskit 버전에 따라 달라질 수 있습니다. 다음은 그 기본 원칙들입니다.
최적화 레벨 0에서는 스테이지가 비어 있습니다.
최적화 레벨 1에서는 단일 큐비트 게이트의 실행을 매트릭스 기반으로 재합성하고, 연속적으로 나타나는 경우 2큐비트 게이트의 매우 간단한 기호 역 취소를 수행합니다. 이는 회로의 크기와 깊이가 고정될 때까지 반복적으로 실행됩니다.
최적화 레벨 2에서는 레벨 1의 최적화에 더해 루프에 게이트 세트의 정류 분석이 포함되어 취소를 고려할 수 있는 게이트의 범위를 넓힙니다. 루프 전에 1큐비트 및 2큐비트 게이트의 실행은 모두 단일 매트릭스 기반 재합성을 거칩니다.
최적화 레벨 3에서는 최적화 루프 내부에서 2쿼비트 행렬 기반 재합성이 실행됩니다. 최적화 루프 조건은 또한 여러 번의 실행을 시도하고 출력이 변동하는 경우 최소값을 선택하는데, 이는 행렬 기반 재합성이 콘크리트 게이트 측면에서 상대적으로 불안정하기 때문에 필요합니다.
최적화 레벨 3은 일반적으로 대규모 회로의 경우 매우 비쌉니다.
스케줄링 단계
스케줄링 개념에 대한 가이드 수준의 설명입니다.
요청이 있는 경우 스케줄링 단계는 명시적인 큐비트 유휴 기간을 명시하는 Delay 명령어를 삽입하여 큐비트의 유휴 기간을 명시하는 역할을 합니다. 플러그인은 동적 디커플링 시퀀스 삽입과 같이 월타임에 민감한 변환을 선택적으로 수행할 수 있습니다.
스케줄링 단계의 입력은 ISA와 호환되는 회로입니다. 스케줄링 단계의 출력은 하드웨어 타이밍 정보를 만족하는 명시적인 Delay 하드웨어의 타이밍 정보를 만족하는 명령어를 포함해야 합니다.
PropertySet스케줄링 단계에서는 파이프라인의 속성을 node_start_time 설정해야 합니다.
스테이지 플러그인을 작성할 때 scheduling 의 엔트리 포인트는 qiskit.transpiler.scheduling 입니다. 기본 제공 플러그인은 다음과 같습니다:
방법 | 요약 |
|---|---|
| default | 다른 스케줄링 없이 타이밍 정렬 제약 조건을 충족하려고 시도합니다. |
| alap | 가능한 한 늦게 작업하는 것을 선호하여 회로를 예약합니다. |
| ASAP | 가능한 한 빠른 작업을 선호하여 회로를 예약합니다. |
내장 default 플러그인
회로에 이미 명시적인 타이밍이 있는 명령어가 포함되어 있지 않으면 아무것도 하지 않습니다. 회로에 명시적으로 시간이 지정된 연산이 있는 경우, 추가 패딩을 삽입하여 이러한 타이밍이 정렬 및 기타 하드웨어 제약 조건을 충족하는지 확인합니다.
내장 alap 플러그인
“가능한 한 늦게”라는 전략을 사용하여 모든 작업을 명시적으로 일정으로 잡으십시오. 이 방법은 알고리즘을 ALAPScheduleAnalysis 사용하여 게이트를 배치할 위치를 결정합니다.
내장 asap 플러그인
모든 작업을 “가능한 한 빨리” 전략을 사용하여 명시적으로 예약하십시오. 이 방법은 알고리즘을 ASAPScheduleAnalysis 사용하여 게이트를 배치할 위치를 결정합니다.
맞춤형 패스 관리자
사전 설정된 패스 매니저를 수정하는 것 외에도, 입력 회로를 변환하기 위한 완전히 사용자 정의된 파이프라인을 구축하기 위해 패스 매니저를 직접 구성할 수도 있습니다. 이를 수행하려면 해당 StagedPassManager 클래스를 직접 사용할 수 있습니다. 임의의 스테이지 이름을 정의하고, 해당 스테이지에 인스턴스를 PassManager 할당할 수 있습니다. translation예를 들어, 다음 코드는 와 라는 init 두 단계로 구성된 새로운 StagedPassManager 를 생성합니다.
from qiskit.transpiler.passes import (
UnitarySynthesis,
Collect2qBlocks,
ConsolidateBlocks,
UnitarySynthesis,
Unroll3qOrMore,
)
from qiskit.transpiler import PassManager, StagedPassManager
basis_gates = ["rx", "ry", "rxx"]
init = PassManager([UnitarySynthesis(basis_gates, min_qubits=3), Unroll3qOrMore()])
translate = PassManager(
[
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
UnitarySynthesis(basis_gates),
]
)
staged_pm = StagedPassManager(
stages=["init", "translation"], init=init, translation=translate
)StagedPassManager. 파일에 포함할 수 있는 단계 수에는 제한이 없습니다. 이 단계들이 Qiskit의 사전 설정 파이프라인에서 사용하는 단계와 일치할 필요는 없습니다.
Stage 생성자 함수는 사용자 정의 StagedPassManager 인스턴스를 생성하는 데 유용할 수 있습니다. 이들은 여러 단계에서 사용되는 공통 기능을 제공하는 패스 관리자를 생성합니다. 예를 들어, generate_embed_passmanager() 는 레이아웃 단계에서 선택된 초기 Layout 를 지정된 대상 디바이스에 “임베드”하기 위해 를 PassManager 생성합니다.
사용자 정의 트랜스파일러 패스 작성
키스킷은 맞춤형 특수 트랜스파일러 패스로 확장할 수 있도록 설계되었습니다.
DAGCircuit``PropertySet트랜스파일러 패스에는 두 가지 유형이 있습니다. 하나는 회로를 읽고 전역 분석 속성을 에 기록하는 “분석” 패스(AnalysisPass)이고, 다른 하나는 를 DAGCircuit 그 자리에서 수정하거나 새로운 를 반환하는 “변환” 패스(TransformationPass)입니다. 역사적으로 Qiskit은 이 두 유형을 엄격하게 구분하려고 시도해 왔습니다. TransformationPass하지만 최신 버전의 Qiskit에서는 모든 것을 분리하려고 시도하기보다는, 분석과 수정 작업을 모두 하나의 독립형 파일에 포함시키는 것이 꽤 일반적입니다. AnalysisPass패스가 순전히 분석에 기반한 것이라면, 여전히 이를 사용하는 것이 적절합니다.
통과의 일반 원칙
TransformationPassDAGCircuit을 수정하거나 새로 만들려면, 을 작성해야 합니다. AnalysisPassDAGCircuit만약 에만 데이터를 PropertySet 기록하고 는 수정하지 않으려면, 를 작성해야 합니다. TransformationPass두 가지를 모두 하고 싶다면,.을 작성하세요. BasePass.run()두 경우 모두, 필수 메서드는 단 하나뿐이며, 이것이 바로 이 패스의 핵심입니다. dag: DAGCircuit``self이 함수는 (를 제외한) 하나의 인자를 받아야 합니다. None``TranspilerPass만약 a라면, 를 DAGCircuit 반환해야 하며(현장에서 수정된 경우 입력값 그대로일 수도 있음), 반면 a라면 AnalysisPass를 반환해야 한다.
패스에 이니셜라이저가 있는 경우 super().__init__() 으로 전화해야 합니다.
일반적으로 패스는 초기화자에서 를 Target 받아들여야 하며, 이는 컴파일 대상인 양자 하드웨어를 설명합니다. 별도의 커플링 맵이나 기본 게이트 목록과 같은 “느슨한” 제약 조건을 수용하는 것은 권장되지 않습니다. 이 Target 방식이 일반적인 이종 하드웨어를 더 정확하게 묘사합니다.
파이프라인이 PassManager 실행되는 동안, 해당 패스의 메서드가 run() 호출되면 속성에 self.property_set 접근하여 트랜스파일링의 현재 PropertySet 상태를 확인할 수 있습니다. 이 파일은 원본을 그대로 읽거나 쓰셔야 합니다. 해당 패스는 읽기 및 쓰기 대상인 속성 집합에 포함된 속성이 있다면, 그 내용을 명확하게 명시해야 합니다.
무작위성과 결정론
퀀텀 컴파일에는 글로벌 최적화가 어려운 문제 해결이 수반되는 경우가 많습니다. 이러한 경우에는 확률론적 알고리즘과 휴리스틱 알고리즘이 더 적합한 경우가 많습니다. 그러나 이는 재현성 문제로 이어집니다.
사용자 지정 패스가 내장된 키스킷 패스가 따라야 하는 것과 정확히 동일한 규칙에 따라 결정론적이어야 한다는 공식적인 요건은 없습니다. 하지만 과학은 재현성을 바탕으로 발전하며, 이전에 관찰한 동작을 재현할 수 없을 때 디버깅은 악몽과도 같기 때문에 각자의 패스에서 이러한 규칙을 따르는 것을 강력히 권장합니다.
트랜스파일러 패스를 작성할 때 순서의 정확한 의미가 완전히 지정되지 않았을 수 있으며 패스가 특정 순서에 의존해서는 안 되지만, 다음과 같은 (대표적이고 완전하지 않은) 예시는 결정론적이라고 할 수 있습니다:
DAGCircuit.op_nodes()순서 노드는 에서 나타납니다. 반면,topological_op_nodes()는 기본적으로 노드 제거/삽입 순서에 전혀 영향을 받지 않는 정렬 키를 포함하고 있으므로, 동일한 데이터 흐름을 가진 동일한 노드 집합이 지정된다면, 비록 다른 순서로 구축되었더라도 완전히 결정론적입니다.DAGCircuit.edges()순서 가장자리는 에서 나타납니다.DAGCircuit.collect_2q_runs()런이 반환되는 순서와, 한 런 내의 노드들이 발견되는 정확한 순서입니다.predecessors(),bfs_successors(), 등과 같은 순서 비특이적 방법에서 노드가 나타나는 순서.
일반적으로 모든 회로를 정확히 동일한 순서로 수정해야 한다는 요구 사항이 있습니다. 예를 들어 노드를 추가, 계약 또는 제거하려면 이러한 수정 순서를 결정론적 순서로 수행해야 하며, 교체도 결정론적으로 지정해야 합니다.
이를 보장하기 위한 몇 가지 팁은 다음과 같습니다:
-
해시 기반 컨테이너를 반복 처리할 때는 각별히 주의하십시오. Python 의 에 대한 반복 처리는 해시 시드 무작위화 때문에 비결정적입니다
set. Rust에서, 기본 해시 함수를 사용하는 컨테이너를hashbrown포함한 표준 라이브러리의 해시 기반 컨테이너를 반복 처리할 때는 결과가 비결정적입니다.참고Python 의 에 대한 반복
dict* 처리는 결정론적이며*, 제거가 없었던 경우에는 삽입 순서가 보장되고, 결정론적인 제거가 있었던 경우에는 임의적이지만 여전히 결정론적인 순서가 보장됩니다.Python 에서 를 생성한
set다음 이를 반복 처리해야 하는 경우, 대신 모든 값이dict인 를 사용하는None것을 고려해 보세요. 단순히set회원 자격 여부를 확인하는 데 사용하는 것은 아무런 문제가 되지 않습니다.Rust에서는 와
indexmap그 구조체인 및IndexMap를 각각HashMap와 의 대체물로IndexSet사용하십시오HashSet. 이들은 Python 의 와 유사한 결정론적 반복 특성을 가지고 있습니다dict. -
패스에 확률적 요소가 포함되어 있다면, 반드시 입력을
seed받아들여야 하며, 이 입력이 정수형으로 주어졌을 경우 출력을 순수하게 만들어야 합니다. 일반적으로 이는 시드를 저장해 두었다가, 각 호출이 시작될 때마다 이 시드를 사용하여 새로운pRNG객체를 생성하는 것을BasePass.run()의미합니다. -
스레드 병렬 처리를 사용하는 경우, 스레드가 작업을 수행하거나 부분 결과를 반환하는 순서에 따라 출력이 달라지지 않도록 주의하세요. 예를 들어 스레드 풀에 작업을 분산하고 마지막에 결과를 수집하는 경우, 입력과 일치하는 순서로 출력이 정렬되도록 하세요. Python 에서
concurrent.futures.ThreadPoolExecutor.map()과 같은 기능을 통해 이를 보장합니다. 마찬가지로 Rust에서rayon의 병렬 반복기는 입력과 동일한 순서로 출력을 수집합니다."이 반복기의 각 항목에 함수를 적용하고 일부 메트릭을 최소화하는 함수를 선택"과 같은 병렬 _환산_은 일반적으로 메트릭의 퇴보의 경우 스레드 비결정성에 매우 취약하다는 점에 유의하세요. 예를 들어, 반복기의 두 항목이 동일한 비교 키를 가지고 있음에도 불구하고 동일하지 않은 출력을 생성하는 경우 스레드 환경에서 선택된 항목은 결정론적이지 않습니다. 이를 방지하려면 입력을 열거하고 시퀀스 번호를 동점 처리 키로 사용하는 등 결정론적 동점 처리기를 적용하여 두 항목의 점수가 동일한 경우 이전 입력에 해당하는 항목이 안정적으로 선택되도록 하여 퇴보성을 해제합니다.
양자 컴퓨터 표현
특정 백엔드용으로 코드를 QuantumCircuit 컴파일하기 위해서는, 트랜스파일러가 해당 백엔드의 제약 조건, 명령어 세트, 큐비트 특성 등을 포함한 특화된 표현을 확보해야만 효과적으로 컴파일하고 최적화할 수 있습니다. 이 BackendV2 클래스는 백엔드에 대한 쿼리 및 상호 작용을 위한 인터페이스를 정의하지만, 그 범위는 트랜스파일러의 요구 사항뿐만 아니라 작업 제출 관리 및 원격 서비스와의 잠재적 연동까지 포함하는 등 더 광범위합니다. 트랜스파일러에 필요한 구체적인 정보는 클래스에 Target 의해 정의됩니다
예를 들어, 간단한 Target 객체를 구성하려면 해당 객체가 지원하는 명령어에 대한 설명을 반복적으로 추가하면 됩니다:
from qiskit.circuit import Parameter, Measure
from qiskit.transpiler import Target, InstructionProperties
from qiskit.circuit.library import UGate, RZGate, RXGate, RYGate, CXGate, CZGate
target = Target(num_qubits=3)
target.add_instruction(CXGate(), {(0, 1): InstructionProperties(error=.0001, duration=5e-7)})
target.add_instruction(
UGate(Parameter('theta'), Parameter('phi'), Parameter('lam')),
{
(0,): InstructionProperties(error=.00001, duration=5e-8),
(1,): InstructionProperties(error=.00002, duration=6e-8)
}
)
target.add_instruction(
RZGate(Parameter('theta')),
{
(1,): InstructionProperties(error=.00001, duration=5e-8),
(2,): InstructionProperties(error=.00002, duration=6e-8)
}
)
target.add_instruction(
RYGate(Parameter('theta')),
{
(1,): InstructionProperties(error=.00001, duration=5e-8),
(2,): InstructionProperties(error=.00002, duration=6e-8)
}
)
target.add_instruction(
RXGate(Parameter('theta')),
{
(1,): InstructionProperties(error=.00001, duration=5e-8),
(2,): InstructionProperties(error=.00002, duration=6e-8)
}
)
target.add_instruction(
CZGate(),
{
(1, 2): InstructionProperties(error=.0001, duration=5e-7),
(2, 0): InstructionProperties(error=.0001, duration=5e-7)
}
)
target.add_instruction(
Measure(),
{
(0,): InstructionProperties(error=.001, duration=5e-5),
(1,): InstructionProperties(error=.002, duration=6e-5),
(2,): InstructionProperties(error=.2, duration=5e-7)
}
)
print(target)Target
Number of qubits: 3
Instructions:
cx
(0, 1):
Duration: 5e-07 sec.
Error Rate: 0.0001
u
(0,):
Duration: 5e-08 sec.
Error Rate: 1e-05
(1,):
Duration: 6e-08 sec.
Error Rate: 2e-05
rz
(1,):
Duration: 5e-08 sec.
Error Rate: 1e-05
(2,):
Duration: 6e-08 sec.
Error Rate: 2e-05
ry
(1,):
Duration: 5e-08 sec.
Error Rate: 1e-05
(2,):
Duration: 6e-08 sec.
Error Rate: 2e-05
rx
(1,):
Duration: 5e-08 sec.
Error Rate: 1e-05
(2,):
Duration: 6e-08 sec.
Error Rate: 2e-05
cz
(1, 2):
Duration: 5e-07 sec.
Error Rate: 0.0001
(2, 0):
Duration: 5e-07 sec.
Error Rate: 0.0001
measure
(0,):
Duration: 5e-05 sec.
Error Rate: 0.001
(1,):
Duration: 6e-05 sec.
Error Rate: 0.002
(2,):
Duration: 5e-07 sec.
Error Rate: 0.2이는 Target 0과 1 큐비트 간, 0과 UGate 1 큐비트에서, RZGate, RXGate, 그리고 1과 RYGate 2 큐비트에서, CZGate 1과 2 큐비트 간, 2와 0 큐비트 간, 그리고 모든 Measure 큐비트에서 를 지원하는 CXGate 3큐비트 백엔드를 나타냅니다.
Target또한.에서 가져온 정보의 특정 부분 집합을 표현하기 위한 특정 데이터 구조들도 있습니다. 예를 들어, 이 CouplingMap 클래스는 백엔드의 연결 제약 조건을 유향 그래프로만 표현하는 데 사용됩니다. 결합 맵은 방법을 Target.build_coupling_map() 사용하여 에서 Target 생성할 수 있습니다. 이러한 데이터 구조는 일반적으로 클래스보다 Target 먼저 등장했지만, 아직 인스턴스와 Target 기본적으로 연동되지 않는 일부 트랜스파일러 패스나 최신 BackendV2 인터페이스를 사용하지 않는 백엔드를 다룰 때 여전히 사용됩니다.
예를 들어, 앞서 소개한 3 큐비트 Target 예시의 를 CouplingMap 시각화하려면:
target.build_coupling_map().draw()
CZGate이는 와 에 CXGate 대해 지원되는 큐비트의 조합인 의 Target 전역적 연결성을 보여줍니다. CouplingMap.build_coupling_map()개별 연결 상태를 확인하려면 다음 명령어에 작업 이름을 전달하면 됩니다:
target.build_coupling_map('cx').draw()
target.build_coupling_map('cz').draw()
회로 배정
사전 설정 패스 관리자의 스케줄링 단계를 구성하는 방법.
회로를 목표 기준으로 변환하고, 디바이스에 매핑하고, 최적화한 후에는 스케줄링 단계를 적용하여 회로의 모든 유휴 시간을 선택적으로 고려할 수 있습니다. 높은 수준에서 스케줄링은 명령 실행 사이의 큐비트 유휴 시간을 고려하기 위해 회로에 지연을 삽입하는 것으로 생각할 수 있습니다. 예를 들어 다음과 같은 회로로 시작한다고 가정해 보겠습니다:
를 호출할 수 있습니다 transpile()scheduling_method 을 설정하여 호출할 수 있습니다:
from qiskit import QuantumCircuit, transpile
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(5)
ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0,range(1,5))
circ = transpile(ghz, backend, scheduling_method="asap")
circ.draw(output='mpl')
여기에서 트랜스파일러가 각 큐비트에 유휴 시간을 고려하기 위해 Delay 명령어를 삽입하여 각 큐비트에서 유휴 시간을 고려한 것을 볼 수 있습니다. 회로의 타이밍을 더 잘 파악하려면 timeline.draw() 함수를 사용하여 살펴볼 수도 있습니다:
회로 스케줄링은 분석과 제약 조건 매핑이라는 두 단계로 이루어지며, 그 다음에 패딩 단계가 이어집니다. ASAPSchedulingAnalysis첫 번째 단계에서는 또는 ASAPSchedulingAnalysis 과 ALAPSchedulingAnalysis 같은 스케줄링 분석 단계를 실행해야 합니다. 이 단계에서는 회로를 분석하고, 속성 집합에 정의된 스케줄링 알고리즘(의 경우 ALAPSchedulingAnalysis “가능한 한 늦게”, 의 경우 “가능한 한 빨리”)을 사용하여 회로 내 각 명령어의 시작 시간을 기록합니다. 회로에 초기 스케줄링이 완료되면, 정렬 제약 조건과 같은 대상 백엔드의 타이밍 제약 조건을 반영하기 위해 추가적인 처리 단계를 수행할 수 있습니다. 이는 일반적으로 pass를 ConstrainedReschedule 통해 수행되며, 이 과정을 통해 속성 세트에 설정된 스케줄링이 대상 백엔드의 제약 조건에 맞게 조정됩니다. 모든 스케줄링과 조정/재스케줄링이 완료되면, 또는 PadDynamicalDecoupling 과 PadDelay 같은 패딩 패스를 실행하여 명령어를 회로에 삽입함으로써 스케줄링이 완료됩니다.
트랜스파일러 API
하드웨어 설명
칼럼 1 | 칼럼 2 |
|---|---|
Target( [설명, num_qubits, dt,...] ) | Target 객체의 의도는 키스킷 컴파일러에 특정 백엔드의 제약 조건을 알려 컴파일러가 입력 회로를 작동하고 장치에 최적화된 것으로 컴파일할 수 있도록 하는 것입니다. |
InstructionProperties( [소요 시간, 오류] ) | 게이트 구현의 속성을 나타냅니다. |
WrapAngleRegistry() | 각도 래핑 기능 레지스트리 |
패스 매니저 정의
칼럼 1 | 칼럼 2 |
|---|---|
StagedPassManager( [단계] ) | 개별 단계로 구축된 패스 관리자 파이프라인입니다. |
PassManager( [passes, max_iteration] ) | 관리자에서 패스 세트와 트랜스파일링 중 일정을 확인할 수 있습니다. |
PassManagerConfig( [initial_layout,...] ) | 패스 관리자 구성. |
PassManagerCliffordTConfig( [initial_layout,...] ) | Clifford+T 트랜스파일링을 위한 Pass Manager 구성. |
generate_preset_pass_manager([...]) | 사전 설정 생성하기 PassManager |
generate_preset_clifford_t_pass_manager([...]) | StagedPassManagerClifford+T 프리셋을 생성합니다. |
generate_preset_pbc_pass_manager([...]) | StagedPassManager사전 설정된 PBC를 생성합니다. |
배치 및 토폴로지
칼럼 1 | 칼럼 2 |
|---|---|
Layout( [input_dict] ) | 레이아웃을 나타내는 양방향 딕셔너리. |
CouplingMap( [연결 목록, 설명] ) | 고정 커플링을 지정하는 지시형 그래프. |
TranspileLayout(initial_layout,...[,...] ) | 트랜스파일러의 출력 회로에 대한 레이아웃 속성입니다. |
스케줄링
칼럼 1 | 칼럼 2 |
|---|---|
InstructionDurations( [instruction_durations, dt] ) | 도우미 클래스를 통해 스케줄링에 대한 설명 시간을 제공합니다. |
초록 패스
칼럼 1 | 칼럼 2 |
|---|---|
TransformationPass(*args, **kwargs) | 변환 패스: 프로퍼티 세트가 아닌 DAG를 변경합니다. |
AnalysisPass(*args, **kwargs) | 분석 패스: DAG가 아닌 속성 집합을 변경합니다. |
예외
TranspilerError
TranspilerAccessError
exception qiskit.transpiler.TranspilerAccessError(*message)
베이스: PassManagerError
DEPRECATED: 트랜스파일러 패스에서 액세스 오류 예외.
오류 메시지를 설정합니다.
CouplingError
exception qiskit.transpiler.CouplingError(*msg)
베이스: QiskitError
커플링 그래프 객체에서 발생하는 오류에 대한 베이스 클래스입니다.
오류 메시지를 설정합니다.
LayoutError
exception qiskit.transpiler.LayoutError(*msg)
베이스: QiskitError
레이아웃 객체에서 발생한 오류입니다.
오류 메시지를 설정합니다.
CircuitTooWideForTarget
exception qiskit.transpiler.CircuitTooWideForTarget(*message)
베이스: TranspilerError
회로가 대상에 비해 너무 넓으면 오류가 발생합니다.
오류 메시지를 설정합니다.
InvalidLayoutError
exception qiskit.transpiler.InvalidLayoutError(*message)
베이스: TranspilerError
사용자가 제공한 레이아웃이 유효하지 않을 때 발생하는 오류입니다.
오류 메시지를 설정합니다.
최적화 지표
칼럼 1 | 칼럼 2 |
|---|---|
OptimizationMetric(*값) | 트랜스파일링 중에 고려되는 최적화 지표입니다. |