Méthodes de compilation pour les circuits de simulation hamiltonienne
Utilisation estimée du QPU : aucune exécution n'a été effectuée dans ce tutoriel car il est centré sur le processus de transpilation.
Contexte
La compilation de circuits quantiques est une étape cruciale dans le flux de travail de l'informatique quantique. Elle consiste à transformer un algorithme quantique de haut niveau en un circuit quantique physique conforme aux contraintes du matériel quantique cible. Une compilation efficace peut avoir un impact significatif sur les performances des algorithmes quantiques en réduisant la profondeur du circuit, le nombre de portes et le temps d'exécution. Ce tutoriel explore trois approches distinctes de la compilation de circuits quantiques dans Qiskit, en présentant leurs atouts et leurs applications à travers des exemples pratiques.
L'objectif de ce tutoriel est d'enseigner aux utilisateurs comment appliquer et évaluer trois méthodes de compilation dans Qiskit : le transpileur SABRE, le transpileur alimenté par l'IA et le plugin Rustiq. Vous apprendrez à utiliser efficacement chaque méthode et à comparer leurs performances sur différents circuits quantiques. À l'issue de ce tutoriel, vous serez en mesure de choisir et d'adapter des stratégies de compilation en fonction d'objectifs d'optimisation spécifiques tels que la réduction de la profondeur du circuit, la minimisation du nombre de portes ou l'amélioration du temps d'exécution.
Ce que vous apprendrez
- Comment utiliser le transpileur Qiskit avec SABRE pour l'optimisation du placement et du routage.
- Comment tirer parti du transpileur IA pour une optimisation avancée et automatisée des circuits.
- Comment utiliser le plugin Rustiq pour les circuits nécessitant une synthèse précise des opérations, en particulier pour les tâches de simulation hamiltonienne.
Ce tutoriel utilise trois exemples de circuits suivant le flux de travail Qiskit patterns pour illustrer les performances de chaque méthode de compilation. À la fin de ce tutoriel, vous serez en mesure de choisir la stratégie de compilation appropriée en fonction de vos exigences et contraintes spécifiques.
Vue d'ensemble des méthodes de compilation
1. Transpileur Qiskit avec SABRE
Le transpileur Qiskit utilise l'algorithme SABRE (SWAP-based BidiREctional heuristic search) pour optimiser le placement et le routage des circuits. SABRE se concentre sur la minimisation des portes SWAP et de leur impact sur la profondeur du circuit tout en respectant les contraintes de connectivité du matériel. Cette méthode est très polyvalente et adaptée à l'optimisation de circuits à usage général, offrant un bon équilibre entre performance et temps de calcul. Pour bénéficier des dernières améliorations de SABRE, détaillées dans [1], vous pouvez augmenter le nombre d'essais (par exemple, layout_trials=400, swap_trials=400). Pour les besoins de ce tutoriel, nous utiliserons les valeurs par défaut du nombre d'essais afin de comparer avec le transpileur par défaut de Qiskit. Les avantages et l'exploration des paramètres de SABRE sont traités dans un tutoriel approfondi séparé.
2. Transpileur IA
Le transpileur alimenté par l'IA dans Qiskit utilise l'apprentissage automatique pour prédire les stratégies de transpilation optimales en analysant les motifs de la structure des circuits et les contraintes matérielles afin de sélectionner la meilleure séquence d'optimisations pour une entrée donnée. Cette méthode est particulièrement efficace pour les circuits quantiques à grande échelle, offrant un haut degré d'automatisation et d'adaptabilité à divers types de problèmes. En plus de l'optimisation générale des circuits, le transpileur IA peut être utilisé avec la passe AIPauliNetworkSynthesis, qui cible les circuits de réseau de Pauli — des blocs composés de portes H, S, SX, CX, RX, RY et RZ — et applique une approche de synthèse basée sur l'apprentissage par renforcement. Pour plus d'informations sur le transpileur IA et ses stratégies de synthèse, consultez [2] et [3].
3. Plugin Rustiq
Le plugin Rustiq introduit des techniques de synthèse avancées spécifiquement pour les opérations PauliEvolutionGate, qui représentent les rotations de Pauli couramment utilisées dans les dynamiques trotterisées. Ce plugin est précieux pour les circuits implémentant la simulation hamiltonienne, tels que ceux utilisés dans les problèmes de chimie quantique et de physique, où des rotations de Pauli précises sont essentielles pour simuler efficacement les hamiltoniens du problème. Rustiq offre une synthèse de circuit précise et de faible profondeur pour ces opérations spécialisées. Pour plus de détails sur l'implémentation et les performances de Rustiq, veuillez consulter [4].
En explorant ces méthodes de compilation en profondeur, ce tutoriel fournit aux utilisateurs les outils nécessaires pour améliorer les performances de leurs circuits quantiques, ouvrant la voie à des calculs quantiques plus efficaces et plus pratiques.
Prérequis
Avant de commencer ce tutoriel, assurez-vous d'avoir installé les éléments suivants :
- Qiskit SDK v1.3 ou version ultérieure, avec prise en charge de la visualisation
- Qiskit Runtime v0.28 ou version ultérieure (
pip install qiskit-ibm-runtime) - Qiskit IBM Transpiler (
pip install qiskit-ibm-transpiler) - Qiskit AI Transpiler mode local (
pip install qiskit_ibm_ai_local_transpiler) - Bibliothèque de graphes Networkx (
pip install networkx)
Configuration
# Added by doQumentation — installs packages not in the Binder environment
%pip install -q qiskit-ibm-transpiler
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
Partie 1 : Circuit Efficient SU2
Étape 1 : Transposer les entrées classiques en un problème quantique
Dans cette section, nous explorons le circuit efficient_su2, un ansatz efficace pour le matériel couramment utilisé dans les algorithmes quantiques variationnels (tels que VQE) et les tâches d'apprentissage automatique quantique. Le circuit se compose de couches alternées de rotations à un qubit et de portes d'intrication disposées selon un schéma circulaire, conçu pour explorer efficacement l'espace des états quantiques tout en maintenant une profondeur raisonnable.
Nous commencerons par construire un circuit efficient_su2 pour démontrer comment comparer les différentes méthodes de compilation. Après la Partie 1, nous élargirons notre analyse à un ensemble plus large de circuits, permettant une évaluation comparative complète des performances de diverses techniques de compilation.
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")
Étape 2 : Optimiser le problème pour l'exécution sur matériel quantique
Cette étape est le point central du tutoriel. Ici, nous visons à optimiser les circuits quantiques pour une exécution efficace sur du matériel quantique réel. Notre objectif principal est de réduire la profondeur du circuit et le nombre de portes, qui sont des facteurs clés pour améliorer la fidélité d'exécution et atténuer le bruit matériel.
- Transpileur SABRE : Utilise le transpileur par défaut de Qiskit avec l'algorithme de placement et de routage SABRE.
- Transpileur IA (mode local) : Le transpileur standard alimenté par l'IA utilisant l'inférence locale et la stratégie de synthèse par défaut.
- Plugin Rustiq : Un plugin de transpilation conçu pour la compilation à faible profondeur, adapté aux tâches de simulation hamiltonienne.
L'objectif de cette étape est de comparer les résultats de ces méthodes en termes de profondeur et de nombre de portes du circuit transpilé. Une autre métrique importante que nous considérons est le temps d'exécution de la transpilation. En analysant ces métriques, nous pouvons évaluer les forces relatives de chaque méthode et déterminer laquelle produit le circuit le plus efficace pour l'exécution sur le matériel sélectionné.
Remarque : Pour l'exemple initial du circuit SU2, nous ne comparerons que le transpileur SABRE avec le transpileur IA par défaut. Cependant, dans le benchmark ultérieur utilisant les circuits Hamlib, nous comparerons les trois méthodes de transpilation.
# 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')>
Transpileur Qiskit avec SABRE :
pm_sabre = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=seed
)
Transpileur IA :
# 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,
)
Transpiler et capturer les métriques
Pour comparer les performances des méthodes de compilation, nous définissons une fonction qui transpile le circuit d'entrée et capture les métriques pertinentes de manière cohérente. Cela inclut la profondeur totale du circuit, le nombre total de portes et le temps de transpilation.
En plus de ces métriques standard, nous enregistrons également la profondeur des portes à 2 qubits, qui est une métrique particulièrement importante pour évaluer l'exécution sur du matériel quantique. Contrairement à la profondeur totale, qui inclut toutes les portes, la profondeur à 2 qubits reflète plus fidèlement la durée réelle d'exécution du circuit sur le matériel. En effet, les portes à 2 qubits dominent généralement le budget en temps et en erreurs dans la plupart des dispositifs quantiques. Ainsi, minimiser la profondeur à 2 qubits est essentiel pour améliorer la fidélité et réduire les effets de décohérence pendant l'exécution.
Nous utiliserons cette fonction pour analyser les performances des différentes méthodes de compilation sur plusieurs circuits.
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.
Afficher les résultats de la transpilation d'un des circuits.
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

