Перейти до основного вмісту

Методи компіляції для схем симуляції гамільтоніана

Оцінка використання QPU: у цьому підручнику не виконувалося жодного виконання, оскільки воно зосереджене на процесі транспіляції.

Передумови

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

Мета цього підручника — навчити користувачів застосовувати та оцінювати три методи компіляції в Qiskit: транспілер SABRE, транспілер з підтримкою AI та плагін Rustiq. Користувачі навчаться ефективно використовувати кожен метод і як оцінювати їхню продуктивність на різних квантових схемах. Наприкінці цього підручника користувачі зможуть обирати та адаптувати стратегії компіляції на основі конкретних цілей оптимізації, таких як зменшення глибини схеми, мінімізація кількості гейтів або покращення часу виконання.

Що ти дізнаєшся

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

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

Огляд методів компіляції

1. Транспілер Qiskit з SABRE

Транспілер Qiskit використовує алгоритм SABRE (SWAP-based BidiREctional heuristic search) для оптимізації компонування та маршрутизації схем. SABRE зосереджується на мінімізації гейтів SWAP та їхнього впливу на глибину схеми, дотримуючись обмежень зв'язності апаратного забезпечення. Цей метод є дуже універсальним і підходить для оптимізації схем загального призначення, забезпечуючи баланс між продуктивністю та часом обчислень. Щоб скористатися перевагами останніх покращень у SABRE, детально описаних у [1], ти можеш збільшити кількість спроб (наприклад, layout_trials=400, swap_trials=400). Для цілей цього підручника ми будемо використовувати стандартні значення для кількості спроб, щоб порівняти зі стандартним транспілером Qiskit. Переваги та дослідження параметрів SABRE розглядаються в окремому детальному підручнику.

2. Транспілер AI

Транспілер з підтримкою AI в Qiskit використовує машинне навчання для прогнозування оптимальних стратегій транспіляції шляхом аналізу шаблонів у структурі схеми та обмежень апаратного забезпечення для вибору найкращої послідовності оптимізацій для заданого входу. Цей метод особливо ефективний для великомасштабних квантових схем, пропонуючи високий ступінь автоматизації та адаптивності до різних типів задач. На додаток до загальної оптимізації схем, транспілер AI може використовуватися з пасом AIPauliNetworkSynthesis, який націлений на схеми мережі Паулі — блоки, що складаються з гейтів H, S, SX, CX, RX, RY та RZ — і застосовує підхід синтезу на основі навчання з підкріпленням. Для отримання додаткової інформації про транспілер AI та його стратегії синтезу дивіться [2] та [3].

3. Плагін Rustiq

Плагін Rustiq впроваджує розширені техніки синтезу спеціально для операцій PauliEvolutionGate, які представляють обертання Паулі, що зазвичай використовуються в троттеризованій динаміці. Цей плагін є цінним для схем, які реалізують симуляцію гамільтоніана, таких як ті, що використовуються у задачах квантової хімії та фізики, де точні обертання Паулі є необхідними для ефективної симуляції задачних гамільтоніанів. Rustiq пропонує точний синтез схем малої глибини для цих спеціалізованих операцій. Для отримання додаткової інформації про реалізацію та продуктивність Rustiq, будь ласка, зверніться до [4].

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

Вимоги

Перед початком цього підручника переконайся, що у тебе встановлено наступне:

  • Qiskit SDK v1.3 або новіше, з підтримкою візуалізації
  • Qiskit Runtime v0.28 або новіше (pip install qiskit-ibm-runtime)
  • Qiskit IBM Transpiler (pip install qiskit-ibm-transpiler)
  • Qiskit AI Transpiler local mode (pip install qiskit_ibm_ai_local_transpiler)
  • Бібліотека графів Networkx (pip install networkx)

Налаштування

# Added by doQumentation — required packages for this notebook
!pip install -q IPython matplotlib numpy pandas qiskit qiskit-ibm-runtime qiskit-ibm-transpiler requests
from qiskit.circuit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.circuit.library import (
efficient_su2,
PauliEvolutionGate,
)
from qiskit_ibm_transpiler import generate_ai_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes.synthesis.high_level_synthesis import HLSConfig
from collections import Counter
from IPython.display import display
import time
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import json
import requests
import logging

# Suppress noisy loggers
logging.getLogger(
"qiskit_ibm_transpiler.wrappers.ai_local_synthesis"
).setLevel(logging.ERROR)

seed = 42 # Seed for reproducibility

Частина 1: Схема Efficient SU2

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

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

Ми почнемо зі створення однієї схеми efficient_su2, щоб продемонструвати, як порівнювати різні методи компіляції. Після Частини 1 ми розширимо наш аналіз на більший набір схем, дозволяючи комплексний бенчмарк для оцінки продуктивності різних методів компіляції.

qubit_size = list(range(10, 101, 10))
qc_su2_list = [
efficient_su2(n, entanglement="circular", reps=1)
.decompose()
.copy(name=f"SU2_{n}")
for n in qubit_size
]

# Draw the first circuit
qc_su2_list[0].draw(output="mpl")

