분수 게이트를 사용한 양자 커널
사용량 추정: Heron r2 프로세서에서 30초 미만 (참고: 이는 추정치이며 실제 실행 시간은 다를 수 있습니다.)
학습 목표
-
분수 게이트가 무엇이며 IBM® QPU에서 Circuit 깊이와 실행 시간을 어떻게 줄이는지
-
분수 게이트 사용과 관련된 제약 조건 (특히 RZZ 각도 범위)
-
IBM Quantum Compute Service와 함께 분수 게이트를 사용하는 양자 커널 워크플로우를 구성하는 방법
-
분수 게이트를 사용한 경우와 사용하지 않은 경우의 하드웨어 실행 지표(깊이, 실행 시간, 비국소 Gate 수, 충실도)를 비교하는 방법
-
표준 Qiskit patterns 워크플로우를 유지하면서 분수 RX Gate만 사용하는 방법
사전 준비 사항
-
Qiskit patterns 워크플로우
-
분수 게이트 가이드
배경
IBM QPU의 분수 게이트
분수 게이트는 임의 각도 회전(특정 범위 내)을 직접 실행할 수 있는 매개변수화된 양자 게이트로, 여러 기저 게이트로 분해할 필요를 없애줍니다. 물리적 qubit 간의 네이티브 상호작용을 활용하면 특정 유니터리를 하드웨어에서 더 효율적으로 구현할 수 있습니다.
IBM Quantum® Heron QPU는 다음과 같은 분수 게이트를 지원합니다:
- ()
- (임의의 실수 )
이 게이트들은 양자 Circuit의 깊이와 실행 시간을 크게 줄일 수 있습니다. 및 에 크게 의존하는 애플리케이션, 예를 들어 해밀토니안 시뮬레이션, 양자 근사 최적화 알고리즘(QAOA), 양자 커널 방법 등에서 특히 유리합니다. 이 튜토리얼에서는 실용적인 예시로 양자 커널에 초점을 맞춥니다.
제한 사항
분수 게이트는 현재 실험적 기능이며 몇 가지 제약이 있습니다:
- 는 범위의 각도로 제한됩니다.
- 분수 게이트는 동적 Circuit, Pauli twirling, 확률적 오류 취소 (PEC), 영잡음 외삽 (ZNE) (확률적 오류 증폭 (PEA) 사용)에는 지원되지 않습니다.
분수 게이트는 표준 방식과 다른 워크플로우를 필요로 합니다. 이 튜토리얼은 실용적인 애플리케이션을 통해 분수 게이트로 작업하는 방법을 설명합니다.
분수 게이트에 대한 자세한 내용은 다음을 참조하세요.
RZZ 각도 제약 조건에 대한 워크플로우 접근 방식
분수 게이트 사용 워크플로우는 일반적으로 Qiskit patterns 워크플로우를 따릅니다. 핵심 차이점은 모든 RZZ 각도가 제약 조건을 만족해야 한다는 것입니다. 아래에서 설명하는 것처럼, 이 조건을 충족시키는 두 가지 접근 방식이 있습니다. 두 번째 접근 방식을 권장하며, 이 튜토리얼에서는 양자 커널 방법에서 영감을 받은 예제를 통해 이를 보여줍니다. 양자 커널이 유용하게 활용될 수 있는 경우를 더 잘 이해하려면 Liu, Arunachalam & Temme (2021) 을 읽어보시는 것을 권장합니다.
양자 커널 훈련 튜토리얼과 IBM Quantum® Learning의 양자 머신 러닝 과정에서 양자 커널 강의도 함께 살펴볼 수 있습니다.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-basis-constructor qiskit-ibm-runtime
1. RZZ 각도 제약을 만족하는 매개변수 값 생성
모든 RZZ 각도가 유효한 범위 내에 있다고 확신하는 경우, 표준 Qiskit 패턴 워크플로우를 따를 수 있습니다. 이 경우, PUB의 일부로 매개변수 값을 제출하기만 하면 됩니다. 워크플로우는 다음과 같이 진행됩니다.
pm = generate_preset_pass_manager(backend=backend, ...)
t_circuit = pm.run(circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit, parameter_values)])
estimator.run([(t_circuit, t_observable, parameter_values)])
유효 범위를 벗어난 각도를 가진 RZZ 게이트가 포함된 PUB를 제출하려고 하면 다음과 같은 오류 메시지가 발생합니다:
'The instruction rzz is supported only for angles in the range [0, pi/2], but an angle (20.0) outside of this range has been requested; via parameter value(s) γ[0]=10.0, substituted in parameter expression 2.0*γ[0].'
이 오류를 방지하려면 아래에 설명된 두 번째 접근 방식을 사용하세요.
2. Transpilation 전에 Circuit에 매개변수 값 할당
qiskit-ibm-runtime 패키지는 FoldRzzAngle이라는 특수 Transpiler 패스를 제공합니다.
이 패스는 모든 RZZ 각도가 RZZ 각도 제약 조건을 준수하도록 양자 Circuit을 변환합니다.
generate_preset_pass_manager 또는 transpile에 Backend를 제공하면, Qiskit이 자동으로 FoldRzzAngle을 양자 Circuit에 적용합니다.
이 접근 방식은 Transpilation 전에 양자 Circuit에 매개변수 값을 할당할 것을 요구합니다.
워크플로우는 다음과 같이 진행됩니다.
pm = generate_preset_pass_manager(backend=backend, ...)
b_circuit = circuit.assign_parameters(parameter_values)
t_circuit = pm.run(b_circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit,)])
estimator.run([(t_circuit, t_observable)])
이 워크플로우는 매개변수 값을 양자 Circuit에 할당하고 매개변수가 결합된 Circuit을 로컬에 저장하는 과정을 포함하므로 첫 번째 접근 방식보다 계산 비용이 더 많이 든다는 점에 유의하세요.
qiskit-ibm-runtime v0.47.0에서 알려진 문제를 참고하세요. 특정 시나리오에서는 Transpile 후에도 유효하지 않은 각도를 가진 RZZ Gate가 Circuit에 남아있을 수 있습니다.
이 문제의 진행 상황을 추적하려면 qiskit-ibm-runtime#2441을 참고하세요. 해결될 때까지는 다음 우회 방법을 권장합니다.
pm = generate_preset_pass_manager(backend=backend, ...)
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
... = pm.run(...)
요구 사항
이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:
- Qiskit SDK v2.0 이상(시각화 지원 포함)
- Qiskit Runtime v0.41 이상 (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 이상 (
pip install qiskit-aer) - Qiskit Basis Constructor (
pip install qiskit_basis_constructor)
설정
import matplotlib.pyplot as plt
import numpy as np
from qiskit import QuantumCircuit, generate_preset_pass_manager
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import UGate, n_local, unitary_overlap
from qiskit.transpiler import Target, PassManager
from qiskit.transpiler.passes import (
Optimize1qGatesDecomposition,
RemoveIdentityEquivalent,
)
from qiskit_aer.primitives import SamplerV2 as AerSampler
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_runtime.transpiler.passes import FoldRzzAngle
분수 게이트 활성화 및 기저 게이트 확인
분수 게이트를 사용하려면 use_fractional_gates=True 옵션을 설정하여 분수 게이트를 지원하는 Backend를 얻을 수 있습니다.
Backend가 분수 게이트를 지원하는 경우, 기저 게이트 목록에서 rzz와 rx를 확인할 수 있습니다.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
) # backend should be a heron device or later
backend_name = backend.name
backend_c = service.backend(backend_name) # w/o fractional gates
backend_f = service.backend(
backend_name, use_fractional_gates=True
) # w/ fractional gates
print(f"Backend: {backend_name}")
print(f"No fractional gates: {backend_c.basis_gates}")
print(f"With fractional gates: {backend_f.basis_gates}")
if "rzz" not in backend_f.basis_gates:
print(f"Backend {backend_name} does not support fractional gates")
Backend: ibm_marrakesh
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']
소규모 시뮬레이터 예제
이 섹션에서는 양자 커널 Circuit을 실용적인 예제로 사용하여 시뮬레이터에서 Qiskit patterns 워크플로우의 네 단계를 살펴봅니다.
Step 1: 고전적 입력을 양자 문제로 매핑
양자 커널 Circuit
이 섹션에서는 분수 게이트의 워크플로우를 소개하기 위해 RZZ 게이트를 사용하는 양자 커널 Circuit을 살펴봅니다.
커널 행렬의 개별 항목을 계산하기 위한 양자 Circuit을 구성하는 것부터 시작합니다. 이는 ZZ 특징 맵 Circuit과 유니터리 오버랩을 결합하여 수행합니다. 커널 함수는 특징 매핑된 공간의 벡터를 받아 커널 행렬의 항목으로서 내적을 반환합니다: 여기서 은 특징 매핑된 양자 상태를 나타냅니다.
RZZ 게이트를 사용하여 ZZ 특징 맵 Circuit을 수동으로 구성합니다.
Qiskit은 내장된 zz_feature_map을 제공하지만, Qiskit v2.4.1 기준으로 현재 RZZ 게이트를 지원하지 않습니다 (이슈 참조).
다음으로, 동일한 입력에 대한 커널 함수를 계산합니다 — 예를 들어 . 잡음이 있는 양자 컴퓨터에서는 잡음으로 인해 이 값이 1보다 작을 수 있습니다. 1에 가까운 결과는 실행에서 잡음이 낮음을 나타냅니다. 이 튜토리얼에서는 이 값을 *충실도(fidelity)*라고 부르며, 다음과 같이 정의합니다:
optimization_level = 2
shots = 2000
reps = 3
rng = np.random.default_rng(seed=123)
def my_zz_feature_map(num_qubits: int, reps: int = 1) -> QuantumCircuit:
x = ParameterVector("x", num_qubits * reps)
qc = QuantumCircuit(num_qubits)
qc.h(range(num_qubits))
for k in range(reps):
K = k * num_qubits
for i in range(num_qubits):
qc.rz(x[i + K], i)
pairs = [(i, i + 1) for i in range(num_qubits - 1)]
for i, j in pairs[0::2] + pairs[1::2]:
qc.rzz((np.pi - x[i + K]) * (np.pi - x[j + K]), i, j)
return qc
def quantum_kernel(num_qubits: int, reps: int = 1) -> QuantumCircuit:
qc = my_zz_feature_map(num_qubits, reps=reps)
inner_product = unitary_overlap(qc, qc, "x", "y", insert_barrier=True)
inner_product.measure_all()
return inner_product
def random_parameters(inner_product: QuantumCircuit) -> np.ndarray:
return np.tile(rng.random(inner_product.num_parameters // 2), 2)
def fidelity(result) -> float:
ba = result.data.meas
return ba.get_int_counts().get(0, 0) / ba.num_shots
4~40 qubit 시스템에 대한 양자 커널 Circuit과 해당 매개변수 값이 생성되고, 이후 충실도가 평가됩니다.
qubits = list(range(4, 12, 2))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
4-Qubit Circuit은 아래에 시각화되어 있습니다.
circuits[0].draw("mpl", fold=-1)

표준 Qiskit 패턴 워크플로우에서는 매개변수 값이 일반적으로 PUB의 일부로 Sampler 또는 Estimator 프리미티브에 전달됩니다. 그러나 분수 게이트를 지원하는 Backend를 사용할 때는 Transpilation 전에 이 매개변수 값을 양자 Circuit에 명시적으로 할당해야 합니다.
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)

Step 2: 양자 하드웨어 실행을 위한 문제 최적화
그런 다음 표준 Qiskit 패턴에 따라 패스 매니저를 사용하여 Circuit을 트랜스파일합니다.
generate_preset_pass_manager에 분수 게이트를 지원하는 Backend를 제공하면, FoldRzzAngle이라는 특수 패스가 자동으로 포함됩니다.
이 패스는 RZZ 각도 제약 조건을 준수하도록 Circuit을 수정합니다.
결과적으로, 이전 그림에서 음수 값을 가졌던 RZZ Gate가 양수 값으로 변환되고, 일부 추가적인 X Gate가 삽입됩니다.
backend_f = service.backend(name=backend_name, use_fractional_gates=True)
# pm_f includes `FoldRzzAngle` pass
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_f
)
pm_f.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend_f.target),
RemoveIdentityEquivalent(target=backend_f.target),
]
)
t_qc_f = pm_f.run(b_qc)
print(t_qc_f[0].count_ops())
t_qc_f[0].draw("mpl", fold=-1)
OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})