AI transpilation

Tableau des résultats :
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
Graphique des résultats
Tout comme nous avons défini une fonction pour capturer les métriques de manière cohérente, nous en définirons également une pour tracer les métriques. Ici, nous tracerons la profondeur à deux qubits, le nombre de portes et le temps d'exécution pour chaque méthode de compilation à travers les circuits.
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"
)

Analyse des résultats de compilation du circuit SU2
Dans cette expérience, nous comparons deux méthodes de transpilation — le transpileur SABRE de Qiskit et le transpileur alimenté par l'IA — sur un ensemble de circuits efficient_su2. Étant donné que ces circuits ne contiennent aucune opération PauliEvolutionGate, le plugin Rustiq n'est pas inclus dans cette comparaison.
En moyenne, le transpileur IA obtient de meilleurs résultats en termes de profondeur de circuit, avec une amélioration supérieure à 10 % sur l'ensemble de la gamme de circuits SU2. Pour le nombre de portes (taille du circuit) et le temps d'exécution de la transpilation, les deux méthodes produisent des résultats globalement similaires.
Cependant, l'examen des points de données individuels révèle un aperçu plus approfondi :
- Pour la plupart des tailles de qubits, SABRE et l'IA produisent des résultats quasi identiques, suggérant que dans de nombreux cas, les deux méthodes convergent vers des solutions d'efficacité comparable.
- Pour certaines tailles de circuits, spécifiquement à 30, 50, 70 et 90 qubits, le transpileur IA trouve des circuits significativement moins profonds que SABRE. Cela indique que l'approche basée sur l'apprentissage de l'IA est capable de découvrir des placements ou des chemins de routage plus optimaux dans les cas où l'heuristique SABRE n'y parvient pas.
Ce comportement met en évidence un enseignement important :
Bien que SABRE et l'IA produisent souvent des résultats comparables, le transpileur IA peut occasionnellement découvrir de bien meilleures solutions, en particulier en termes de profondeur, ce qui peut conduire à des performances significativement améliorées sur le matériel.
Partie 2 : Circuit de simulation hamiltonienne
Étape 1 : Étudier les circuits avec PauliEvolutionGate
Dans cette section, nous étudions les circuits quantiques construits à l'aide de PauliEvolutionGate, qui permet la simulation efficace des hamiltoniens. Nous analyserons comment les différentes méthodes de compilation optimisent ces circuits pour divers hamiltoniens.
Hamiltoniens utilisés dans le benchmark
Les hamiltoniens utilisés dans ce benchmark décrivent des interactions par paires entre qubits, incluant des termes tels que , et . Ces hamiltoniens sont couramment utilisés en chimie quantique, en physique de la matière condensée et en science des matériaux, où ils modélisent des systèmes de particules en interaction.
Pour référence, les utilisateurs peuvent explorer un ensemble plus large d'hamiltoniens dans cet article : Efficient Hamiltonian Simulation on Noisy Quantum Devices.
Source du benchmark : Hamlib et Benchpress
Les circuits utilisés dans ce benchmark sont issus du dépôt de benchmarks Hamlib, qui contient des charges de travail réalistes de simulation hamiltonienne.
Ces mêmes circuits ont été précédemment évalués à l'aide de Benchpress, un framework open source pour l'évaluation des performances de transpilation quantique. En utilisant cet ensemble standardisé de circuits, nous pouvons comparer directement l'efficacité des différentes stratégies de compilation sur des problèmes de simulation représentatifs.
La simulation hamiltonienne est une tâche fondamentale en informatique quantique, avec des applications dans les simulations moléculaires, les problèmes d'optimisation et la physique quantique à N corps. Comprendre comment les différentes méthodes de compilation optimisent ces circuits peut aider les utilisateurs à améliorer l'exécution pratique de tels circuits sur les dispositifs quantiques actuels.
# 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
Étape 2 : Optimiser le problème pour l'exécution sur matériel quantique
Comme dans l'exemple précédent, nous utiliserons le même backend pour assurer la cohérence de nos comparaisons. Puisque les gestionnaires de passes (pm_sabre, pm_ai et pm_rustiq) ont déjà été initialisés, nous pouvons directement procéder à la transpilation des circuits hamiltoniens en utilisant chaque méthode.
Cette étape se concentre uniquement sur la transpilation et l'enregistrement des métriques de circuit résultantes, notamment la profondeur, le nombre de portes et le temps d'exécution de la transpilation. En analysant ces résultats, nous cherchons à déterminer l'efficacité de chaque méthode de transpilation pour ce type de circuit. Transpiler et capturer les métriques :
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.
Tableau des résultats (la visualisation est omise car les circuits de sortie sont très volumineux) :
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]
Visualiser les performances en fonction de l'index du circuit :
plot_transpilation_metrics(
results_ham, "Transpilation Metrics for Hamiltonian Circuits"
)