Output of the previous code cell

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

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

  • Транспілер SABRE: Використовує стандартний транспілер Qiskit з алгоритмом компонування та маршрутизації SABRE.
  • Транспілер AI (локальний режим): Стандартний транспілер з підтримкою AI, який використовує локальний висновок та стандартну стратегію синтезу.
  • Плагін Rustiq: Плагін транспілера, розроблений для компіляції малої глибини, адаптований для завдань симуляції гамільтоніана.

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

Примітка: Для початкового прикладу схеми SU2 ми будемо порівнювати лише транспілер SABRE зі стандартним транспілером AI. Однак у наступному бенчмарку з використанням схем Hamlib ми порівняємо всі три методи транспіляції.

# QiskitRuntimeService.save_account(channel="ibm_quantum_platform", token="<YOUR-API-KEY>", overwrite=True, set_as_default=True)
service = QiskitRuntimeService(channel="ibm_quantum_platform")
backend = service.backend("ibm_torino")
print(f"Using backend: {backend}")
qiskit_runtime_service._get_crn_from_instance_name:WARNING:2025-07-30 21:46:30,843: Multiple instances found. Using all matching instances.
Using backend: <IBMBackend('ibm_torino')>

Транспілер Qiskit з SABRE:

pm_sabre = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=seed
)

Транспілер AI:

# Standard AI transpiler pass manager, using the local mode
pm_ai = generate_ai_pass_manager(
backend=backend, optimization_level=3, ai_optimization_level=3
)

Плагін Rustiq:

hls_config = HLSConfig(
PauliEvolution=[
(
"rustiq",
{
"nshuffles": 400,
"upto_phase": True,
"fix_clifford": True,
"preserve_order": False,
"metric": "depth",
},
)
]
)
pm_rustiq = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
hls_config=hls_config,
seed_transpiler=seed,
)

Транспілювати та зібрати метрики

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

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

Ми будемо використовувати цю функцію для аналізу продуктивності різних методів компіляції на декількох схемах.

def capture_transpilation_metrics(
results, pass_manager, circuits, method_name
):
"""
Capture transpilation metrics for a list of circuits and stores the results in a DataFrame.

Args:
results (pd.DataFrame): DataFrame to store the results.
pass_manager: Pass manager used for transpilation.
circuits (list): List of quantum circuits to transpile.
method_name (str): Name of the transpilation method.

Returns:
list: List of transpiled circuits.
"""
transpiled_circuits = []

for i, qc in enumerate(circuits):
# Transpile the circuit
start_time = time.time()
transpiled_qc = pass_manager.run(qc)
end_time = time.time()

# Needed for AI transpiler to be consistent with other methods
transpiled_qc = transpiled_qc.decompose(gates_to_decompose=["swap"])

# Collect metrics
transpilation_time = end_time - start_time
circuit_depth = transpiled_qc.depth(
lambda x: x.operation.num_qubits == 2
)
circuit_size = transpiled_qc.size()

# Append results to DataFrame
results.loc[len(results)] = {
"method": method_name,
"qc_name": qc.name,
"qc_index": i,
"num_qubits": qc.num_qubits,
"ops": transpiled_qc.count_ops(),
"depth": circuit_depth,
"size": circuit_size,
"runtime": transpilation_time,
}
transpiled_circuits.append(transpiled_qc)
print(
f"Transpiled circuit index {i} ({qc.name}) in {transpilation_time:.2f} seconds with method {method_name}, "
f"depth {circuit_depth}, and size {circuit_size}."
)

return transpiled_circuits
results_su2 = pd.DataFrame(
columns=[
"method",
"qc_name",
"qc_index",
"num_qubits",
"ops",
"depth",
"size",
"runtime",
]
)

tqc_sabre = capture_transpilation_metrics(
results_su2, pm_sabre, qc_su2_list, "sabre"
)
tqc_ai = capture_transpilation_metrics(results_su2, pm_ai, qc_su2_list, "ai")
Transpiled circuit index 0 (SU2_10) in 0.06 seconds with method sabre, depth 13, and size 167.
Transpiled circuit index 1 (SU2_20) in 0.24 seconds with method sabre, depth 20, and size 299.
Transpiled circuit index 2 (SU2_30) in 10.72 seconds with method sabre, depth 72, and size 627.
Transpiled circuit index 3 (SU2_40) in 16.16 seconds with method sabre, depth 40, and size 599.
Transpiled circuit index 4 (SU2_50) in 76.89 seconds with method sabre, depth 77, and size 855.
Transpiled circuit index 5 (SU2_60) in 86.12 seconds with method sabre, depth 60, and size 899.
Transpiled circuit index 6 (SU2_70) in 94.46 seconds with method sabre, depth 79, and size 1085.
Transpiled circuit index 7 (SU2_80) in 69.05 seconds with method sabre, depth 80, and size 1199.
Transpiled circuit index 8 (SU2_90) in 88.25 seconds with method sabre, depth 105, and size 1420.
Transpiled circuit index 9 (SU2_100) in 83.80 seconds with method sabre, depth 100, and size 1499.
Transpiled circuit index 0 (SU2_10) in 0.17 seconds with method ai, depth 10, and size 168.
Transpiled circuit index 1 (SU2_20) in 0.29 seconds with method ai, depth 20, and size 299.
Transpiled circuit index 2 (SU2_30) in 13.56 seconds with method ai, depth 36, and size 548.
Transpiled circuit index 3 (SU2_40) in 15.95 seconds with method ai, depth 40, and size 599.
Transpiled circuit index 4 (SU2_50) in 80.70 seconds with method ai, depth 54, and size 823.
Transpiled circuit index 5 (SU2_60) in 75.99 seconds with method ai, depth 60, and size 899.
Transpiled circuit index 6 (SU2_70) in 64.96 seconds with method ai, depth 74, and size 1087.
Transpiled circuit index 7 (SU2_80) in 68.25 seconds with method ai, depth 80, and size 1199.
Transpiled circuit index 8 (SU2_90) in 75.07 seconds with method ai, depth 90, and size 1404.
Transpiled circuit index 9 (SU2_100) in 63.97 seconds with method ai, depth 100, and size 1499.

