Vai al contenuto principale

Metodi di compilazione per circuiti di simulazione hamiltoniana

Utilizzo stimato della QPU: non è stata eseguita alcuna esecuzione in questo tutorial perché è incentrato sul processo di transpilazione.

Premesse​

La compilazione dei circuiti quantistici è un passo cruciale nel flusso di lavoro del calcolo quantistico. Comporta la trasformazione di un algoritmo quantistico ad alto livello in un circuito quantistico fisico che aderisce ai vincoli dell'hardware quantistico target. Una compilazione efficace può avere un impatto significativo sulle prestazioni degli algoritmi quantistici riducendo la profondità del circuito, il numero di gate e il tempo di esecuzione. Questo tutorial esplora tre approcci distinti alla compilazione di circuiti quantistici in Qiskit, mostrando i loro punti di forza e le applicazioni attraverso esempi pratici.

L'obiettivo di questo tutorial è insegnare agli utenti come applicare e valutare tre metodi di compilazione in Qiskit: il transpiler SABRE, il transpiler potenziato dall'IA e il plugin Rustiq. Gli utenti impareranno come utilizzare ogni metodo in modo efficace e come confrontare le loro prestazioni su diversi circuiti quantistici. Al termine di questo tutorial, gli utenti saranno in grado di scegliere e adattare le strategie di compilazione in base a specifici obiettivi di ottimizzazione come la riduzione della profondità del circuito, la minimizzazione del numero di gate o il miglioramento del tempo di esecuzione.

Cosa imparerai​

  • Come utilizzare il transpiler Qiskit con SABRE per l'ottimizzazione del layout e del routing.
  • Come sfruttare il transpiler AI per l'ottimizzazione avanzata e automatizzata dei circuiti.
  • Come impiegare il plugin Rustiq per circuiti che richiedono una sintesi precisa delle operazioni, in particolare nelle attività di simulazione hamiltoniana.

Questo tutorial utilizza tre circuiti di esempio seguendo il flusso di lavoro dei pattern Qiskit per illustrare le prestazioni di ciascun metodo di compilazione. Al termine di questo tutorial, gli utenti saranno equipaggiati per scegliere la strategia di compilazione appropriata in base ai loro requisiti e vincoli specifici.

Panoramica dei metodi di compilazione​

1. Transpiler Qiskit con SABRE​

Il transpiler Qiskit utilizza l'algoritmo SABRE (SWAP-based BidiREctional heuristic search) per ottimizzare il layout e il routing del circuito. SABRE si concentra sulla minimizzazione dei gate SWAP e del loro impatto sulla profondità del circuito rispettando i vincoli di connettività dell'hardware. Questo metodo è altamente versatile e adatto per l'ottimizzazione di circuiti general-purpose, fornendo un equilibrio tra prestazioni e tempo di calcolo. Per sfruttare i più recenti miglioramenti di SABRE, dettagliati in [1], puoi aumentare il numero di prove (ad esempio, layout_trials=400, swap_trials=400). Ai fini di questo tutorial, utilizzeremo i valori predefiniti per il numero di prove al fine di confrontare con il transpiler predefinito di Qiskit. I vantaggi e l'esplorazione dei parametri di SABRE sono trattati in un tutorial di approfondimento separato.

2. Transpiler AI​

Il transpiler potenziato dall'IA in Qiskit utilizza l'apprendimento automatico per prevedere strategie di transpilazione ottimali analizzando i pattern nella struttura del circuito e i vincoli hardware per selezionare la migliore sequenza di ottimizzazioni per un dato input. Questo metodo è particolarmente efficace per circuiti quantistici su larga scala, offrendo un alto grado di automazione e adattabilità a diversi tipi di problemi. Oltre all'ottimizzazione generale dei circuiti, il transpiler AI può essere utilizzato con il pass AIPauliNetworkSynthesis, che si rivolge ai circuiti di rete Pauli — blocchi composti da gate H, S, SX, CX, RX, RY e RZ — e applica un approccio di sintesi basato sull'apprendimento per rinforzo. Per ulteriori informazioni sul transpiler AI e le sue strategie di sintesi, consultate [2] e [3].

3. Plugin Rustiq​

