Métodos de compilación para circuitos de simulación hamiltoniana
Uso estimado de QPU: no se realizó ninguna ejecución en este tutorial porque se centra en el proceso de transpilación.
Contexto
La compilación de circuitos cuánticos es un paso crucial en el flujo de trabajo de la computación cuántica. Implica transformar un algoritmo cuántico de alto nivel en un circuito cuántico físico que cumpla con las restricciones del hardware cuántico objetivo. Una compilación eficaz puede impactar significativamente el rendimiento de los algoritmos cuánticos al reducir la profundidad del circuito, el número de compuertas y el tiempo de ejecución. Este tutorial explora tres enfoques distintos para la compilación de circuitos cuánticos en Qiskit, mostrando sus fortalezas y aplicaciones a través de ejemplos prácticos.
El objetivo de este tutorial es enseñar a los usuarios cómo aplicar y evaluar tres métodos de compilación en Qiskit: el transpilador SABRE, el transpilador potenciado por IA y el plugin Rustiq. Los usuarios aprenderán cómo utilizar cada método de manera efectiva y cómo comparar su rendimiento en diferentes circuitos cuánticos. Al finalizar este tutorial, los usuarios podrán elegir y adaptar estrategias de compilación basadas en objetivos de optimización específicos, como reducir la profundidad del circuito, minimizar el número de compuertas o mejorar el tiempo de ejecución.
Lo que aprenderás
- Cómo utilizar el transpilador de Qiskit con SABRE para la optimización de distribución y enrutamiento.
- Cómo aprovechar el transpilador de IA para la optimización avanzada y automatizada de circuitos.
- Cómo emplear el plugin Rustiq para circuitos que requieren síntesis precisa de operaciones, particularmente en tareas de simulación hamiltoniana.
Este tutorial utiliza tres circuitos de ejemplo siguiendo el flujo de trabajo de patrones de Qiskit para ilustrar el rendimiento de cada método de compilación. Al finalizar este tutorial, los usuarios estarán preparados para elegir la estrategia de compilación adecuada según sus requisitos y restricciones específicas.
Descripción general de los métodos de compilación
1. Transpilador de Qiskit con SABRE
El transpilador de Qiskit utiliza el algoritmo SABRE (SWAP-based BidiREctional heuristic search) para optimizar la distribución y el enrutamiento del circuito. SABRE se enfoca en minimizar las compuertas SWAP y su impacto en la profundidad del circuito, respetando las restricciones de conectividad del hardware. Este método es altamente versátil y adecuado para la optimización de circuitos de propósito general, proporcionando un equilibrio entre rendimiento y tiempo de cómputo. Para aprovechar las últimas mejoras en SABRE, detalladas en [1], puede aumentar el número de intentos (por ejemplo, layout_trials=400, swap_trials=400). Para los propósitos de este tutorial, utilizaremos los valores predeterminados para el número de intentos con el fin de comparar con el transpilador predeterminado de Qiskit. Las ventajas y la exploración de parámetros de SABRE se cubren en un tutorial de profundización separado.
2. Transpilador de IA
El transpilador potenciado por IA en Qiskit utiliza aprendizaje automático para predecir estrategias óptimas de transpilación, analizando patrones en la estructura del circuito y las restricciones del hardware para seleccionar la mejor secuencia de optimizaciones para una entrada dada. Este método es particularmente efectivo para circuitos cuánticos a gran escala, ofreciendo un alto grado de automatización y adaptabilidad a diversos tipos de problemas. Además de la optimización general de circuitos, el transpilador de IA puede utilizarse con el pase AIPauliNetworkSynthesis, que se dirige a circuitos de redes de Pauli — bloques compuestos por compuertas H, S, SX, CX, RX, RY y RZ — y aplica un enfoque de síntesis basado en aprendizaje por refuerzo. Para más información sobre el transpilador de IA y sus estrategias de síntesis, consulta [2] y [3].
3. Plugin Rustiq
El plugin Rustiq introduce técnicas avanzadas de síntesis específicamente para operaciones PauliEvolutionGate, que representan rotaciones de Pauli comúnmente utilizadas en dinámicas trotterizadas. Este plugin es valioso para circuitos que implementan simulación hamiltoniana, como los utilizados en problemas de química cuántica y física, donde las rotaciones de Pauli precisas son esenciales para simular efectivamente los hamiltonianos del problema. Rustiq ofrece síntesis de circuitos precisa y de baja profundidad para estas operaciones especializadas. Para más detalles sobre la implementación y el rendimiento de Rustiq, consulta [4].
Al explorar estos métodos de compilación en profundidad, este tutorial proporciona a los usuarios las herramientas para mejorar el rendimiento de tus circuitos cuánticos, allanando el camino hacia computaciones cuánticas más eficientes y prácticas.
Requisitos
Antes de comenzar este tutorial, asegúrate de tener instalado lo siguiente:
- Qiskit SDK v1.3 o posterior, con soporte de visualización
- Qiskit Runtime v0.28 o posterior (
pip install qiskit-ibm-runtime) - Qiskit IBM Transpiler (
pip install qiskit-ibm-transpiler) - Qiskit AI Transpiler modo local (
pip install qiskit_ibm_ai_local_transpiler) - Biblioteca de grafos Networkx (
pip install networkx)
Configuración
# 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
Paso 1: Mapear entradas clásicas a un problema cuántico
En esta sección, exploramos el circuito efficient_su2, un ansatz eficiente en hardware comúnmente utilizado en algoritmos cuánticos variacionales (como VQE) y tareas de aprendizaje automático cuántico. El circuito consiste en capas alternadas de rotaciones de un solo qubit y compuertas de entrelazamiento dispuestas en un patrón circular, diseñado para explorar el espacio de estados cuánticos de manera efectiva manteniendo una profundidad manejable.
Comenzaremos construyendo un circuito efficient_su2 para demostrar cómo comparar diferentes métodos de compilación. Después de la Parte 1, expandiremos nuestro análisis a un conjunto más grande de circuitos, permitiendo una comparación integral para evaluar el rendimiento de varias técnicas de compilación.
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")
Paso 2: Optimizar el problema para la ejecución en hardware cuántico
Este paso es el enfoque principal del tutorial. Aquí, nuestro objetivo es optimizar los circuitos cuánticos para una ejecución eficiente en hardware cuántico real. Nuestro objetivo principal es reducir la profundidad del circuito y el número de compuertas, que son factores clave para mejorar la fidelidad de ejecución y mitigar el ruido del hardware.
- Transpilador SABRE: Utiliza el transpilador predeterminado de Qiskit con el algoritmo de distribución y enrutamiento SABRE.
- Transpilador de IA (modo local): El transpilador estándar potenciado por IA que utiliza inferencia local y la estrategia de síntesis predeterminada.
- Plugin Rustiq: Un plugin de transpilación diseñado para compilación de baja profundidad, adaptado a tareas de simulación hamiltoniana.
El objetivo de este paso es comparar los resultados de estos métodos en términos de la profundidad del circuito transpilado y el número de compuertas. Otra métrica importante que consideramos es el tiempo de ejecución de la transpilación. Al analizar estas métricas, podemos evaluar las fortalezas relativas de cada método y determinar cuál produce el circuito más eficiente para la ejecución en el hardware seleccionado.
Nota: Para el ejemplo inicial del circuito SU2, solo compararemos el transpilador SABRE con el transpilador de IA predeterminado. Sin embargo, en la comparación posterior utilizando circuitos Hamlib, compararemos los tres métodos de transpilación.
# 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')>
Transpilador de Qiskit con SABRE:
pm_sabre = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=seed
)
Transpilador de 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,
)
Transpilar y capturar métricas
Para comparar el rendimiento de los métodos de compilación, definimos una función que transpila el circuito de entrada y captura métricas relevantes de manera consistente. Esto incluye la profundidad total del circuito, el número total de compuertas y el tiempo de transpilación.
Además de estas métricas estándar, también registramos la profundidad de compuertas de 2 qubits, que es una métrica particularmente importante para evaluar la ejecución en hardware cuántico. A diferencia de la profundidad total, que incluye todas las compuertas, la profundidad de 2 qubits refleja con mayor precisión la duración real de ejecución del circuito en el hardware. Esto se debe a que las compuertas de 2 qubits típicamente dominan el presupuesto de tiempo y error en la mayoría de los dispositivos cuánticos. Por lo tanto, minimizar la profundidad de 2 qubits es fundamental para mejorar la fidelidad y reducir los efectos de decoherencia durante la ejecución.
Utilizaremos esta función para analizar el rendimiento de los diferentes métodos de compilación en múltiples circuitos.
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.
Mostrar los resultados transpilados de uno de los circuitos.
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

