Skip to main content
IBM Quantum Platform

분수 게이트를 갖는 양자 커널

사용 예상 시간: Heron r2 프로세서에서 30초 미만(참고: 이는 예상치일 뿐입니다. 런타임은 다를 수 있습니다.)


학습 성과

  • IBM® QPU에서 분수 게이트란 무엇이며, 이를 통해 회로 깊이와 실행 시간을 어떻게 줄이는가
  • 분수 게이트 사용과 관련된 제약 사항(특히 RZZ 각도 범위)
  • IBM Quantum Compute Service를 사용하여 분수 게이트를 활용하는 양자 커널 워크플로를 구축하는 방법
  • 분수 게이트를 적용한 경우와 적용하지 않은 경우의 하드웨어 실행 지표(깊이, 소요 시간, 비국소 게이트 수, 정확도)를 비교하는 방법
  • 표준 Qiskit 패턴 워크플로를 유지하면서 분수형 RX 게이트만 사용하는 방법

전제조건


배경

IBM 양자 프로세싱 유닛(QPU)의 분수 게이트

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

IBM 퀀텀® 헤론 QPU는 다음과 같은 프랙셔널 게이트를 지원합니다:

  • RZZ(θ)R_{ZZ}(\theta) 에 대한 0<θ<π/20 < \theta < \pi / 2
  • RX(θ)R_X(\theta) 진정한 가치를 위해 θ\theta

이러한 게이트는 양자 회로의 깊이와 지속 시간을 크게 줄일 수 있습니다. 특히 RZZR_{ZZ}RXR_X 에 크게 의존하는 애플리케이션에 유리합니다, 해밀턴 시뮬레이션, 양자 근사 최적화 알고리즘(QAOA), 양자 커널 방법과 같은 애플리케이션에 특히 유용합니다. 이 튜토리얼에서는 실제 사례로 퀀텀 커널에 초점을 맞춥니다.

제한사항

프랙셔널 게이트는 현재 실험적인 기능이며 몇 가지 제약이 따릅니다:

프랙셔널 게이트는 표준 접근 방식과 다른 워크플로우가 필요합니다. 이 튜토리얼에서는 실제 애플리케이션을 통해 프랙셔널 게이트로 작업하는 방법을 설명합니다.

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

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

프랙셔널 게이트를 사용하는 워크플로는 일반적으로 Qiskit 패턴 워크플로를 따릅니다. 핵심적인 차이점은 모든 RZZ 각도가 0<θπ/20 < \theta \leq \pi/2 라는 제약 조건을 충족해야 한다는 점입니다. 이 조건이 충족되도록 보장하는 방법에는 두 가지가 있으며, 이에 대해서는 아래에서 설명하겠습니다. 저희는 두 번째 접근 방식을 권장하며, 이 튜토리얼에서는 양자 커널 기법에서 영감을 얻은 예시를 통해 이를 설명합니다. 양자 커널이 어떤 분야에서 유용하게 활용될 수 있는지 더 잘 이해하기 위해서는 Liu, Arunachalam 및 Temme (2021) 의 논문을 읽어보시기를 권장합니다.

또한 IBM Quantum® Learning 의 ‘양자 머신러닝’ 과정에 포함된 ‘양자 커널 실습 튜토리얼’과 ‘양자 커널 강의’를 통해 학습할 수도 있습니다.

1. RZZ 각도 제약 조건을 만족하는 매개변수 값 생성

모든 RZZ 각도가 유효한 범위 내에 있다고 확신하는 경우 표준 키스킷 패턴 워크플로우를 따를 수 있습니다. 이 경우 매개변수 값을 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. 트랜스파일레이션 전에 회로에 매개변수 값을 할당하십시오

FoldRzzAngleqiskit-ibm-runtime 패키지는 라는 이름의 특수한 트랜스파일러 패스를 제공합니다. 이 패스는 모든 RZZ 각도가 RZZ 각도 제약 조건을 충족하도록 양자 회로를 변환합니다. transpile또는 에 generate_preset_pass_manager 백엔드를 제공하면, Qiskit이 양자 회로에 를 자동으로 적용합니다 FoldRzzAngle . 이 접근 방식에서는 트랜스파일링 전에 양자 회로에 매개변수 값을 할당해야 합니다. 워크플로는 다음과 같이 진행됩니다.

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

이 워크플로는 양자 회로에 매개변수 값을 할당하고 매개변수가 바인딩된 회로를 로컬에 저장하는 과정을 포함하기 때문에, 첫 번째 접근 방식보다 더 높은 계산 비용이 소요된다는 점에 유의해야 합니다.

Caution