Il plugin Rustiq introduce tecniche avanzate di sintesi specificamente per le operazioni PauliEvolutionGate, che rappresentano rotazioni di Pauli comunemente utilizzate nelle dinamiche trotterizzate. Questo plugin è prezioso per i circuiti che implementano la simulazione hamiltoniana, come quelli utilizzati nei problemi di chimica quantistica e fisica, dove rotazioni di Pauli accurate sono essenziali per simulare efficacemente gli hamiltoniani dei problemi. Rustiq offre una sintesi di circuiti precisa e a bassa profondità per queste operazioni specializzate. Per maggiori dettagli sull'implementazione e le prestazioni di Rustiq, consulta [4].

Esplorando in profondità questi metodi di compilazione, questo tutorial fornisce agli utenti gli strumenti per migliorare le prestazioni dei loro circuiti quantistici, aprendo la strada a calcoli quantistici più efficienti e pratici.

Requisiti​

Prima di iniziare questo tutorial, assicurati di avere installato quanto segue:

  • Qiskit SDK v1.3 o successivo, con supporto per la visualizzazione
  • Qiskit Runtime v0.28 o successivo (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)
  • Libreria grafica Networkx (pip install networkx)

Configurazione​

# 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

Parte 1: Circuito Efficient SU2​

Passo 1: Mappare gli input classici a un problema quantistico​

In questa sezione, esploriamo il circuito efficient_su2, un ansatz hardware-efficient comunemente utilizzato negli algoritmi quantistici variazionali (come VQE) e nelle attività di apprendimento automatico quantistico. Il circuito è costituito da strati alternati di rotazioni a singolo qubit e gate di entanglement disposti in un pattern circolare, progettato per esplorare efficacemente lo spazio degli stati quantistici mantenendo una profondità gestibile.

Inizieremo costruendo un circuito efficient_su2 per dimostrare come confrontare diversi metodi di compilazione. Dopo la Parte 1, espanderemo la nostra analisi a un insieme più ampio di circuiti, consentendo un benchmark completo per valutare le prestazioni di varie tecniche di compilazione.

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

Passo 2: Ottimizzare il problema per l'esecuzione su hardware quantistico​

Questo passo è il focus principale del tutorial. Qui, miriamo a ottimizzare i circuiti quantistici per un'esecuzione efficiente su hardware quantistico reale. Il nostro obiettivo principale è ridurre la profondità del circuito e il numero di gate, che sono fattori chiave per migliorare la fedeltà di esecuzione e mitigare il rumore hardware.

  • Transpiler SABRE: Utilizza il transpiler predefinito di Qiskit con l'algoritmo di layout e routing SABRE.
  • Transpiler AI (modalità locale): Il transpiler standard potenziato dall'IA che utilizza l'inferenza locale e la strategia di sintesi predefinita.
  • Plugin Rustiq: Un plugin transpiler progettato per la compilazione a bassa profondità su misura per le attività di simulazione hamiltoniana.

L'obiettivo di questo passo è confrontare i risultati di questi metodi in termini di profondità del circuito transpilato e numero di gate. Un'altra metrica importante che consideriamo è il tempo di esecuzione della transpilazione. Analizzando queste metriche, possiamo valutare i punti di forza relativi di ciascun metodo e determinare quale produce il circuito più efficiente per l'esecuzione sull'hardware selezionato.

Nota: Per l'esempio iniziale del circuito SU2, confronteremo solo il transpiler SABRE con il transpiler AI predefinito. Tuttavia, nel successivo benchmark utilizzando i circuiti Hamlib, confronteremo tutti e tre i metodi di transpilazione.

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

Transpiler Qiskit con SABRE:

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

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

Plugin 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,
)

Transpilare e catturare le metriche​

Per confrontare le prestazioni dei metodi di compilazione, definiamo una funzione che transpila il circuito di input e cattura le metriche rilevanti in modo coerente. Ciò include la profondità totale del circuito, il numero complessivo di gate e il tempo di transpilazione.

Oltre a queste metriche standard, registriamo anche la profondità dei gate a 2 qubit, che è una metrica particolarmente importante per valutare l'esecuzione su hardware quantistico. A differenza della profondità totale, che include tutti i gate, la profondità a 2 qubit riflette più accuratamente la durata effettiva dell'esecuzione del circuito sull'hardware. Questo perché i gate a 2 qubit dominano tipicamente il budget di tempo ed errore nella maggior parte dei dispositivi quantistici. Pertanto, minimizzare la profondità a 2 qubit è fondamentale per migliorare la fedeltà e ridurre gli effetti di decoerenza durante l'esecuzione.

Utilizzeremo questa funzione per analizzare le prestazioni dei diversi metodi di compilazione su più circuiti.

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.

Visualizzare i risultati transpilati di uno dei circuiti.

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

Tabella dei risultati:

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