분수 게이트의 영향을 평가하기 위해, 비국소 게이트(이 Backend의 경우 CZ 및 RZZ)의 수와 Circuit 깊이 및 실행 시간을 측정하고, 이를 나중에 표준 워크플로와 비교합니다.
nnl_f = [qc.num_nonlocal_gates() for qc in t_qc_f]
depth_f = [qc.depth() for qc in t_qc_f]
duration_f = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc_f
]
Step 3: Qiskit 프리미티브를 사용한 실행
분수 게이트를 지원하는 Backend로 트랜스파일된 Circuit을 실행합니다.
sampler_f = AerSampler.from_backend(backend_f)
job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())
085ce928-767e-4200-93bf-3905e5411cfe
Step 4: 결과 후처리 및 원하는 고전적 형식으로 반환
출력에서 전체 0 비트스트링 00...00의 측정 확률을 통해 커널 함수 값 를 구할 수 있습니다.
result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)
[0.929, 0.882, 0.8645, 0.817]
분수 게이트 없는 워크플로 및 Circuit 비교
이 섹션에서는 분수 Gate를 지원하지 않는 Backend를 사용하는 표준 Qiskit 패턴 워크플로를 소개합니다. Transpile된 Circuit을 비교하면, 분수 Gate를 사용하는 버전(이전 섹션)이 분수 Gate를 사용하지 않는 버전보다 더 간결하다는 것을 확인할 수 있습니다.
# step 1: map classical inputs to quantum problem
# `circuits` and `params` from the previous section are reused here
# step 2: optimize circuits
backend_c = service.backend(backend_name) # w/o fractional gates
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc_c = pm_c.run(circuits)
print(t_qc_c[0].count_ops())
t_qc_c[0].draw("mpl", fold=-1)
OrderedDict({'rz': 130, 'sx': 80, 'cz': 36, 'measure': 4, 'barrier': 2})