Tabla de resultados:
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
Gráfico de resultados
Así como definimos una función para capturar métricas de manera consistente, también definiremos una para graficar las métricas. Aquí, graficaremos la profundidad de dos qubits, el número de compuertas y el tiempo de ejecución para cada método de compilación a lo largo de los circuitos.
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"
)

Análisis de los resultados de compilación del circuito SU2
En este experimento, comparamos dos métodos de transpilación — el transpilador SABRE de Qiskit y el transpilador potenciado por IA — en un conjunto de circuitos efficient_su2. Dado que estos circuitos no incluyen ninguna operación PauliEvolutionGate, el plugin Rustiq no se incluye en esta comparación.
En promedio, el transpilador de IA obtiene mejores resultados en términos de profundidad de circuito, con una mejora superior al 10% en todo el rango de circuitos SU2. Para el número de compuertas (tamaño del circuito) y el tiempo de ejecución de la transpilación, ambos métodos producen resultados similares en general.
Sin embargo, al inspeccionar los puntos de datos individuales se revela una perspectiva más profunda:
- Para la mayoría de los tamaños de qubits, tanto SABRE como IA producen resultados casi idénticos, lo que sugiere que en muchos casos, ambos métodos convergen hacia soluciones igualmente eficientes.
- Para ciertos tamaños de circuito, específicamente en 30, 50, 70 y 90 qubits, el transpilador de IA encuentra circuitos significativamente menos profundos que SABRE. Esto indica que el enfoque basado en aprendizaje de la IA es capaz de descubrir distribuciones o rutas de enrutamiento más óptimas en casos donde la heurística de SABRE no lo logra.
Este comportamiento destaca una conclusión importante:
Aunque SABRE y la IA frecuentemente producen resultados comparables, el transpilador de IA puede ocasionalmente descubrir soluciones mucho mejores, particularmente en términos de profundidad, lo que puede conducir a un rendimiento significativamente mejorado en el hardware.
Parte 2: Circuito de simulación hamiltoniana
Paso 1: Investigar circuitos con PauliEvolutionGate
En esta sección, investigamos circuitos cuánticos construidos utilizando PauliEvolutionGate, que permite la simulación eficiente de hamiltonianos. Analizaremos cómo los diferentes métodos de compilación optimizan estos circuitos a través de varios hamiltonianos.
Hamiltonianos utilizados en la comparación
Los hamiltonianos utilizados en esta comparación describen interacciones por pares entre qubits, incluyendo términos como , e . Estos hamiltonianos se utilizan comúnmente en química cuántica, física de materia condensada y ciencia de materiales, donde modelan sistemas de partículas interactuantes.
Como referencia, los usuarios pueden explorar un conjunto más amplio de hamiltonianos en este artículo: Efficient Hamiltonian Simulation on Noisy Quantum Devices.
Fuente de la comparación: Hamlib y Benchpress
Los circuitos utilizados en esta comparación provienen del repositorio de comparación Hamlib, que contiene cargas de trabajo realistas de simulación hamiltoniana.
Estos mismos circuitos fueron previamente evaluados utilizando Benchpress, un marco de código abierto para evaluar el rendimiento de transpilación cuántica. Al utilizar este conjunto estandarizado de circuitos, podemos comparar directamente la efectividad de diferentes estrategias de compilación en problemas de simulación representativos.
La simulación hamiltoniana es una tarea fundamental en la computación cuántica, con aplicaciones en simulaciones moleculares, problemas de optimización y física cuántica de muchos cuerpos. Comprender cómo los diferentes métodos de compilación optimizan estos circuitos puede ayudar a los usuarios a mejorar la ejecución práctica de dichos circuitos en dispositivos cuánticos de corto plazo.
# 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
Paso 2: Optimizar el problema para la ejecución en hardware cuántico
Como en el ejemplo anterior, utilizaremos el mismo backend para garantizar la consistencia en nuestras comparaciones. Dado que los gestores de pases (pm_sabre, pm_ai y pm_rustiq) ya han sido inicializados, podemos proceder directamente con la transpilación de los circuitos hamiltonianos utilizando cada método.
Este paso se enfoca únicamente en realizar la transpilación y registrar las métricas resultantes del circuito, incluyendo la profundidad, el número de compuertas y el tiempo de ejecución de la transpilación. Al analizar estos resultados, buscamos determinar la eficiencia de cada método de transpilación para este tipo de circuito. Transpilar y capturar métricas:
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.
Tabla de resultados (se omite la visualización ya que los circuitos de salida son muy grandes):
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]
Visualizar el rendimiento basado en el índice del circuito:
plot_transpilation_metrics(
results_ham, "Transpilation Metrics for Hamiltonian Circuits"
)