Grafico dei risultati​

Così come definiamo una funzione per catturare costantemente le metriche, definiremo anche una per rappresentare graficamente le metriche. Qui, traceremo la profondità a due qubit, il numero di gate e il tempo di esecuzione per ciascun metodo di compilazione sui circuiti.

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

Analisi dei risultati di compilazione del circuito SU2​

In questo esperimento, confrontiamo due metodi di transpilazione — il transpiler SABRE di Qiskit e il transpiler potenziato dall'IA — su un insieme di circuiti efficient_su2. Poiché questi circuiti non includono alcuna operazione PauliEvolutionGate, il plugin Rustiq non è incluso in questo confronto.

In media, il transpiler AI si comporta meglio in termini di profondità del circuito, con un miglioramento superiore al 10% su tutta la gamma di circuiti SU2. Per il numero di gate (dimensione del circuito) e il tempo di esecuzione della transpilazione, entrambi i metodi producono risultati complessivamente simili.

Tuttavia, ispezionando i singoli punti dati si rivela un'intuizione più profonda:

  • Per la maggior parte delle dimensioni di qubit, sia SABRE che AI producono risultati quasi identici, suggerendo che in molti casi entrambi i metodi convergono verso soluzioni similmente efficienti.
  • Per alcune dimensioni di circuito, specificamente a 30, 50, 70 e 90 qubit, il transpiler AI trova circuiti significativamente meno profondi rispetto a SABRE. Ciò indica che l'approccio basato sull'apprendimento dell'AI è in grado di scoprire layout o percorsi di routing più ottimali nei casi in cui l'euristica SABRE non lo fa.

Questo comportamento evidenzia un punto importante da ricordare:

Sebbene SABRE e AI producano spesso risultati comparabili, il transpiler AI può occasionalmente scoprire soluzioni molto migliori, in particolare in termini di profondità, il che può portare a prestazioni significativamente migliorate sull'hardware.

Parte 2: Circuito di simulazione hamiltoniana​

Passo 1: Investigare i circuiti con PauliEvolutionGate​

In questa sezione, investigheremo i circuiti quantistici costruiti utilizzando PauliEvolutionGate, che consente una simulazione efficiente degli Hamiltoniani. Analizzeremo come i diversi metodi di compilazione ottimizzano questi circuiti attraverso vari Hamiltoniani.

Hamiltoniani utilizzati nel benchmark​

Gli Hamiltoniani utilizzati in questo benchmark descrivono interazioni a coppie tra qubit, inclusi termini come ZZZZ, XXXX e YYYY. Questi Hamiltoniani sono comunemente utilizzati nella chimica quantistica, nella fisica della materia condensata e nella scienza dei materiali, dove modellano sistemi di particelle interagenti.

Per riferimento, gli utenti possono esplorare un insieme più ampio di Hamiltoniani in questo articolo: Efficient Hamiltonian Simulation on Noisy Quantum Devices.

Fonte del benchmark: Hamlib e Benchpress​

I circuiti utilizzati in questo benchmark sono tratti dal repository di benchmark Hamlib, che contiene carichi di lavoro realistici di simulazione hamiltoniana.

Questi stessi circuiti sono stati precedentemente sottoposti a benchmark utilizzando Benchpress, un framework open-source per valutare le prestazioni della transpilazione quantistica. Utilizzando questo insieme standardizzato di circuiti, possiamo confrontare direttamente l'efficacia di diverse strategie di compilazione su problemi di simulazione rappresentativi.

La simulazione hamiltoniana è un compito fondamentale nel calcolo quantistico, con applicazioni nelle simulazioni molecolari, nei problemi di ottimizzazione e nella fisica quantistica dei sistemi a molti corpi. Comprendere come i diversi metodi di compilazione ottimizzano questi circuiti può aiutare gli utenti a migliorare l'esecuzione pratica di tali circuiti su dispositivi quantistici a breve termine.

# 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

Passo 2: Ottimizzare il problema per l'esecuzione su hardware quantistico​

Come nell'esempio precedente, utilizzeremo lo stesso backend per garantire coerenza nei nostri confronti. Poiché i pass manager (pm_sabre, pm_ai e pm_rustiq) sono già stati inizializzati, possiamo procedere direttamente con la transpilazione dei circuiti hamiltoniani utilizzando ciascun metodo.