Відобразити транспільовані результати однієї зі схем.

print("Sabre transpilation")
display(tqc_sabre[0].draw("mpl", fold=-1, idle_wires=False))
print("AI transpilation")
display(tqc_ai[0].draw("mpl", fold=-1, idle_wires=False))
Sabre transpilation

Output of the previous code cell

AI transpilation

Output of the previous code cell

Таблиця результатів:

summary_su2 = (
results_su2.groupby("method")[["depth", "size", "runtime"]]
.mean()
.round(2)
)
print(summary_su2)

results_su2
depth   size  runtime
method
ai 56.4 852.5 45.89
sabre 64.6 864.9 52.57
method  qc_name  qc_index  num_qubits                                ops  \
0 sabre SU2_10 0 10 {'rz': 81, 'sx': 70, 'cz': 16}
1 sabre SU2_20 1 20 {'rz': 160, 'sx': 119, 'cz': 20}
2 sabre SU2_30 2 30 {'sx': 295, 'rz': 242, 'cz': 90}
3 sabre SU2_40 3 40 {'rz': 320, 'sx': 239, 'cz': 40}
4 sabre SU2_50 4 50 {'rz': 402, 'sx': 367, 'cz': 86}
5 sabre SU2_60 5 60 {'rz': 480, 'sx': 359, 'cz': 60}
6 sabre SU2_70 6 70 {'rz': 562, 'sx': 441, 'cz': 82}
7 sabre SU2_80 7 80 {'rz': 640, 'sx': 479, 'cz': 80}
8 sabre SU2_90 8 90 {'rz': 721, 'sx': 585, 'cz': 114}
9 sabre SU2_100 9 100 {'rz': 800, 'sx': 599, 'cz': 100}
10 ai SU2_10 0 10 {'rz': 81, 'sx': 71, 'cz': 16}
11 ai SU2_20 1 20 {'rz': 160, 'sx': 119, 'cz': 20}
12 ai SU2_30 2 30 {'sx': 243, 'rz': 242, 'cz': 63}
13 ai SU2_40 3 40 {'rz': 320, 'sx': 239, 'cz': 40}
14 ai SU2_50 4 50 {'rz': 403, 'sx': 346, 'cz': 74}
15 ai SU2_60 5 60 {'rz': 480, 'sx': 359, 'cz': 60}
16 ai SU2_70 6 70 {'rz': 563, 'sx': 442, 'cz': 82}
17 ai SU2_80 7 80 {'rz': 640, 'sx': 479, 'cz': 80}
18 ai SU2_90 8 90 {'rz': 721, 'sx': 575, 'cz': 108}
19 ai SU2_100 9 100 {'rz': 800, 'sx': 599, 'cz': 100}

depth size runtime
0 13 167 0.058845
1 20 299 0.238217
2 72 627 10.723922
3 40 599 16.159262
4 77 855 76.886604
5 60 899 86.118255
6 79 1085 94.458287
7 80 1199 69.048184
8 105 1420 88.254809
9 100 1499 83.795482
10 10 168 0.171532
11 20 299 0.291691
12 36 548 13.555931
13 40 599 15.952733
14 54 823 80.702141
15 60 899 75.993404
16 74 1087 64.960162
17 80 1199 68.253280
18 90 1404 75.072412
19 100 1499 63.967446

Графіки результатів

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

def plot_transpilation_metrics(results, overall_title, x_axis="qc_index"):
"""
Plots transpilation metrics (depth, size, runtime) for different transpilation methods.

Parameters:
results (DataFrame): Data containing columns ['num_qubits', 'method', 'depth', 'size', 'runtime']
overall_title (str): The title of the overall figure.
x_axis (str): The x-axis label, either 'num_qubits' or 'qc_index'.
"""

fig, axs = plt.subplots(1, 3, figsize=(24, 6))
metrics = ["depth", "size", "runtime"]
titles = ["Circuit Depth", "Circuit Size", "Transpilation Runtime"]
y_labels = ["Depth", "Size (Gate Count)", "Runtime (s)"]