Visualizar el porcentaje de circuitos para los cuales cada método obtuvo el mejor rendimiento.
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)
Análisis de los resultados de compilación de circuitos hamiltonianos
En esta sección, evaluamos el rendimiento de tres métodos de transpilación — SABRE, el transpilador potenciado por IA y Rustiq — en circuitos cuánticos construidos con PauliEvolutionGate, que se utilizan comúnmente en tareas de simulación hamiltoniana.
Rustiq obtuvo el mejor rendimiento promedio en términos de profundidad de circuito**, logrando aproximadamente un 20% menos de profundidad que SABRE. Esto es esperado, ya que Rustiq está específicamente diseñado para sintetizar operaciones PauliEvolutionGate con estrategias de descomposición optimizadas y de baja profundidad. Además, el gráfico de profundidad muestra que a medida que los circuitos escalan en tamaño y complejidad, Rustiq escala de manera más efectiva, manteniendo una profundidad significativamente menor que tanto IA como SABRE en circuitos más grandes.
El transpilador de IA mostró un rendimiento sólido y consistente en la profundidad del circuito, superando consistentemente a SABRE en la mayoría de los circuitos. Sin embargo, incurrió en el mayor tiempo de ejecución, especialmente en circuitos más grandes, lo que puede limitar su practicidad en cargas de trabajo sensibles al tiempo. Su escalabilidad en tiempo de ejecución sigue siendo una limitación clave, aunque ofrece mejoras sólidas en profundidad.
SABRE, aunque produjo la mayor profundidad promedio, logró el menor número promedio de compuertas, seguido de cerca por el transpilador de IA. Esto se alinea con el diseño de la heurística de SABRE, que prioriza la minimización directa del número de compuertas. Rustiq, a pesar de su fortaleza en reducir la profundidad, tuvo el mayor número promedio de compuertas, lo cual es una compensación notable a considerar en aplicaciones donde el tamaño del circuito importa más que la duración del circuito.
Resumen
Aunque el transpilador de IA generalmente ofrece mejores resultados que SABRE, particularmente en la profundidad del circuito, la conclusión no debería ser simplemente "usar siempre el transpilador de IA". Hay matices importantes a considerar:
-
El transpilador de IA es típicamente confiable y proporciona circuitos optimizados en profundidad, pero conlleva compensaciones en tiempo de ejecución, y también tiene otras limitaciones, incluyendo mapas de acoplamiento soportados y capacidades de síntesis. Estas se detallan en la documentación del Servicio de Transpilación de Qiskit.
-
En algunos casos, particularmente con circuitos muy grandes o específicos del hardware, el transpilador de IA puede no ser tan efectivo. En estos casos, el transpilador SABRE predeterminado sigue siendo extremadamente confiable y puede optimizarse aún más ajustando sus parámetros (consulta el tutorial de optimización de SABRE).
-
También es importante considerar la estructura del circuito al elegir un método. Por ejemplo,
rustiqestá diseñado específicamente para circuitos que involucranPauliEvolutionGatey frecuentemente produce el mejor rendimiento para problemas de simulación hamiltoniana.
Recomendación:
No existe una estrategia de transpilación única que sirva para todo. Se recomienda a los usuarios que comprendan la estructura de tu circuito y prueben múltiples métodos de transpilación — incluyendo IA, SABRE y herramientas especializadas como Rustiq — para encontrar la solución más eficiente para su problema específico y las restricciones de tu hardware.
Paso 3: Ejecutar utilizando primitivas de Qiskit
Dado que este tutorial se centra en la transpilación, no se ejecutan experimentos en un dispositivo cuántico. El objetivo es aprovechar las optimizaciones del Paso 2 para obtener un circuito transpilado con profundidad y número de compuertas reducidos.
Paso 4: Post-procesar y devolver el resultado en el formato clásico deseado
Dado que no hay ejecución en este cuaderno, no hay resultados para post-procesar.
Referencias
[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