Questo passo si concentra esclusivamente sull'esecuzione della transpilazione e sulla registrazione delle metriche dei circuiti risultanti, incluse profondità, conteggio delle porte e tempo di esecuzione della transpilazione. Analizzando questi risultati, miriamo a determinare l'efficienza di ciascun metodo di transpilazione per questo tipo di circuito. Transpilare e catturare le metriche:

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.

Tabella dei risultati (omettendo la visualizzazione poiché i circuiti di output sono molto grandi):

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]

Visualizzare le prestazioni in base all'indice del circuito:

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

Output of the previous code cell

Visualizzare la percentuale di circuiti per i quali ciascun metodo ha ottenuto le migliori prestazioni.

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

Analisi dei risultati della compilazione del circuito hamiltoniano​

In questa sezione, valutiamo le prestazioni di tre metodi di transpilazione — SABRE, il transpiler basato su AI e Rustiq — su circuiti quantistici costruiti con PauliEvolutionGate, che sono comunemente utilizzati nei compiti di simulazione hamiltoniana.

Rustiq ha ottenuto le migliori prestazioni in media in termini di profondità del circuito, raggiungendo una profondità inferiore di circa il 20% rispetto a SABRE. Questo è prevedibile, poiché Rustiq è specificamente progettato per sintetizzare operazioni PauliEvolutionGate con strategie di decomposizione ottimizzate a bassa profondità. Inoltre, il grafico della profondità mostra che man mano che i circuiti aumentano in dimensione e complessità, Rustiq scala in modo più efficace, mantenendo una profondità significativamente inferiore rispetto sia a AI che a SABRE sui circuiti più grandi.

Il transpiler AI ha mostrato prestazioni forti e coerenti per la profondità del circuito, superando costantemente SABRE nella maggior parte dei circuiti. Tuttavia, ha comportato il tempo di esecuzione più elevato, specialmente sui circuiti più grandi, il che potrebbe limitarne la praticità nei carichi di lavoro sensibili al tempo. La sua scalabilità in termini di tempo di esecuzione rimane un limite chiave, anche se offre miglioramenti solidi in profondità.

SABRE, pur producendo la profondità media più elevata, ha raggiunto il conteggio medio di porte più basso, seguito da vicino dal transpiler AI. Questo è in linea con il design dell'euristica di SABRE, che dà priorità alla minimizzazione diretta del conteggio delle porte. Rustiq, nonostante la sua forza nella riduzione della profondità, aveva il conteggio medio di porte più elevato, che è un compromesso notevole da considerare nelle applicazioni in cui la dimensione del circuito è più importante della durata del circuito.

Riepilogo​

Sebbene il transpiler AI generalmente fornisca risultati migliori rispetto a SABRE, in particolare nella profondità del circuito, la conclusione non dovrebbe essere semplicemente "usare sempre il transpiler AI". Ci sono importanti sfumature da considerare:

  • Il transpiler AI è tipicamente affidabile e fornisce circuiti ottimizzati per la profondità, ma comporta compromessi nel tempo di esecuzione e ha anche altre limitazioni, tra cui mappe di accoppiamento supportate e capacità di sintesi. Queste sono dettagliate nella documentazione del Qiskit Transpiler Service.

  • In alcuni casi, in particolare con circuiti molto grandi o specifici per l'hardware, il transpiler AI potrebbe non essere così efficace. In questi casi, il transpiler SABRE predefinito rimane estremamente affidabile e può essere ulteriormente ottimizzato regolando i suoi parametri (vedere il tutorial di ottimizzazione SABRE).

  • È anche importante considerare la struttura del circuito quando si sceglie un metodo. Ad esempio, rustiq è progettato appositamente per i circuiti che coinvolgono PauliEvolutionGate e spesso produce le migliori prestazioni per i problemi di simulazione hamiltoniana.

Raccomandazione:

Non esiste una strategia di transpilazione universale. Si incoraggiano gli utenti a comprendere la struttura del loro circuito e a testare più metodi di transpilazione — inclusi AI, SABRE e strumenti specializzati come Rustiq — per trovare la soluzione più efficiente per il loro problema specifico e i vincoli hardware.

Passo 3: Eseguire utilizzando le primitive di Qiskit​

Poiché questo tutorial si concentra sulla transpilazione, nessun esperimento viene eseguito su un dispositivo quantistico. L'obiettivo è sfruttare le ottimizzazioni del Passo 2 per ottenere un circuito transpilato con profondità e conteggio delle porte ridotti.

Passo 4: Post-elaborare e restituire il risultato nel formato classico desiderato​

Poiché non c'è esecuzione per questo notebook, non ci sono risultati da post-elaborare.

Riferimenti​

[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