methods = results["method"].unique()
colors = plt.colormaps["tab10"]
markers = ["o", "^", "s", "D", "P", "*", "X", "v"]
color_list = [colors(i % colors.N) for i in range(len(methods))]
color_map = {method: color_list[i] for i, method in enumerate(methods)}
marker_map = {
method: markers[i % len(markers)] for i, method in enumerate(methods)
}
jitter_factor = 0.1 # Small x-axis jitter for visibility
handles, labels = [], [] # Unique handles for legend

# Plot each metric
for i, metric in enumerate(metrics):
for method in methods:
method_data = results[results["method"] == method]

# Introduce slight jitter to avoid exact overlap
jitter = np.random.uniform(
-jitter_factor, jitter_factor, len(method_data)
)

scatter = axs[i].scatter(
method_data[x_axis] + jitter,
method_data[metric],
color=color_map[method],
label=method,
marker=marker_map[method],
alpha=0.7,
edgecolors="black",
s=80,
)

if method not in labels:
handles.append(scatter)
labels.append(method)

axs[i].set_title(titles[i])
axs[i].set_xlabel(x_axis)
axs[i].set_ylabel(y_labels[i])
axs[i].grid(axis="y", linestyle="--", alpha=0.7)
axs[i].tick_params(axis="x", rotation=45)
axs[i].set_xticks(sorted(results[x_axis].unique()))

fig.suptitle(overall_title, fontsize=16)
fig.legend(
handles=handles,
labels=labels,
loc="upper right",
bbox_to_anchor=(1.05, 1),
)

plt.tight_layout()
plt.show()
plot_transpilation_metrics(
results_su2, "Transpilation Metrics for SU2 Circuits", x_axis="num_qubits"
)

Output of the previous code cell

Аналіз результатів компіляції схеми SU2

У цьому експерименті ми порівнюємо два методи транспіляції — транспілер SABRE від Qiskit та транспілер з підтримкою AI — на наборі схем efficient_su2. Оскільки ці схеми не включають жодних операцій PauliEvolutionGate, плагін Rustiq не включено до цього порівняння.

У середньому транспілер AI показує кращі результати з точки зору глибини схеми, з покращенням більше ніж на 10% у всьому діапазоні схем SU2. Для кількості гейтів (розміру схеми) та часу виконання транспіляції обидва методи дають схожі результати загалом.

Однак перевірка окремих точок даних розкриває глибше розуміння:

  • Для більшості розмірів квітів як SABRE, так і AI виробляють майже ідентичні результати, що свідчить про те, що в багатьох випадках обидва методи збігаються до схожих ефективних рішень.
  • Для певних розмірів схем, зокрема на 30, 50, 70 та 90 квітах, транспілер AI знаходить значно менші за глибиною схеми, ніж SABRE. Це вказує на те, що підхід AI на основі навчання здатний відкривати більш оптимальні компонування або шляхи маршрутизації у випадках, коли евристика SABRE цього не робить.

Ця поведінка підкреслює важливий висновок:

Хоча SABRE і AI часто виробляють порівнянні результати, транспілер AI іноді може виявляти значно кращі рішення, особливо з точки зору глибини, що може призвести до значно покращеної продуктивності на апаратному забезпеченні.

Частина 2: Ланцюг симуляції гамільтоніана

Крок 1: Дослідження ланцюгів з PauliEvolutionGate

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

Гамільтоніани, використані в тесті продуктивності

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

Для довідки користувачі можуть ознайомитися з ширшим набором гамільтоніанів у цій статті: Efficient Hamiltonian Simulation on Noisy Quantum Devices.

Джерело тесту продуктивності: Hamlib та Benchpress

Ланцюги, використані в цьому тесті продуктивності, взяті з репозиторію тестів продуктивності Hamlib, який містить реалістичні робочі навантаження для симуляції гамільтоніанів.

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

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

# Obtain the Hamiltonian JSON from the benchpress repository
url = "https://raw.githubusercontent.com/Qiskit/benchpress/e7b29ef7be4cc0d70237b8fdc03edbd698908eff/benchpress/hamiltonian/hamlib/100_representative.json"
response = requests.get(url)
response.raise_for_status() # Raise an error if download failed
ham_records = json.loads(response.text)
# Remove circuits that are too large for the backend
ham_records = [
h for h in ham_records if h["ham_qubits"] <= backend.num_qubits
]
# Remove the circuits that are large to save transpilation time
ham_records = sorted(ham_records, key=lambda x: x["ham_terms"])[:35]

qc_ham_list = []
for h in ham_records:
terms = h["ham_hamlib_hamiltonian_terms"]
coeff = h["ham_hamlib_hamiltonian_coefficients"]
num_qubits = h["ham_qubits"]
name = h["ham_problem"]

evo_gate = PauliEvolutionGate(SparsePauliOp(terms, coeff))

qc_ham = QuantumCircuit(num_qubits)
qc_ham.name = name

qc_ham.append(evo_gate, range(num_qubits))
qc_ham_list.append(qc_ham)
print(f"Number of Hamiltonian circuits: {len(qc_ham_list)}")

# Draw the first Hamiltonian circuit
qc_ham_list[0].draw("mpl", fold=-1)
Number of Hamiltonian circuits: 35