v0.47.0 에 qiskit-ibm-runtime 보고된 알려진 문제점을 유의하시기 바랍니다. 특정 상황에서 트랜스파일레이션 후에도 각도가 잘못된 RZZ 게이트가 회로에 남아 있을 수 있습니다.

이 이슈의 진행 상황을 확인하려면 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)
  • 키스킷 베이시스 생성자 (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 옵션을 설정하여 이를 지원하는 백엔드를 구할 수 있습니다. 백엔드에서 분수 게이트를 지원하는 경우 기본 게이트에 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")

Output:

Backend: ibm_marrakesh
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']

소규모 시뮬레이터 예시

이 섹션에서는 양자 커널 회로를 실제 예시로 삼아, 시뮬레이터에서 Qiskit 패턴 워크플로의 네 단계를 단계별로 살펴보겠습니다.

1단계: 고전적 입력을 양자 문제에 매핑하기

양자 커널 회로

이 섹션에서는 RZZ 게이트를 사용하는 양자 커널 회로를 살펴보고 분수 게이트의 워크플로우를 소개합니다.

먼저 커널 행렬의 개별 항목을 계산하기 위해 양자 회로를 구성합니다. 이는 ZZ 피처 맵 회로를 단일 겹침과 결합하여 수행됩니다. 커널 함수는 피처 매핑된 공간에서 벡터를 가져와 그 내부 곱을 커널 행렬의 항목으로 반환합니다: K(x,y)=Φ(x)Φ(y),K(x, y) = \langle \Phi(x) | \Phi(y) \rangle, 여기서 Φ(x)|\Phi(x)\rangle 은 피처 매핑된 양자 상태를 나타냅니다.

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

다음으로, 동일한 입력(예: K(x,x)=1K(x, x) = 1 )에 대한 커널 함수를 계산합니다. 노이즈가 많은 양자 컴퓨터에서는 노이즈로 인해 이 값이 1보다 작을 수 있습니다. 결과가 1에 가까울수록 실행 시 노이즈가 적음을 나타냅니다. 이 자습서에서는 이 값을 충실도라고 하며 다음과 같이 정의합니다 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큐비트의 시스템에 대해 양자 커널 회로와 해당 파라미터 값이 생성되고, 이후 충실도가 평가됩니다.

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쿼비트 회로는 아래와 같이 시각화되어 있습니다.

circuits[0].draw("mpl", fold=-1)

Output:

Output of the previous code cell

표준 키스킷 패턴 워크플로우에서 매개변수 값은 일반적으로 PUB 의 일부로 샘플러 또는 추정기 프리미티브에 전달됩니다. 그러나 프랙셔널 게이트를 지원하는 백엔드를 사용하는 경우, 트랜스필레이션 전에 이러한 파라미터 값을 양자 회로에 명시적으로 할당해야 합니다.

b_qc = [
    circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)

Output:

Output of the previous code cell

2단계: 양자 하드웨어 실행을 위한 문제 최적화

그런 다음 표준 키스킷 패턴에 따라 패스 관리자를 사용하여 회로를 트랜스파일링합니다. 분수 게이트를 지원하는 백엔드를 generate_preset_pass_manager 에 제공하면 FoldRzzAngle 이라는 특수 패스가 자동으로 포함됩니다. 이 패스는 RZZ 각도 제약 조건을 준수하도록 회로를 수정합니다. 결과적으로 이전 그림에서 음수 값을 가진 RZZ 게이트가 양수 값으로 변환되고 일부 X 게이트가 추가됩니다.

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)

Output:

OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})
Output of the previous code cell

부분 게이트의 영향을 평가하기 위해 비로컬 게이트(이 백엔드의 경우 CZ 및 RZZ)의 수를 평가합니다, 회로 깊이 및 지속 시간과 함께 평가하고 이러한 메트릭을 나중에 표준 워크플로우의 메트릭과 비교합니다.

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
]

3단계: Qiskit primitives 명령어로 실행합니다

프랙셔널 게이트를 지원하는 백엔드로 트랜스파일된 회로를 실행합니다.

sampler_f = AerSampler.from_backend(backend_f)
job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())

Output:

085ce928-767e-4200-93bf-3905e5411cfe

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)

Output:

[0.929, 0.882, 0.8645, 0.817]

분수 게이트가 없는 워크플로우 및 회로 비교

이 섹션에서는 소수 게이트를 지원하지 않는 백엔드를 사용하는 표준 Qiskit 패턴 워크플로를 소개합니다. 트랜스파일된 회로들을 비교해 보면, (이전 절에서 다룬) 분수 게이트를 사용한 버전이 분수 게이트를 사용하지 않은 버전보다 더 간결하다는 것을 알 수 있습니다.

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