Visualiser le pourcentage de circuits pour lesquels chaque méthode a obtenu les meilleurs résultats.
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)
Best-performing methods based on size:
sabre: 18 circuit(s)
rustiq: 14 circuit(s)
ai: 10 circuit(s)
Analyse des résultats de compilation des circuits hamiltoniens
Dans cette section, nous évaluons les performances de trois méthodes de transpilation — SABRE, le transpileur alimenté par l'IA et Rustiq — sur des circuits quantiques construits avec PauliEvolutionGate, couramment utilisés dans les tâches de simulation hamiltonienne.
Rustiq a obtenu les meilleurs résultats en moyenne en termes de profondeur de circuit**, atteignant une profondeur environ 20 % inférieure à celle de SABRE. Cela est attendu, car Rustiq est spécifiquement conçu pour synthétiser les opérations PauliEvolutionGate avec des stratégies de décomposition optimisées et à faible profondeur. De plus, le graphique de profondeur montre qu'à mesure que les circuits augmentent en taille et en complexité, Rustiq passe à l'échelle de manière plus efficace, maintenant une profondeur significativement inférieure à celle de l'IA et de SABRE sur les circuits plus grands.
Le transpileur IA a montré des performances solides et constantes en termes de profondeur de circuit, surpassant systématiquement SABRE sur la plupart des circuits. Cependant, il a engendré le temps d'exécution le plus élevé, en particulier sur les circuits de grande taille, ce qui peut limiter son applicabilité pour les charges de travail sensibles au temps. Sa capacité de passage à l'échelle en termes de temps d'exécution reste une limitation clé, même s'il offre des améliorations solides en profondeur.
SABRE, bien qu'il produise la profondeur moyenne la plus élevée, a atteint le nombre moyen de portes le plus bas, suivi de près par le transpileur IA. Cela correspond à la conception de l'heuristique SABRE, qui privilégie directement la minimisation du nombre de portes. Rustiq, malgré ses atouts en réduction de profondeur, a présenté le nombre moyen de portes le plus élevé, ce qui constitue un compromis notable à considérer dans les applications où la taille du circuit importe davantage que sa durée.
Résumé
Bien que le transpileur IA fournisse généralement de meilleurs résultats que SABRE, en particulier en termes de profondeur de circuit, la conclusion ne devrait pas simplement être « utilisez toujours le transpileur IA ». Il y a des nuances importantes à prendre en compte :
-
Le transpileur IA est généralement fiable et fournit des circuits optimisés en profondeur, mais il comporte des compromis en termes de temps d'exécution, ainsi que d'autres limitations, notamment les cartes de couplage prises en charge et les capacités de synthèse. Celles-ci sont détaillées dans la documentation du Qiskit Transpiler Service.
-
Dans certains cas, en particulier avec des circuits très grands ou spécifiques au matériel, le transpileur IA peut ne pas être aussi efficace. Dans ces cas, le transpileur SABRE par défaut reste extrêmement fiable et peut être davantage optimisé en ajustant ses paramètres (voir le tutoriel d'optimisation SABRE).
-
Il est également important de considérer la structure du circuit lors du choix d'une méthode. Par exemple,
rustiqest spécialement conçu pour les circuits impliquantPauliEvolutionGateet produit souvent les meilleures performances pour les problèmes de simulation hamiltonienne.
Recommandation :
Il n'existe pas de stratégie de transpilation universelle. Les utilisateurs sont encouragés à comprendre la structure de leur circuit et à tester plusieurs méthodes de transpilation — incluant l'IA, SABRE et des outils spécialisés comme Rustiq — afin de trouver la solution la plus efficace pour leur problème spécifique et les contraintes de leur matériel.
Étape 3 : Exécuter à l'aide des primitives Qiskit
Comme ce tutoriel est centré sur la transpilation, aucune expérience n'est exécutée sur un dispositif quantique. L'objectif est d'exploiter les optimisations de l'Étape 2 pour obtenir un circuit transpilé avec une profondeur et un nombre de portes réduits.
Étape 4 : Post-traiter et retourner le résultat dans le format classique souhaité
Puisqu'il n'y a pas d'exécution pour ce notebook, il n'y a pas de résultats à post-traiter.
Références
[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
[4] "Faster and shorter synthesis of Hamiltonian simulation circuits". T. Goubault de Brugière, S. Martiel et al. https://arxiv.org/abs/2404.03280