Output of the previous code cell

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

Як і в попередньому прикладі, ми будемо використовувати той самий бекенд для забезпечення узгодженості в наших порівняннях. Оскільки менеджери проходів (pm_sabre, pm_ai та pm_rustiq) вже були ініціалізовані, ми можемо безпосередньо продовжити транспіляцію ланцюгів гамільтоніанів за допомогою кожного методу.

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

results_ham = pd.DataFrame(
columns=[
"method",
"qc_name",
"qc_index",
"num_qubits",
"ops",
"depth",
"size",
"runtime",
]
)

tqc_sabre = capture_transpilation_metrics(
results_ham, pm_sabre, qc_ham_list, "sabre"
)
tqc_ai = capture_transpilation_metrics(results_ham, pm_ai, qc_ham_list, "ai")
tqc_rustiq = capture_transpilation_metrics(
results_ham, pm_rustiq, qc_ham_list, "rustiq"
)
Transpiled circuit index 0 (all-vib-o3) in 0.02 seconds with method sabre, depth 6, and size 58.
Transpiled circuit index 1 (all-vib-c2h) in 1.10 seconds with method sabre, depth 2, and size 39.
Transpiled circuit index 2 (all-vib-bh) in 0.01 seconds with method sabre, depth 3, and size 30.
Transpiled circuit index 3 (all-vib-c2h) in 0.03 seconds with method sabre, depth 18, and size 115.
Transpiled circuit index 4 (graph-gnp_k-2) in 0.02 seconds with method sabre, depth 24, and size 129.
Transpiled circuit index 5 (all-vib-fccf) in 0.05 seconds with method sabre, depth 14, and size 134.
Transpiled circuit index 6 (all-vib-hno) in 8.39 seconds with method sabre, depth 6, and size 174.
Transpiled circuit index 7 (all-vib-bhf2) in 3.92 seconds with method sabre, depth 22, and size 220.
Transpiled circuit index 8 (LiH) in 0.03 seconds with method sabre, depth 67, and size 290.
Transpiled circuit index 9 (uf20-ham) in 0.04 seconds with method sabre, depth 50, and size 340.
Transpiled circuit index 10 (all-vib-fccf) in 0.62 seconds with method sabre, depth 30, and size 286.
Transpiled circuit index 11 (all-vib-fccf) in 0.04 seconds with method sabre, depth 67, and size 339.
Transpiled circuit index 12 (all-vib-ch2) in 0.04 seconds with method sabre, depth 87, and size 421.
Transpiled circuit index 13 (tfim) in 0.05 seconds with method sabre, depth 36, and size 222.
Transpiled circuit index 14 (all-vib-cyclo_propene) in 9.51 seconds with method sabre, depth 22, and size 345.
Transpiled circuit index 15 (graph-gnp_k-4) in 0.05 seconds with method sabre, depth 128, and size 704.
Transpiled circuit index 16 (all-vib-hc3h2cn) in 13.83 seconds with method sabre, depth 2, and size 242.
Transpiled circuit index 17 (TSP_Ncity-4) in 0.05 seconds with method sabre, depth 106, and size 609.
Transpiled circuit index 18 (tfim) in 0.29 seconds with method sabre, depth 73, and size 399.
Transpiled circuit index 19 (all-vib-h2co) in 21.97 seconds with method sabre, depth 30, and size 572.
Transpiled circuit index 20 (Be2) in 0.09 seconds with method sabre, depth 324, and size 1555.
Transpiled circuit index 21 (graph-complete_bipart) in 0.12 seconds with method sabre, depth 250, and size 1394.
Transpiled circuit index 22 (all-vib-f2) in 0.07 seconds with method sabre, depth 215, and size 1027.
Transpiled circuit index 23 (all-vib-cyclo_propene) in 41.22 seconds with method sabre, depth 30, and size 1144.
Transpiled circuit index 24 (TSP_Ncity-5) in 1.89 seconds with method sabre, depth 175, and size 1933.
Transpiled circuit index 25 (H2) in 0.32 seconds with method sabre, depth 1237, and size 5502.
Transpiled circuit index 26 (uuf100-ham) in 0.20 seconds with method sabre, depth 385, and size 4303.
Transpiled circuit index 27 (ham-graph-gnp_k-5) in 0.20 seconds with method sabre, depth 311, and size 3654.
Transpiled circuit index 28 (tfim) in 0.15 seconds with method sabre, depth 276, and size 3213.
Transpiled circuit index 29 (uuf100-ham) in 0.21 seconds with method sabre, depth 520, and size 5250.
Transpiled circuit index 30 (flat100-ham) in 0.15 seconds with method sabre, depth 131, and size 3157.
Transpiled circuit index 31 (uf100-ham) in 0.24 seconds with method sabre, depth 624, and size 7378.
Transpiled circuit index 32 (OH) in 0.88 seconds with method sabre, depth 2175, and size 9808.
Transpiled circuit index 33 (HF) in 0.66 seconds with method sabre, depth 2206, and size 9417.
Transpiled circuit index 34 (BH) in 0.89 seconds with method sabre, depth 2177, and size 9802.
Transpiled circuit index 0 (all-vib-o3) in 0.02 seconds with method ai, depth 6, and size 58.
Transpiled circuit index 1 (all-vib-c2h) in 1.11 seconds with method ai, depth 2, and size 39.
Transpiled circuit index 2 (all-vib-bh) in 0.01 seconds with method ai, depth 3, and size 30.
Transpiled circuit index 3 (all-vib-c2h) in 0.11 seconds with method ai, depth 18, and size 94.
Transpiled circuit index 4 (graph-gnp_k-2) in 0.11 seconds with method ai, depth 22, and size 129.
Transpiled circuit index 5 (all-vib-fccf) in 0.06 seconds with method ai, depth 22, and size 177.
Transpiled circuit index 6 (all-vib-hno) in 8.62 seconds with method ai, depth 10, and size 198.
Transpiled circuit index 7 (all-vib-bhf2) in 3.71 seconds with method ai, depth 18, and size 195.
Transpiled circuit index 8 (LiH) in 0.19 seconds with method ai, depth 62, and size 267.
Transpiled circuit index 9 (uf20-ham) in 0.22 seconds with method ai, depth 47, and size 321.
Transpiled circuit index 10 (all-vib-fccf) in 0.71 seconds with method ai, depth 38, and size 369.
Transpiled circuit index 11 (all-vib-fccf) in 0.24 seconds with method ai, depth 65, and size 315.
Transpiled circuit index 12 (all-vib-ch2) in 0.24 seconds with method ai, depth 91, and size 430.
Transpiled circuit index 13 (tfim) in 0.15 seconds with method ai, depth 12, and size 251.
Transpiled circuit index 14 (all-vib-cyclo_propene) in 8.50 seconds with method ai, depth 18, and size 311.
Transpiled circuit index 15 (graph-gnp_k-4) in 0.25 seconds with method ai, depth 117, and size 659.
Transpiled circuit index 16 (all-vib-hc3h2cn) in 16.11 seconds with method ai, depth 2, and size 242.
Transpiled circuit index 17 (TSP_Ncity-4) in 0.39 seconds with method ai, depth 98, and size 564.
Transpiled circuit index 18 (tfim) in 0.38 seconds with method ai, depth 23, and size 437.
Transpiled circuit index 19 (all-vib-h2co) in 24.97 seconds with method ai, depth 38, and size 707.
Transpiled circuit index 20 (Be2) in 1.07 seconds with method ai, depth 293, and size 1392.
Transpiled circuit index 21 (graph-complete_bipart) in 0.61 seconds with method ai, depth 229, and size 1437.
Transpiled circuit index 22 (all-vib-f2) in 0.57 seconds with method ai, depth 178, and size 964.
Transpiled circuit index 23 (all-vib-cyclo_propene) in 50.89 seconds with method ai, depth 34, and size 1425.
Transpiled circuit index 24 (TSP_Ncity-5) in 1.61 seconds with method ai, depth 171, and size 2020.
Transpiled circuit index 25 (H2) in 6.39 seconds with method ai, depth 1148, and size 5208.
Transpiled circuit index 26 (uuf100-ham) in 3.97 seconds with method ai, depth 376, and size 5048.
Transpiled circuit index 27 (ham-graph-gnp_k-5) in 3.54 seconds with method ai, depth 357, and size 4451.
Transpiled circuit index 28 (tfim) in 1.72 seconds with method ai, depth 216, and size 3026.
Transpiled circuit index 29 (uuf100-ham) in 4.45 seconds with method ai, depth 426, and size 5399.
Transpiled circuit index 30 (flat100-ham) in 7.02 seconds with method ai, depth 86, and size 3108.
Transpiled circuit index 31 (uf100-ham) in 12.85 seconds with method ai, depth 623, and size 8354.
Transpiled circuit index 32 (OH) in 15.19 seconds with method ai, depth 2084, and size 9543.
Transpiled circuit index 33 (HF) in 17.51 seconds with method ai, depth 2063, and size 9446.
Transpiled circuit index 34 (BH) in 15.33 seconds with method ai, depth 2094, and size 9730.
Transpiled circuit index 0 (all-vib-o3) in 0.02 seconds with method rustiq, depth 13, and size 83.
Transpiled circuit index 1 (all-vib-c2h) in 1.11 seconds with method rustiq, depth 2, and size 39.
Transpiled circuit index 2 (all-vib-bh) in 0.01 seconds with method rustiq, depth 3, and size 30.
Transpiled circuit index 3 (all-vib-c2h) in 0.01 seconds with method rustiq, depth 13, and size 79.
Transpiled circuit index 4 (graph-gnp_k-2) in 0.02 seconds with method rustiq, depth 31, and size 131.
Transpiled circuit index 5 (all-vib-fccf) in 0.04 seconds with method rustiq, depth 50, and size 306.
Transpiled circuit index 6 (all-vib-hno) in 14.03 seconds with method rustiq, depth 22, and size 276.
Transpiled circuit index 7 (all-vib-bhf2) in 3.15 seconds with method rustiq, depth 13, and size 155.
Transpiled circuit index 8 (LiH) in 0.03 seconds with method rustiq, depth 54, and size 270.
Transpiled circuit index 9 (uf20-ham) in 0.04 seconds with method rustiq, depth 65, and size 398.
Transpiled circuit index 10 (all-vib-fccf) in 0.16 seconds with method rustiq, depth 41, and size 516.
Transpiled circuit index 11 (all-vib-fccf) in 0.02 seconds with method rustiq, depth 34, and size 189.
Transpiled circuit index 12 (all-vib-ch2) in 0.03 seconds with method rustiq, depth 49, and size 240.
Transpiled circuit index 13 (tfim) in 0.05 seconds with method rustiq, depth 20, and size 366.
Transpiled circuit index 14 (all-vib-cyclo_propene) in 9.08 seconds with method rustiq, depth 16, and size 277.
Transpiled circuit index 15 (graph-gnp_k-4) in 0.04 seconds with method rustiq, depth 116, and size 612.
Transpiled circuit index 16 (all-vib-hc3h2cn) in 13.89 seconds with method rustiq, depth 2, and size 257.
Transpiled circuit index 17 (TSP_Ncity-4) in 0.05 seconds with method rustiq, depth 133, and size 737.
Transpiled circuit index 18 (tfim) in 0.11 seconds with method rustiq, depth 25, and size 680.
Transpiled circuit index 19 (all-vib-h2co) in 27.19 seconds with method rustiq, depth 66, and size 983.
Transpiled circuit index 20 (Be2) in 0.07 seconds with method rustiq, depth 215, and size 1030.
Transpiled circuit index 21 (graph-complete_bipart) in 0.14 seconds with method rustiq, depth 328, and size 1918.
Transpiled circuit index 22 (all-vib-f2) in 0.05 seconds with method rustiq, depth 114, and size 692.
Transpiled circuit index 23 (all-vib-cyclo_propene) in 62.25 seconds with method rustiq, depth 74, and size 2348.
Transpiled circuit index 24 (TSP_Ncity-5) in 0.20 seconds with method rustiq, depth 436, and size 3605.
Transpiled circuit index 25 (H2) in 0.21 seconds with method rustiq, depth 643, and size 3476.
Transpiled circuit index 26 (uuf100-ham) in 0.24 seconds with method rustiq, depth 678, and size 6120.
Transpiled circuit index 27 (ham-graph-gnp_k-5) in 0.22 seconds with method rustiq, depth 588, and size 5241.
Transpiled circuit index 28 (tfim) in 0.34 seconds with method rustiq, depth 340, and size 5901.
Transpiled circuit index 29 (uuf100-ham) in 0.33 seconds with method rustiq, depth 881, and size 7667.
Transpiled circuit index 30 (flat100-ham) in 0.31 seconds with method rustiq, depth 279, and size 4910.
Transpiled circuit index 31 (uf100-ham) in 0.38 seconds with method rustiq, depth 1138, and size 10607.
Transpiled circuit index 32 (OH) in 0.38 seconds with method rustiq, depth 1148, and size 6512.
Transpiled circuit index 33 (HF) in 0.37 seconds with method rustiq, depth 1090, and size 6256.
Transpiled circuit index 34 (BH) in 0.37 seconds with method rustiq, depth 1148, and size 6501.

