Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Квантові ядра з дробовими гейтами

Оцінка використання: менше 30 секунд на процесорі Heron r2 (ПРИМІТКА: Це лише оцінка. Твій час виконання може відрізнятися.)

Результати навчання​

  • Що таке дробові гейти і як вони зменшують глибину та тривалість схеми на IBM® QPU

  • Обмеження, пов'язані з використанням дробових гейтів (зокрема, діапазон кута RZZ)

  • Як побудувати робочий процес квантового ядра, що використовує дробові гейти з IBM Quantum Compute Service

  • Як порівнювати метрики виконання на апаратному забезпеченні (глибина, тривалість, кількість нелокальних гейтів, точність) з дробовими гейтами та без них

  • Як використовувати лише дробові гейти RX, зберігаючи стандартний робочий процес шаблонів Qiskit

Передумови​

Загальна інформація​

Дробові гейти на IBM QPU​

Дробові гейти - це параметризовані квантові гейти, які дозволяють пряме виконання обертань під довільними кутами (в межах певних обмежень), усуваючи необхідність розкладати їх на кілька базисних гейтів. Використовуючи природні взаємодії між фізичними кубітами, ти можеш реалізовувати певні унітарні оператори більш ефективно на обладнанні.

IBM Quantum® Heron 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.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_basis_constructor": "qiskit-basis-constructor", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")

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

Якщо ти спробуєш надіслати PUB, який включає гейт RZZ з кутом за межами допустимого діапазону, ти отримаєш повідомлення про помилку, таке як:

'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. Призначення значень параметрів схемам перед транспіляцією​

Пакет qiskit-ibm-runtime надає спеціалізований прохід транспілятора під назвою FoldRzzAngle. Цей прохід трансформує квантові схеми так, щоб всі кути RZZ відповідали обмеженню кута RZZ. Якщо ти надаєш backend до generate_preset_pass_manager або transpile, 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)])

Зауваж, що цей робочий процес спричиняє вищу обчислювальну вартість порівняно з першим підходом, оскільки він передбачає присвоєння значень параметрів квантовим схемам та локальне зберігання схем зі зв'язаними параметрами.

обережно

Зверни увагу на відому проблему в qiskit-ibm-runtime v0.47.0, коли гейти 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)
  • 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

Увімкнення дробових гейтів та перевірка базисних гейтів​

Щоб використовувати дробові гейти, ти можеш отримати backend, який їх підтримує, встановивши опцію use_fractional_gates=True. Якщо 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']

Невеликий приклад на симуляторі​

У цьому розділі ми пройдемо чотири кроки робочого процесу шаблонів Qiskit на симуляторі, використовуючи схему квантового ядра як практичний приклад.

Крок 1: Відображення класичних входів у квантову задачу​

Схема квантового ядра​

У цьому розділі ми досліджуємо схему квантового ядра з використанням гейтів RZZ, щоб представити робочий процес для дробових гейтів.

Ми починаємо з побудови квантової схеми для обчислення окремих елементів матриці ядра. Це виконується шляхом поєднання схем карти ознак ZZ з унітарним перекриттям. Функція ядра приймає вектори в просторі, відображеному на ознаки, і повертає їх внутрішній добуток як елемент матриці ядра: K(x,y)=⟨Φ(x)∣Φ(y)⟩,K(x, y) = \langle \Phi(x) | \Phi(y) \rangle, де ∣Φ(x)⟩|\Phi(x)\rangle представляє квантовий стан, відображений на ознаки.

Ми вручну конструюємо схему карти ознак ZZ з використанням гейтів RZZ. Хоча Qiskit надає вбудований zz_feature_map, він наразі не підтримує гейти RZZ станом на Qiskit v2.4.1 (див. проблему).

Далі ми обчислюємо функцію ядра для ідентичних входів - наприклад, 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]

Схема з чотирма кубітами візуалізована нижче.

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

Output of the previous code cell