nnl_c = [qc.num_nonlocal_gates() for qc in t_qc_c]
depth_c = [qc.depth() for qc in t_qc_c]
duration_c = [
qc.estimate_duration(backend_c.target, unit="u") for qc in t_qc_c
]
# step 3: execute
sampler_c = AerSampler.from_backend(backend_c)
job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)
print(job.job_id())
f2cca29d-7263-4976-9e51-13a91b75c3ae
# step 4: post-processing
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)
[0.8625, 0.7605, 0.702, 0.671]
깊이, 실행 시간, 충실도 비교
이 섹션에서는 분수 Gate가 있는 Circuit과 없는 Circuit 간의 비국소 Gate 수와 충실도를 비교합니다. 이를 통해 실행 효율성과 품질 측면에서 분수 Gate 사용의 잠재적 이점을 살펴봅니다.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x116af3cb0>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11ea4f4d0>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x1247fc440>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x120b792b0>
대규모 하드웨어 예제
이 섹션에서는 최대 40 qubit의 양자 하드웨어에서 분수 게이트를 사용한 경우와 사용하지 않은 경우의 양자 커널 워크플로우를 벤치마크합니다.
단계 1-4 결합
이 워크플로우는 소규모 예제와 동일한 구조를 따릅니다. 분수 게이트를 사용한 경우와 사용하지 않은 경우의 모든 Circuit을 Transpile하고 지표를 수집한 다음, 실제 양자 하드웨어에 Circuit을 제출합니다.
# -------------------------Step 1-------------------------
qubits = list(range(4, 44, 4))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
def benchmark(b_qc, backend):
# -------------------------Step 2-------------------------
pm = generate_preset_pass_manager(optimization_level, backend=backend)
if "rzz" in backend.target.operation_names:
# workaround until https://github.com/Qiskit/qiskit-ibm-runtime/issues/2441 is resolved
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
t_qc = pm.run(b_qc)
nnl = [qc.num_nonlocal_gates() for qc in t_qc]
depth = [qc.depth() for qc in t_qc]
duration = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc
]
# -------------------------Step 3-------------------------
sampler = SamplerV2(mode=backend)
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
sampler.options.dynamical_decoupling.skip_reset_qubits = True
sampler.options.environment.job_tags = ["TUT_FG"]
job = sampler.run(t_qc, shots=shots)
job_id = job.job_id()
return nnl, depth, duration, job_id
def postprocessing(job_id: str):
# -------------------------Step 4-------------------------
job = service.job(job_id)
result = job.result()
fidelities = [fidelity(result=res) for res in result]
usage = job.usage()
return fidelities, usage
backend_f = service.backend(backend_name, use_fractional_gates=True)
nnl_f, depth_f, duration_f, job_id_f = benchmark(
b_qc, backend_f
) # step 2 & 3
print("job id (w/ fractional gates):", job_id_f)
fidelity_f, usage_f = postprocessing(job_id_f) # step 4
job id (w/ fractional gates): d8uasitbh0os73eqnpig
backend_c = service.backend(backend_name, use_fractional_gates=False)
nnl_c, depth_c, duration_c, job_id_c = benchmark(b_qc, backend_c)
print("job id (w/o fractional gates):", job_id_c)
fidelity_c, usage_c = postprocessing(job_id_c)
job id (w/o fractional gates): d8uav3lposuc738pruug
그런 다음 지표를 비교합니다.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12461e660>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11f2ac980>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x125c91be0>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11fcf6e40>
분수 Gate가 있는 경우와 없는 경우의 QPU 사용 시간을 비교합니다. 다음 셀의 결과는 QPU 사용 시간이 거의 동일함을 보여줍니다.
print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")
no fractional gates: 8 seconds
fractional gates: 8 seconds
고급 주제: 분수 RX Gate만 사용하기
분수 Gate를 사용할 때 수정된 워크플로가 필요한 주된 이유는 RZZ Gate 각도의 제한에서 비롯됩니다. 그러나 분수 RX Gate만 사용하고 분수 RZZ Gate를 제외하면, 표준 Qiskit 패턴 워크플로를 계속 따를 수 있습니다. 이 접근 방식은 RX Gate와 U Gate가 많이 포함된 Circuit에서 전체 Gate 수를 줄이고 성능을 향상시킬 가능성이 있어 의미 있는 이점을 제공할 수 있습니다. 이 섹션에서는 RZZ Gate를 제외하고 분수 RX Gate만 사용하여 Circuit을 최적화하는 방법을 시연합니다.
이를 지원하기 위해, Target 객체에서 특정 기저 Gate를 비활성화할 수 있는 유틸리티 함수를 제공합니다. 여기서는 이 함수를 사용하여 RZZ Gate를 비활성화합니다.
def remove_instruction_from_target(target: Target, gate_name: str) -> Target:
new_target = Target(
description=target.description,
num_qubits=target.num_qubits,
dt=target.dt,
granularity=target.granularity,
min_length=target.min_length,
pulse_alignment=target.pulse_alignment,
acquire_alignment=target.acquire_alignment,
qubit_properties=target.qubit_properties,
concurrent_measurements=target.concurrent_measurements,
)
for name, qarg_map in target.items():
if name == gate_name:
continue
instruction = target.operation_from_name(name)
if qarg_map == {None: None}:
qarg_map = None
new_target.add_instruction(instruction, qarg_map, name=name)
return new_target
U, CZ, RZZ Gate로 구성된 Circuit을 예시로 사용합니다.
qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")
먼저 분수 Gate를 지원하지 않는 Backend를 위해 Circuit을 Transpile합니다.
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc = pm_c.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 23, 'sx': 16, 'cz': 4})