Таблиця результатів (пропускаємо візуалізацію, оскільки вихідні ланцюги дуже великі):

summary_ham = (
results_ham.groupby("method")[["depth", "size", "runtime"]]
.mean()
.round(2)
)
print(summary_ham)

results_ham
depth     size  runtime
method
ai 316.86 2181.26 5.97
rustiq 281.94 2268.80 3.86
sabre 337.97 2120.14 3.07
method        qc_name  qc_index  num_qubits  \
0 sabre all-vib-o3 0 4
1 sabre all-vib-c2h 1 4
2 sabre all-vib-bh 2 2
3 sabre all-vib-c2h 3 3
4 sabre graph-gnp_k-2 4 4
.. ... ... ... ...
100 rustiq flat100-ham 30 90
101 rustiq uf100-ham 31 46
102 rustiq OH 32 10
103 rustiq HF 33 10
104 rustiq BH 34 10

ops depth size runtime
0 {'rz': 28, 'sx': 24, 'cz': 6} 6 58 0.016597
1 {'rz': 17, 'sx': 16, 'cz': 4, 'x': 2} 2 39 1.102089
2 {'sx': 14, 'rz': 13, 'cz': 3} 3 30 0.011042
3 {'sx': 46, 'rz': 45, 'cz': 18, 'x': 6} 18 115 0.025816
4 {'sx': 49, 'rz': 47, 'cz': 24, 'x': 9} 24 129 0.023077
.. ... ... ... ...
100 {'sx': 2709, 'cz': 1379, 'rz': 817, 'x': 5} 279 4910 0.309448
101 {'sx': 6180, 'cz': 3120, 'rz': 1303, 'x': 4} 1138 10607 0.380977
102 {'sx': 3330, 'cz': 1704, 'rz': 1455, 'x': 23} 1148 6512 0.383564
103 {'sx': 3213, 'cz': 1620, 'rz': 1406, 'x': 17} 1090 6256 0.368578
104 {'sx': 3331, 'cz': 1704, 'rz': 1447, 'x': 19} 1148 6501 0.374822