У стандартному робочому процесі шаблонів Qiskit значення параметрів зазвичай передаються примітиву Sampler або Estimator як частина PUB. Однак, при використанні backend, який підтримує дробові гейти, ці значення параметрів повинні бути явно призначені квантовій схемі перед транспіляцією.

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

Крок 2: Оптимізація задачі для виконання на квантовому обладнанні​

Потім ми транспілюємо схему, використовуючи менеджер проходів, слідуючи стандартному шаблону Qiskit. Надаючи backend, який підтримує дробові гейти, до 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)
OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})

Output of the previous code cell

Щоб оцінити вплив дробових гейтів, ми оцінюємо кількість нелокальних гейтів (CZ та RZZ для цього backend), разом із глибиною схем та тривалістю, і порівнюємо ці метрики з метриками зі стандартного робочого процесу пізніше.

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​

Ми запускаємо транспільовану схему з backend, який підтримує дробові гейти.

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

Крок 4: Постобробка та повернення результату в бажаному класичному форматі​

Ти можеш отримати значення функції ядра K(x,x)K(x, x) шляхом вимірювання ймовірності бітового рядка з усіма нулями 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]

Порівняння робочого процесу та схеми без дробових гейтів​

У цьому розділі ми представляємо стандартний робочий процес шаблонів Qiskit з використанням backend, який не підтримує дробові гейти. Порівнюючи транспільовані схеми, ти помітиш, що версія з використанням дробових гейтів (з попереднього розділу) є більш компактною, ніж версія без дробових гейтів.

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

Порівняння глибини, тривалості та точності​

У цьому розділі ми порівнюємо кількість нелокальних гейтів та точність між схемами з дробовими гейтами та без них. Це підкреслює потенційні переваги використання дробових гейтів з точки зору ефективності виконання та якості.

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.

Кроки 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
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

Ми порівнюємо час використання 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​

Необхідність модифікованого робочого процесу при використанні дробових гейтів в основному випливає з обмеження на кути гейтів RZZ. Однак, якщо ти використовуєш лише дробові гейти RX та виключаєш дробові гейти RZZ, ти можеш продовжувати дотримуватися стандартного робочого процесу шаблонів Qiskit. Цей підхід все ще може запропонувати значні переваги, особливо в схемах, які містять велику кількість гейтів RX та гейтів U, зменшуючи загальну кількість гейтів і потенційно покращуючи продуктивність. У цьому розділі ми демонструємо, як оптимізувати твої схеми, використовуючи лише дробові гейти RX, виключаючи гейти RZZ.

Для підтримки цього ми надаємо допоміжну функцію, яка дозволяє тобі вимкнути конкретний базисний гейт в об'єкті 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 of the previous code cell

Спочатку ми транспілюємо схему для backend, який не підтримує дробові гейти.

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

Потім ми транспілюємо ту саму схему, використовуючи дробові гейти RX, виключаючи гейти RZZ. Це призводить до незначного зменшення загальної кількості гейтів завдяки більш ефективній реалізації гейтів 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")
OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})

Output of the previous code cell

Оптимізація U воріт з дробовими RX воротами​

У цьому розділі ми демонструємо, як оптимізувати гейти U за допомогою дробових гейтів RX, спираючись на ту саму схему, представлену в попередньому розділі. Ми транспілюємо схему, використовуючи лише дробові гейти RX, виключаючи гейти RZZ. Вводячи користувацьке правило декомпозиції, як показано нижче, ми можемо зменшити кількість однокубітних гейтів, необхідних для реалізації гейта U.

Ця функція наразі обговорюється в цій GitHub issue.

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

Далі ми застосовуємо транспілятор, використовуючи трансляцію constructor-beta, надану пакетом qiskit-basis-constructor. В результаті загальна кількість воріт зменшується порівняно з попередньою транспіляцією.

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

Наступні кроки​

Рекомендації

Якщо ця робота зацікавила тебе, тобі можуть бути цікаві наступні матеріали: