Вступ до дробових воріт
Оцінка використання: менше 30 секунд на процесорі Heron r2 (ПРИМІТКА: Це лише оцінка. Твій час виконання може відрізнятися.)
Загальна інформація
Дробові ворота на IBM QPU
Дробові ворота - це параметризовані квантові ворота, які дозволяють пряме виконання обертань під довільними кутами (в межах певних обмежень), усуваючи необхідність розкладати їх на кілька базисних воріт. Використовуючи природні взаємодії між фізичними кубітами, користувачі можуть реалізовувати певні унітарні оператори більш ефективно на обладнанні.
IBM Quantum® Heron QPU підтримують наступні дробові ворота:
- для
- для будь-якого дійсного значення
Ці ворота можуть значно зменшити як глибину, так і тривалість квантових схем. Вони особливо вигідні в застосуваннях, які значною мірою покладаються на та , таких як гамільтонова симуляція, Алгоритм квантової приблизної оптимізації (QAOA) та методи квантових ядер. У цьому підручнику ми зосереджуємося на квантовому ядрі як практичному прикладі.
Обмеження
Дробові ворота наразі є експериментальною функцією і мають кілька обмежень:
- обмежений кутами в діапазоні .
- Використання дробових воріт не підтримується для динамічних схем, скручування Паулі, імовірнісного скасування помилок (PEC) та екстраполяції з нульовим шумом (ZNE) (з використанням імовірнісного підсилення помилок (PEA)).
Дробові ворота вимагають іншого робочого процесу порівняно зі стандартним підходом. Цей підручник пояснює, як працювати з дробовими воротами через практичне застосування.
Дивись наступні джерела для отримання більш детальної інформації про дробові ворота.
Огляд
Робочий процес для використання дробових воріт загалом слідує робочому процесу шаблонів Qiskit. Ключова відмінність полягає в тому, що всі кути RZZ повинні задовольняти обмеження . Існує два підходи для забезпечення виконання цієї умови. Цей підручник зосереджується на другому підході та рекомендує його.
# 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)])
Якщо ти спробуєш надіслати 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, де трансформація воріт RZZ може не вдатися в певних сценаріях. Для обхідного шляху, будь ласка, звернись до розділу Усунення несправностей. Цей підручник демонструє, як використовувати дробові ворота через другий підхід на прикладі, натхненному методом квантових ядер. Щоб краще зрозуміти, де квантові ядра, ймовірно, будуть корисними, ми рекомендуємо прочитати 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
Увімкнення дробових воріт та перевірка базисних воріт
Щоб використовувати дробові ворота, ти можеш отримати 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_fez
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']
Робочий процес з дробовими воротами
Крок 1: Відображення класичних входів у квантову задачу
Схема квантового ядра
У цьому розділі ми досліджуємо схему квантового ядра з використанням воріт RZZ, щоб представити робочий процес для дробових воріт.
Ми починаємо з побудови квантової схеми для обчислення окремих елементів матриці ядра. Це виконується шляхом поєднання схем карти ознак ZZ з унітарним перекриттям. Функція ядра приймає вектори в просторі, відображеному на ознаки, і повертає їх внутрішній добуток як елемент матриці ядра: де представляє квантовий стан, відображений на ознаки.
Ми вручну конструюємо схему карти ознак ZZ з використанням воріт RZZ.
Хоча Qiskit надає вбудований zz_feature_map, він наразі не підтримує ворота RZZ станом на Qiskit v2.0.2 (див. проблему).
Далі ми обчислюємо функцію ядра для ідентичних входів - наприклад, . На зашумлених квантових комп'ютерах це значення може бути меншим за 1 через шум. Результат, ближчий до 1, вказує на менший шум під час виконання. У цьому підручнику ми називаємо це значення точністю, визначеною як
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, 44, 4))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
Схема з чотирма кубітами візуалізована нижче.
circuits[0].draw("mpl", fold=-1)

У стандартному робочому процесі шаблонів 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)

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

Щоб оцінити вплив дробових воріт, ми оцінюємо кількість нелокальних воріт (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 = 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
Крок 4: Постобробка та повернення результату в бажаному класичному форматі
Ти можеш отримати значення функції ядра шляхом вимірювання ймовірності бітового рядка з усіма нулями 00...00 у виході.
# 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]
Порівняння робочого процесу та схеми без дробових воріт
У цьому розділі ми представляємо стандартний робочий процес шаблонів 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)])

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]
Порівняння глибини та точності
У цьому розділі ми порівнюємо кількість нелокальних воріт та точність між схемами з дробовими воротами та без них. Це підкреслює потенційні переваги використання дробових воріт з точки зору ефективності виконання та якості.
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>
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>
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>
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>
Ми порівнюємо час використання 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 воріт
Необхідність модифікованого робочого процесу при використанні дробових воріт в основному випливає з обмеження на кути RZZ воріт. Однак, якщо ти використовуєш лише дробові RX ворота та виключаєш дробові RZZ ворота, ти можеш продовжувати дотримуватися стандартного робочого процесу Qiskit patterns. Цей підхід все ще може запропонувати значні переваги, особливо в схемах, які містять велику кількість RX воріт та U воріт, зменшуючи загальну кількість воріт і потенційно покращуючи продуктивність. У цьому розділі ми демонструємо, як оптимізувати твої схеми, використовуючи лише дробові RX ворота, виключаючи RZZ ворота.
Для підтримки цього ми надаємо допоміжну функцію, яка дозволяє тобі вимкнути конкретні базові ворота в об'єкті Target. Тут ми використовуємо її для вимкнення RZZ воріт.
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 воріт, як приклад.
qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")
Спочатку ми транспілюємо схему для 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)])

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

Оптимізація U воріт з дробовими RX воротами
У цьому розділі ми демонструємо, як оптимізувати U ворота, використовуючи дробові RX ворота, базуючись на тій самій схемі, представленій у попередньому розділі.
Тобі потрібно буде встановити пакет qiskit-basis-constructor для цього розділу.
Це бета-версія нового плагіна транспіляції для Qiskit, який може бути інтегрований в Qiskit у майбутньому.
# %pip install qiskit-basis-constructor
from qiskit.circuit.library import UGate
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY
Ми транспілюємо схему, використовуючи лише дробові 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)])
Усунення несправностей
Проблема: Недійсні кути RZZ можуть залишитися після транспіляції
Станом на Qiskit v2.0.3 існують відомі проблеми, коли RZZ ворота з недійсними кутами можуть залишатися в схемах навіть після транспіляції. Проблема зазвичай виникає за наступних умов.
Помилка при використанні опції target з generate_preset_pass_manager або transpiler
Коли опція target використовується з generate_preset_pass_manager або transpiler, спеціалізований прохід транспілятора FoldRzzAngle не викликається.
Для забезпечення правильної обробки кутів RZZ для дробових воріт, ми рекомендуємо завжди використовувати опцію backend замість цього.
Дивись цю issue для більш детальної інформації.
Помилка, коли схеми містять певні ворота
Якщо твоя схема включає певні ворота, такі як XXPlusYYGate, транспілятор Qiskit може генерувати RZZ ворота з недійсними кутами.
Якщо ти зіткнувся з цією проблемою, дивись цю GitHub issue для обхідного рішення.
Опитування щодо навчального посібника
Будь ласка, пройди це коротке опитування, щоб надати відгук про цей навчальний посібник. Твої думки допоможуть нам покращити наші пропозиції контенту та користувацький досвід.
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.