주 콘텐츠로 건너뛰기

분수 게이트를 사용한 양자 커널

사용량 추정: Heron r2 프로세서에서 30초 미만 (참고: 이는 추정치이며 실제 실행 시간은 다를 수 있습니다.)

학습 목표​

  • 분수 게이트가 무엇이며 IBM® QPU에서 Circuit 깊이와 실행 시간을 어떻게 줄이는지

  • 분수 게이트 사용과 관련된 제약 조건 (특히 RZZ 각도 범위)

  • IBM Quantum Compute Service와 함께 분수 게이트를 사용하는 양자 커널 워크플로우를 구성하는 방법

  • 분수 게이트를 사용한 경우와 사용하지 않은 경우의 하드웨어 실행 지표(깊이, 실행 시간, 비국소 Gate 수, 충실도)를 비교하는 방법

  • 표준 Qiskit patterns 워크플로우를 유지하면서 분수 RX Gate만 사용하는 방법

사전 준비 사항​

배경​

IBM QPU의 분수 게이트​

분수 게이트는 임의 각도 회전(특정 범위 내)을 직접 실행할 수 있는 매개변수화된 양자 게이트로, 여러 기저 게이트로 분해할 필요를 없애줍니다. 물리적 qubit 간의 네이티브 상호작용을 활용하면 특정 유니터리를 하드웨어에서 더 효율적으로 구현할 수 있습니다.

IBM Quantum® Heron QPU는 다음과 같은 분수 게이트를 지원합니다:

  • RZZ(θ)R_{ZZ}(\theta) (0<θ<π/20 < \theta < \pi / 2)
  • RX(θ)R_X(\theta) (임의의 실수 θ\theta)

이 게이트들은 양자 Circuit의 깊이와 실행 시간을 크게 줄일 수 있습니다. RZZR_{ZZ} 및 RXR_X에 크게 의존하는 애플리케이션, 예를 들어 해밀토니안 시뮬레이션, 양자 근사 최적화 알고리즘(QAOA), 양자 커널 방법 등에서 특히 유리합니다. 이 튜토리얼에서는 실용적인 예시로 양자 커널에 초점을 맞춥니다.

제한 사항​

분수 게이트는 현재 실험적 기능이며 몇 가지 제약이 있습니다:

분수 게이트는 표준 방식과 다른 워크플로우를 필요로 합니다. 이 튜토리얼은 실용적인 애플리케이션을 통해 분수 게이트로 작업하는 방법을 설명합니다.

분수 게이트에 대한 자세한 내용은 다음을 참조하세요.

RZZ 각도 제약 조건에 대한 워크플로우 접근 방식​

분수 게이트 사용 워크플로우는 일반적으로 Qiskit patterns 워크플로우를 따릅니다. 핵심 차이점은 모든 RZZ 각도가 0<θ≤π/20 < \theta \leq \pi/2 제약 조건을 만족해야 한다는 것입니다. 아래에서 설명하는 것처럼, 이 조건을 충족시키는 두 가지 접근 방식이 있습니다. 두 번째 접근 방식을 권장하며, 이 튜토리얼에서는 양자 커널 방법에서 영감을 받은 예제를 통해 이를 보여줍니다. 양자 커널이 유용하게 활용될 수 있는 경우를 더 잘 이해하려면 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과 유니터리 오버랩을 결합하여 수행합니다. 커널 함수는 특징 매핑된 공간의 벡터를 받아 커널 행렬의 항목으로서 내적을 반환합니다: K(x,y)=⟨Φ(x)∣Φ(y)⟩,K(x, y) = \langle \Phi(x) | \Phi(y) \rangle, 여기서 ∣Φ(x)⟩|\Phi(x)\rangle은 특징 매핑된 양자 상태를 나타냅니다.

RZZ 게이트를 사용하여 ZZ 특징 맵 Circuit을 수동으로 구성합니다. Qiskit은 내장된 zz_feature_map을 제공하지만, Qiskit v2.4.1 기준으로 현재 RZZ 게이트를 지원하지 않습니다 (이슈 참조).

다음으로, 동일한 입력에 대한 커널 함수를 계산합니다 — 예를 들어 K(x,x)=1K(x, x) = 1. 잡음이 있는 양자 컴퓨터에서는 잡음으로 인해 이 값이 1보다 작을 수 있습니다. 1에 가까운 결과는 실행에서 잡음이 낮음을 나타냅니다. 이 튜토리얼에서는 이 값을 *충실도(fidelity)*라고 부르며, 다음과 같이 정의합니다: fidelity=K(x,x).\text{fidelity} = K(x, x).

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)

Output of the previous code cell

표준 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)

Output of the previous code cell

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})

Output of the previous code cell

분수 게이트의 영향을 평가하기 위해, 비국소 게이트(이 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의 측정 확률을 통해 커널 함수 값 K(x,x)K(x, x)를 구할 수 있습니다.

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})

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

대규모 하드웨어 예제​

이 섹션에서는 최대 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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

분수 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")

Output of the previous code cell

먼저 분수 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})

Output of the previous code cell

다음으로, 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})

Output of the previous code cell

분수 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})

Output of the previous code cell

다음 단계​

권장 사항

이 내용이 흥미로웠다면 다음 자료에도 관심이 있을 수 있습니다.