[105 rows x 8 columns]

Візуалізувати продуктивність на основі індексу ланцюга:

plot_transpilation_metrics(
results_ham, "Transpilation Metrics for Hamiltonian Circuits"
)

Output of the previous code cell

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

def analyze_and_plot_best_methods(results, metric):
"""
Analyze the best-performing methods for a given metric and plot a pie chart.

Parameters:
results (DataFrame): The input DataFrame containing method performance data.
metric (str): The metric to evaluate ("depth" or "size").
"""
method_counts = Counter()
for qc_idx, group in results.groupby("qc_index"):
min_value = group[metric].min()

# Find all methods that achieved this minimum value
best_methods = group[group[metric] == min_value]["method"]
# Update counts for all best methods (handling ties)
method_counts.update(best_methods)
best_method_counts = dict(
sorted(method_counts.items(), key=lambda x: x[1], reverse=True)
)

# Print summary
print(f"Best-performing methods based on {metric}:")
for method, count in best_method_counts.items():
print(f" {method}: {count} circuit(s)")

# Plot pie chart
num_methods = len(best_method_counts)
colors = plt.cm.viridis_r(range(0, 256, 256 // num_methods))
plt.figure(figsize=(5, 5))
plt.pie(
best_method_counts.values(),
labels=best_method_counts.keys(),
autopct="%1.1f%%",
startangle=140,
wedgeprops={"edgecolor": "black"},
textprops={"fontsize": 10},
colors=colors,
)
plt.title(
f"Percentage of Circuits Method Performed Best for {metric.capitalize()}",
fontsize=12,
fontweight="bold",
)
plt.show()

analyze_and_plot_best_methods(results_ham, "depth")
analyze_and_plot_best_methods(results_ham, "size")
Best-performing methods based on depth:
ai: 16 circuit(s)
rustiq: 16 circuit(s)
sabre: 10 circuit(s)

Output of the previous code cell

Best-performing methods based on size:
sabre: 18 circuit(s)
rustiq: 14 circuit(s)
ai: 10 circuit(s)

Output of the previous code cell

Аналіз результатів компіляції ланцюгів гамільтоніанів

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

Rustiq показав найкращі результати в середньому з точки зору глибини ланцюга, досягнувши приблизно на 20% меншої глибини, ніж SABRE. Це очікувано, оскільки Rustiq спеціально розроблений для синтезу операцій PauliEvolutionGate з оптимізованими стратегіями декомпозиції з низькою глибиною. Крім того, графік глибини показує, що у міру збільшення розміру та складності ланцюгів Rustiq масштабується найбільш ефективно, підтримуючи значно меншу глибину, ніж AI та SABRE на більших ланцюгах.

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

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

Підсумок

Хоча транспілятор на основі штучного інтелекту зазвичай дає кращі результати, ніж SABRE, особливо в глибині ланцюга, висновок не повинен бути просто «завжди використовуйте транспілятор на основі штучного інтелекту». Є важливі нюанси, які слід враховувати:

  • Транспілятор на основі штучного інтелекту зазвичай надійний і забезпечує ланцюги, оптимізовані за глибиною, але він має компроміси в часі виконання, а також має інші обмеження, включаючи підтримувані карти зв'язків та можливості синтезу. Вони детально описані в документації Qiskit Transpiler Service.

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

  • Також важливо враховувати структуру ланцюга при виборі методу. Наприклад, rustiq спеціально створений для ланцюгів, що включають PauliEvolutionGate, і часто дає найкращу продуктивність для задач симуляції гамільтоніанів.

Рекомендація:

Не існує універсальної стратегії транспіляції. Користувачам рекомендується розуміти структуру свого ланцюга та тестувати кілька методів транспіляції — включаючи AI, SABRE та спеціалізовані інструменти, такі як Rustiq — щоб знайти найбільш ефективне рішення для їхньої конкретної задачі та обмежень апаратного забезпечення.

Крок 3: Виконання за допомогою примітивів Qiskit

Оскільки цей посібник зосереджений на транспіляції, жодних експериментів на квантовому пристрої не виконується. Мета полягає в тому, щоб використати оптимізації з Кроку 2 для отримання транспільованого ланцюга зі зменшеною глибиною та кількістю воріт.

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

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

Посилання

[1] "LightSABRE: A Lightweight and Enhanced SABRE Algorithm". H. Zou, M. Treinish, K. Hartman, A. Ivrii, J. Lishman et al. https://arxiv.org/abs/2409.08368

[2] "Practical and efficient quantum circuit synthesis and transpiling with Reinforcement Learning". D. Kremer, V. Villar, H. Paik, I. Duran, I. Faro, J. Cruz-Benito et al. https://arxiv.org/abs/2405.13196

[3] "Pauli Network Circuit Synthesis with Reinforcement Learning". A. Dubal, D. Kremer, S. Martiel, V. Villar, D. Wang, J. Cruz-Benito et al. https://arxiv.org/abs/2503.14448