다음으로, RZZ Gate를 제외하고 분수 RX Gate를 사용하여 동일한 Circuit을 Transpile합니다. RX Gate를 더 효율적으로 구현함으로써 전체 Gate 수가 약간 줄어드는 것을 확인할 수 있습니다.
backend_f = service.backend(backend_name, use_fractional_gates=True)
target = remove_instruction_from_target(backend_f.target, "rzz")
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})

분수 RX Gate로 U Gate 최적화하기
이 섹션에서는 이전 섹션에서 소개한 동일한 Circuit을 기반으로, 분수 RX Gate를 사용하여 U Gate를 최적화하는 방법을 보여줍니다. RZZ Gate를 제외하고 분수 RX Gate만 사용하여 Circuit을 Transpile합니다. 다음과 같이 사용자 정의 분해 규칙을 도입함으로써, U Gate를 구현하는 데 필요한 단일 qubit Gate 수를 줄일 수 있습니다.
이 기능은 현재 이 GitHub 이슈에서 논의 중입니다.
# special decomposition rule for UGate
x = ParameterVector("x", 3)
zxz = QuantumCircuit(1)
zxz.rz(x[2] - np.pi / 2, 0)
zxz.rx(x[0], 0)
zxz.rz(x[1] + np.pi / 2, 0)
DEFAULT_EQUIVALENCE_LIBRARY.add_equivalence(UGate(x[0], x[1], x[2]), zxz)
다음으로, qiskit-basis-constructor 패키지가 제공하는 constructor-beta 변환(translation) 방법을 사용하여 Transpiler를 적용합니다.
그 결과, 이전 Transpile과 비교했을 때 전체 Gate 수가 줄어듭니다.
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
translation_method="constructor-beta",
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 16, 'rx': 9, 'cz': 4})
다음 단계
이 내용이 흥미로웠다면 다음 자료에도 관심이 있을 수 있습니다.
- 분수 게이트 가이드
- 분수 게이트를 사용하지 않아야 하는 경우
FoldRzzAngleTranspiler pass API 참조- 양자 커널 훈련 튜토리얼
- 양자 머신 러닝 과정의 양자 커널 강의