Output:

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

Output:

f2cca29d-7263-4976-9e51-13a91b75c3ae
# step 4: post-processing
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)

Output:

[0.8625, 0.7605, 0.702, 0.671]

깊이, 지속 시간 및 충실도의 비교

이 섹션에서는 비로컬 게이트의 수와 프랙셔널 게이트가 있는 회로와 없는 회로 간의 충실도를 비교합니다. 이는 실행 효율성과 품질 측면에서 프랙셔널 게이트 사용의 잠재적 이점을 강조합니다.

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

Output:

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

Output:

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

Output:

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

Output:

<matplotlib.legend.Legend at 0x120b792b0>
Output of the previous code cell

대규모 하드웨어 예시

이 섹션에서는 최대 40 큐비트를 지원하는 양자 하드웨어에서, 분수 게이트를 적용한 경우와 적용하지 않은 경우의 양자 커널 워크플로우에 대한 벤치마크를 수행합니다.

1~4단계 통합

이 워크플로는 소규모 예제와 동일한 구조를 따릅니다. 우리는 분수 게이트가 포함된 회로와 포함되지 않은 회로를 모두 트랜스파일하고, 메트릭을 수집한 다음, 해당 회로들을 실제 양자 하드웨어에 제출합니다.

# -------------------------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

Output:

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)

Output:

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

Output:

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

Output:

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

Output:

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

Output:

<matplotlib.legend.Legend at 0x11fcf6e40>
Output of the previous code cell

프랙셔널 게이트가 있는 경우와 없는 경우의 QPU 사용 시간을 비교합니다. 다음 셀의 결과를 보면 QPU 사용 시간이 거의 동일하다는 것을 알 수 있습니다.

print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")

Output:

no fractional gates: 8 seconds
fractional gates: 8 seconds

고급 주제: 분수 RX 게이트만 사용하기

프랙셔널 게이트를 사용할 때 워크플로우를 수정해야 하는 이유는 주로 RZZ 게이트 각도에 대한 제한에서 비롯됩니다. 그러나 부분 RX 게이트만 사용하고 부분 RZZ 게이트를 제외하면 표준 키스킷 패턴 워크플로우를 계속 따를 수 있습니다. 이 접근 방식은 전체 게이트 수를 줄이고 잠재적으로 성능을 개선함으로써 특히 많은 수의 RX 게이트와 U 게이트를 포함하는 회로에서 여전히 의미 있는 이점을 제공할 수 있습니다. 이 섹션에서는 RZZ 게이트를 생략하고 부분 RX 게이트만 사용하여 회로를 최적화하는 방법을 설명합니다.

이를 지원하기 위해 Target 개체에서 특정 기준 게이트를 비활성화할 수 있는 유틸리티 함수를 제공합니다. 여기서는 RZZ 게이트를 비활성화하는 데 사용합니다.

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 게이트로 구성된 회로를 예로 들어보겠습니다.

qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")

Output:

Output of the previous code cell

먼저 프랙셔널 게이트를 지원하지 않는 백엔드에 대한 회로를 트랜스파일링합니다.

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

Output:

OrderedDict({'rz': 23, 'sx': 16, 'cz': 4})
Output of the previous code cell

그런 다음 RZZ 게이트를 제외한 부분 RX 게이트를 사용하여 동일한 회로를 트랜스파일링합니다. 이렇게 하면 RX 게이트의 보다 효율적인 구현 덕분에 총 게이트 수가 약간 감소합니다.

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

Output:

OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})
Output of the previous code cell

분수 RX 게이트를 사용한 U 게이트 최적화

이 섹션에서는 이전 섹션에서 소개한 것과 동일한 회로를 기반으로 프랙셔널 RX 게이트를 사용하여 U 게이트를 최적화하는 방법을 보여줍니다.

RZZ 게이트를 제외한 부분 RX 게이트만 사용하여 회로를 트랜스파일링합니다. 다음과 같이 사용자 정의 분해 규칙을 도입하면, 을 도입하면 U 게이트를 구현하는 데 필요한 단일 큐비트 게이트의 수를 줄일 수 있습니다.

이 기능은 현재 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 트랜스파일러를 적용합니다. 그 결과, 이전 트랜스파일레이션에 비해 게이트의 총 수가 줄어들었습니다.

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

Output:

OrderedDict({'rz': 16, 'rx': 9, 'cz': 4})
Output of the previous code cell

다음 단계

권장사항

이 글이 흥미로웠다면, 다음 자료도 참고해 보시기 바랍니다:

이 페이지가 도움이 되었습니까?
GitHub에서 버그, 오타를 보고하거나 컨텐츠를 요청하십시오.