주 콘텐츠로 건너뛰기

분수 게이트 소개

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

배경

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), 양자 커널 방법 등에서 특히 유리합니다. 이 튜토리얼에서는 실용적인 예시로 양자 커널에 초점을 맞춥니다.

제한 사항

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

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

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

개요

분수 게이트 사용 워크플로우는 일반적으로 Qiskit 패턴 워크플로우를 따릅니다. 핵심 차이점은 모든 RZZ 각도가 0<θπ/20 < \theta \leq \pi/2 제약 조건을 만족해야 한다는 것입니다. 이 조건을 충족시키는 두 가지 접근 방식이 있습니다. 이 튜토리얼은 두 번째 접근 방식에 초점을 맞추고 권장합니다.

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit 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을 로컬에 저장해야 하므로 첫 번째 접근 방식보다 높은 계산 비용이 발생합니다. 또한 특정 시나리오에서 RZZ 게이트 변환이 실패할 수 있는 Qiskit의 알려진 문제가 있습니다. 해결 방법은 문제 해결 섹션을 참조하세요. 이 튜토리얼은 양자 커널 방법에서 영감을 받은 예시를 통해 두 번째 접근 방식으로 분수 게이트를 사용하는 방법을 보여줍니다. 양자 커널이 어디서 유용할지 더 잘 이해하기 위해 Liu, Arunachalam & Temme (2021)을 읽어보세요.

양자 커널 훈련 튜토리얼과 IBM Quantum Learning의 양자 머신 러닝 과정에서 양자 커널 강의도 함께 살펴볼 수 있습니다.

요구 사항

이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:

  • Qiskit SDK v2.0 이상(시각화 지원 포함)
  • Qiskit Runtime v0.37 이상 (pip install qiskit-ibm-runtime)
  • 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 unitary_overlap
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

분수 게이트 활성화 및 기저 게이트 확인

분수 게이트를 사용하려면 use_fractional_gates=True 옵션을 설정하여 분수 게이트를 지원하는 Backend를 얻을 수 있습니다. Backend가 분수 게이트를 지원하는 경우, 기저 게이트 목록에서 rzzrx를 확인할 수 있습니다.

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_fez
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']

분수 게이트를 사용한 워크플로우

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.0.2 기준으로 현재 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, 44, 4))
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
)
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 = SamplerV2(mode=backend_f)
sampler_f.options.dynamical_decoupling.enable = True
sampler_f.options.dynamical_decoupling.sequence_type = "XY4"
sampler_f.options.dynamical_decoupling.skip_reset_qubits = True
job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())
d4bninsi51bc738j97eg

Step 4: 결과 후처리 및 원하는 고전적 형식으로 반환

출력에서 전체 0 비트스트링 00...00의 측정 확률을 통해 커널 함수 값 K(x,x)K(x, x)를 구할 수 있습니다.

# job = service.job("d1obougt0npc73flhiag")
result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)
usage_f = job.usage()
[0.9005, 0.647, 0.3345, 0.355, 0.3315, 0.174, 0.1875, 0.149, 0.1175, 0.085]

분수 게이트 없는 워크플로 및 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 = SamplerV2(backend_c)
sampler_c.options.dynamical_decoupling.enable = True
sampler_c.options.dynamical_decoupling.sequence_type = "XY4"
sampler_c.options.dynamical_decoupling.skip_reset_qubits = True
job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)
print(job.job_id())
d4bnirvnmdfs73ae3a2g
# step 4: post-processing
# job = service.job("d1obp8j3rr0s73bg4810")
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)
usage_c = job.usage()
[0.6675, 0.5725, 0.098, 0.102, 0.065, 0.0235, 0.006, 0.0015, 0.0015, 0.002]

깊이와 충실도 비교

이 섹션에서는 분수 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 0x12bcaac50>

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 0x12bdef310>

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 0x12be8ac90>

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 0x12bea8290>

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: 7 seconds
fractional gates: 7 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를 비활성화합니다.

from qiskit.circuit.library import n_local
from qiskit.transpiler import Target
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를 최적화하는 방법을 시연합니다.

이 섹션을 진행하려면 qiskit-basis-constructor 패키지를 설치해야 합니다. 이는 Qiskit의 새로운 Transpile 플러그인 베타 버전으로, 향후 Qiskit에 통합될 수 있습니다.

# %pip install qiskit-basis-constructor
from qiskit.circuit.library import UGate
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY

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 번역 방법을 사용하여 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

문제 해결

문제: Transpile 후에도 유효하지 않은 RZZ 각도가 남아있을 수 있음

Qiskit v2.0.3 기준으로, Transpile 후에도 유효하지 않은 각도를 가진 RZZ Gate가 Circuit에 남아있는 알려진 문제가 있습니다. 이 문제는 일반적으로 다음 조건에서 발생합니다.

generate_preset_pass_manager 또는 transpiler와 함께 target 옵션 사용 시 오류

generate_preset_pass_manager 또는 transpiler와 함께 target 옵션을 사용하면, 특수 Transpiler 패스인 FoldRzzAngle이 호출되지 않습니다. 분수 Gate에 대해 RZZ 각도가 올바르게 처리되도록 하려면, 항상 target 대신 backend 옵션을 사용하는 것을 권장합니다. 자세한 내용은 이 이슈를 참조하세요.

특정 Gate가 포함된 Circuit에서 오류

Circuit에 XXPlusYYGate와 같은 특정 Gate가 포함되어 있으면, Qiskit Transpiler가 유효하지 않은 각도를 가진 RZZ Gate를 생성할 수 있습니다. 이 문제가 발생하면, 해결 방법에 대해 이 GitHub 이슈를 참조하세요.

튜토리얼 설문조사

이 짧은 설문조사에 참여하여 본 튜토리얼에 대한 피드백을 제공해 주세요. 여러분의 의견은 콘텐츠와 사용자 경험을 개선하는 데 도움이 됩니다.

설문조사 링크

Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.