SQD y SKQD
En este capítulo, exploraremos cómo los ordenadores cuánticos y clásicos trabajan juntos para resolver uno de los retos más importantes de la ciencia: estimar con precisión la energía de las moléculas y los materiales.
Iskandar Sitdikov describe el enfoque algorítmico en el siguiente vídeo.
hamiltoniano
La clave de este problema es un operador matemático: el hamiltoniano, que representa la energía total de un sistema. A efectos computacionales, podemos considerar este hamiltoniano como una gran matriz. Las soluciones que buscamos, concretamente el estado fundamental del sistema, son los valores propios más bajos de esta matriz. Sin embargo, el problema es que, para los problemas prácticos, esta matriz hamiltoniana es muy grande. Crece exponencialmente con el tamaño del sistema, volviéndose rápidamente demasiado grande ( , donde es el número de qubits) incluso para que los superordenadores más potentes puedan almacenarlo o resolverlo directamente.
Para solucionar esto, usamos una estrategia súper útil que se llama método del subespacio. En lugar de abordar toda la matriz, seleccionamos de forma inteligente una pequeña parte relevante —un «subespacio»— que creemos que contiene la información más importante sobre la solución de baja energía que estamos buscando.
Una vez definido este pequeño subespacio mediante un conjunto de estados básicos , se proyecta el hamiltoniano completo sobre él para crear la nueva matriz más pequeña . Cada elemento de esta matriz se calcula a partir de los estados básicos del subespacio y el hamiltoniano original, como en . Esta pequeña matriz se puede diagonalizar fácilmente en un ordenador clásico, y los valores propios resultantes son nuestras energías estimadas.
Como se puede imaginar, el éxito de todo este enfoque depende en gran medida de la elección de un «buen» subespacio. Si nuestro subespacio no representa con precisión el verdadero estado fundamental, nuestra respuesta final será incorrecta. Aquí es donde entran en juego los ordenadores cuánticos: nos permiten preparar y tomar muestras de estados cuánticos complejos diseñados para identificar estos importantes subespacios. En el caso de problemas realmente enormes, como estructuras químicas complejas o sitios de unión, incluso la matriz proyectada puede seguir siendo difícil de diagonalizar. Por lo tanto, estos problemas son ideales para aprovechar las ventajas tanto de los recursos informáticos cuánticos como de los clásicos.
En las siguientes secciones, exploraremos dos algoritmos avanzados, SQD y SKQD, que aprovechan la mecánica cuántica para encontrar y construir estos subespacios. Para profundizar más, hay un curso completo sobre aprendizaje automático en IBM Quantum dedicado a estos temas en detalle. Para los fines de este curso, mantendremos la explicación a un nivel general.
Diagonalización cuántica basada en muestras
La diagonalización cuántica basada en muestras (SQD) es un potente algoritmo variacional que implementa el método del subespacio de forma cuántica. Evita procedimientos costosos y complejos, como las pruebas de Hadamard, utilizando un ordenador cuántico para preparar un estado de prueba y cadenas de bits de muestra, que definen el subespacio para la diagonalización clásica.
El algoritmo SQD se puede desglosar en los siguientes pasos:
Paso 1: Preparar el estado Ansatz
Sea el hamiltoniano en un qubit d . Aunque el estado fundamental real puede apoyarse en todos los estados básicos e es, el SQD es más eficaz en los casos en que el estado fundamental puede aproximarse bien mediante un subespacio disperso (un conjunto de cadenas de bits de tamaño polinomial).
Para construir este subespacio, comenzamos con un estado de entrada , como el estado de Hartree-Fock (HF) en química. A continuación, aplicamos un circuito cuántico parametrizado, , conocido como ansatz.
Este diagrama ilustra el objetivo de un buen ansatz. El ansatz prepara un estado cuántico cuyo soporte (el conjunto de estados básicos que lo componen) debería, idealmente, tener una gran superposición con el soporte del verdadero estado fundamental. Este circuito nos permite proyectar rápidamente el ansatz sobre estados de base computacional, que se utilizarán posteriormente en la diagonalización clásica. En otras palabras: no tenemos que adivinar un ansatz que sea el estado fundamental; solo necesitamos que contenga los mismos estados básicos. Entonces, la diagonalización clásica del hamiltoniano proyectado nos dará la superposición de estados base que mejor se aproxima al estado fundamental.
Paso 2: Muestrear el subespacio
Al tomar muestras del circuito preparado por el ansatz, obtenemos una colección de cadenas de bits, . Estas cadenas de bits definen la base de nuestro subespacio elegido. El tiempo de ejecución cuántico para este paso viene determinado por la profundidad del circuito y el número de muestras tomadas.
Paso 3: Proyectar y diagonalizar clásicamente
Utilizando las cadenas de bits muestreadas, proyectamos el hamiltoniano en el subespacio que abarcan. Para cada par de cadenas de bits , calculamos clásicamente el elemento de la matriz . Dado que los operadores de Pauli son dispersos, este paso es clásicamente eficiente para los hamiltonianos físicos. La pequeña matriz resultante se diagonaliza entonces en un procesador clásico para estimar el estado fundamental y su energía.
Paso 4: Optimizar el ansatz (opcional)
El proceso puede hacerse iterativo. Al tratar la energía estimada del estado fundamental como una función de coste, podemos optimizar los parámetros del circuito ( ) utilizando métodos como el descenso de gradiente para mejorar el ansatz y, a su vez, la aproximación de la energía en la siguiente iteración.
Ventajas clave de SQD
SQD ofrece varias funciones potentes que lo convierten en un candidato líder para demostrar la ventaja cuántica:
- Gran robustez frente al ruido: supongamos que el estado fundamental real se apoya solo en dos cadenas de bits. Si se muestrean, aunque su solapamiento con nuestro ansatz sea pequeño, la diagonalización les asignará los pesos adecuados e ignorará eficazmente todas las demás cadenas de bits extrañas y ruidosas que también podrían haber sido muestreadas. Este filtrado inherente hace que SQD sea especialmente tolerante al ruido.
- Verificabilidad clásica: a diferencia de QPE o VQA, SQD produce una aproximación clásica al estado fundamental. Esto significa que cualquier persona con acceso a la lista de cadenas de bits y sus pesos puede volver a calcular y verificar la estimación de energía directamente en un ordenador clásico.
El SQD ya se ha utilizado para estimar la energía de disociación en estado fundamental del N y las propiedades electrónicas de los [2Fe-2S][4Fe-4S] clústeres y [2], con circuitos de hasta 77 qubits y 10 570 puertas.
Comprueba tu comprensión
Verdadero o falso: SQD se puede aplicar a sistemas químicos.
Sí
Comprueba tu comprensión
Denominemos A al conjunto de todos los estados de la base computacional que conforman tu hipótesis. Denominemos G al conjunto de todos los estados de la base computacional que conforman el verdadero estado fundamental de tu sistema. ¿Cuál de las siguientes opciones corresponde a un «buen» enfoque? Seleccione todas las opciones que correspondan:
(a)
(b)
(c)
(d)
(c) y (d)
SKQD (Diagonalización cuántica de Krylov basada en muestras)
La diagonalización cuántica de Krylov basada en muestras (SKQD) es otro potente algoritmo cuántico basado en muestras que se basa en los principios de la SQD. Aunque su objetivo es el mismo —encontrar un buen subespacio para la diagonalización—, SKQD emplea un método más estructurado para generar las cadenas de bits, especialmente para problemas como los hamiltonianos de red.
La idea central de SKQD es que, en lugar de optimizar un circuito parametrizado para encontrar un buen ansatz, se puede converger de forma demostrable al estado fundamental mediante el muestreo de un conjunto de estados generados por la propia evolución temporal natural del sistema: el subespacio de Krylov. El algoritmo SKQD se puede desglosar en los siguientes pasos:
Paso 1: Construir el subespacio de Krylov con evolución temporal
El proceso comienza con un estado inicial . Es importante destacar que no es necesario que este estado inicial tenga una «buena» superposición con el estado fundamental. Solo tiene que ser «polinómicamente grande» o describirse mediante un polinomio en el tamaño del sistema. El propio algoritmo hará entonces que el estado se acerque cada vez más al estado fundamental del sistema. SKQD aplica el operador de evolución temporal, , durante distintos intervalos de tiempo. Esto da lugar a un conjunto de estados cuánticos diferentes, definidos como:
Esta colección de estados evolucionados en el tiempo forma una base de Krylov. Este paso es especialmente eficaz para hamiltonianos reticulares en los que el número de términos del hamiltoniano no es elevado. En el caso de los problemas químicos, esta evolución temporal puede dar lugar a circuitos muy profundos, por lo que a menudo se recomienda el SQD para esos casos.
Paso 2: Muestra de los estados de la base de Krylov
A continuación, se recogen muestras de cadenas de bits de cada uno de los diferentes estados ( ) del generador de números aleatorios ( ) preparado en el paso anterior. Todas estas cadenas de bits se agrupan para formar la base del subespacio.
Paso 3: Proyectar y diagonalizar clásicamente
Este paso es idéntico al de SQD. Las cadenas de bits recopiladas se utilizan para proyectar el hamiltoniano completo en el subespacio que abarcan. La pequeña matriz resultante, , se diagonaliza en un ordenador clásico para encontrar la energía del estado fundamental.
Ventajas y garantías clave de SKQD
El enfoque estructurado de SKQD ofrece ventajas únicas:
-
Convergencia demostrable: la ventaja clave de SKQD es su garantía teórica de convergencia en condiciones específicas y bien definidas. Si el estado fundamental real es escaso (puede aproximarse bien mediante un número polinómico de cadenas de bits) y la diferencia de energía con respecto al primer estado excitado no es demasiado pequeña, se ha demostrado que el método funciona de manera eficiente. En estas condiciones, SKQD garantiza que encontrará las cadenas de bits cruciales que componen el estado fundamental y puede aproximar la energía del estado fundamental con gran precisión. Esto solo requiere un número polinómico de experimentos y disparos cuánticos. Esta garantía sitúa el enfoque basado en muestras sobre una base teórica rigurosa, similar a métodos establecidos como la estimación de fase cuántica.
-
Ventajas compartidas con SQD: Al igual que SQD, SKQD también tiene la propiedad de robustez frente al ruido. En otras palabras, siempre que se disponga de todas las cadenas de bits correctas en el conjunto de cadenas de bits muestreadas, la diagonalización asigna un peso casi nulo a las cadenas de bits incorrectas, lo que hace que el procedimiento sea robusto frente al ruido. Además, dado que la solución se obtiene a partir de un HPC clásico, la energía de la solución es verificable de forma clásica.
En experimentos, SKQD se ha utilizado con hasta 70 qubits y miles de puertas para estudiar el estado fundamental de modelos Anderson complejos de 4 impurezas, logrando una excelente concordancia con métodos clásicos de vanguardia como DMRG. [1]
Comprueba tu comprensión
¿Qué parte del algoritmo SKQD lo hace más adecuado para problemas físicos como las redes de espín que para problemas químicos? ¿Por qué?
La evolución temporal requiere circuitos de Trotter, que son muy profundos para hamiltonianos complicados y no dispersos. Las interacciones de la red de espín se rigen por matrices de espín, equivalentes a las matrices de Pauli. Por lo tanto, los hamiltonianos para redes de espín tienden a expresarse de forma más compacta en matrices de Pauli, especialmente aquellos con interacciones entre vecinos más cercanos.
SQD y SKQD como computación heterogénea
Para resumirlo todo, podemos representar los algoritmos basados en muestras como una combinación de diferentes modelos de programación en un conjunto de recursos heterogéneos. Por ejemplo, podemos representar nuestro algoritmo como un flujo de trabajo de tareas.
Esta figura ilustra el flujo de trabajo fundamental de cuatro etapas. En primer lugar, tendremos una tarea para la preparación del circuito cuántico que se superpone con nuestro estado objetivo, seguida de una tarea de transpilación, que solo requiere recursos clásicos para su ejecución. A continuación, se presentará una tarea que utiliza primitivas para ejecutar nuestro circuito cuántico, lo que requiere recursos cuánticos. Por último, tenemos una tarea de posprocesamiento, que en sí misma podría ser un algoritmo de diagonalización paralelo que se ejecuta en varios nodos.
Además, es posible que queramos ejecutar uno de estos algoritmos muchas veces mientras variamos nuestro ansatz, o que queramos ejecutarlos completamente en paralelo con diferentes poblaciones.
Esquema de 
Como se muestra arriba, es posible ejecutar varios flujos de trabajo simultáneamente mientras se realiza lo siguiente:
- Variar los parámetros o la estructura del ansatz para encontrar el más eficaz.
- Comenzar con diferentes estados o configuraciones iniciales («poblaciones») para evitar mínimos locales y garantizar un resultado más sólido.
Este enfoque multicapa, que combina la heterogeneidad basada en tareas con el paralelismo a nivel de flujo de trabajo, es clave para liberar todo el potencial de estos algoritmos.
Práctica de programación
Practiquemos el algoritmo SKQD, demostrando el flujo de trabajo heterogéneo descrito anteriormente. El proceso se divide en cuatro etapas distintas, cada una con su propio script Python y un script de shell correspondiente para el envío de trabajos.
Mapeo (mapping.py y mapping.sh)
El primer paso en nuestro flujo de trabajo es definir el problema físico y asignarlo a un conjunto de circuitos cuánticos.
El mapping.py define los parámetros para un problema físico específico, en este caso, un modelo de impureza de Anderson con siete sitios de baño (n_bath = 7). Construye las integrales de un cuerpo (h1e) y dos cuerpos (h2e) que representan el hamiltoniano del sistema.
...
n_bath = 7 # number of bath sites
...
# One body matrix elements in the "position" basis
h1e = -t * np.diag(np.ones(n_bath), k=1) - t * np.diag(np.ones(n_bath), k=-1)
h1e[impurity_index, impurity_index + 1] = -V
h1e[impurity_index + 1, impurity_index] = -V
h1e[impurity_index, impurity_index] = eps
# Two body matrix elements in the "position" basis
h2e = np.zeros((n_bath + 1, n_bath + 1, n_bath + 1, n_bath + 1))
h2e[impurity_index, impurity_index, impurity_index, impurity_index] = U
...
# The one-body time evolution
free_fermion_evolution = ffsim.qiskit.OrbitalRotationJW(n_modes, Utar)
# The two-body time evolution
def append_diagonal_evolution(dt, U, impurity_qubit, num_orb, q_circuit):
"""Append two-body time evolution to a quantum circuit."""
if U != 0:
q_circuit.append(
CPhaseGate(-dt / 2 * U),
[impurity_qubit, impurity_qubit + num_orb],
)
A continuación, genera los circuitos cuánticos necesarios para el algoritmo SKQD. Comienza creando un estado inicial (initial_state) y luego aplica operadores de evolución temporal para un número variable de pasos (d = 8) con el fin de generar los diferentes estados de la base de Krylov, .
# The reference state
def initial_state(q_circuit, norb, nocc):
"""Prepare an initial state."""
for i in range(nocc):
q_circuit.append(XGate(), [i])
q_circuit.append(XGate(), [norb + i])
rot = XXPlusYYGate(np.pi / 2, -np.pi / 2)
for i in range(3):
for j in range(nocc - i - 1, nocc + i, 2):
q_circuit.append(rot, [j, j + 1])
q_circuit.append(rot, [norb + j, norb + j + 1])
q_circuit.append(rot, [j + 1, j + 2])
q_circuit.append(rot, [norb + j + 1, norb + j + 2])
...
# Generate the initial state
qubits = QuantumRegister(2 * n_modes, name="q")
init_state = QuantumCircuit(qubits)
initial_state(init_state, n_modes, n_modes // 2)
...
d = 8 # Number of Krylov basis states
circuits = []
for i in range(d):
circ = init_state.copy()
circuits.append(circ)
for _ in range(i):
append_diagonal_evolution(dt, U, impurity_index, n_modes, circ)
circ.append(free_fermion_evolution, qubits)
append_diagonal_evolution(dt, U, impurity_index, n_modes, circ)
circ.measure_all()
print(circuits[0].draw(scale=0.4, fold=-1))
El script guarda la lista de los 8 circuitos generados (cada uno con las mediciones adjuntas) en un archivo llamado circuits.qpy.
El mapping.sh es un script por lotes de Slurm que se utiliza para enviar el mapping.py trabajo. Dado que se trata de un cálculo clásico, solicita recursos de una partición de CPU estándar (--partition=normal).
#!/bin/bash
#
#SBATCH --job-name=sqd-mapping
#SBATCH --output=sqd-mapping.out
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=normal
srun python /data/ch4/sqd/mapping.pyOptimización (optimization.py y optimization.sh)
Una vez que tenemos nuestros circuitos, deben optimizarse y compilarse para que funcionen de manera eficiente en el hardware cuántico de destino.
En el optimization.py, este script primero carga el circuits.qpy archivo creado en la etapa de mapeo y trae la información de recursos cuánticos mediante QRMI(), un gestor de recursos cuánticos. A continuación, utiliza Qiskit generate_preset_pass_manager con un alto nivel de optimización (optimization_level=3) para convertir los circuitos lógicos abstractos en circuitos de arquitectura de conjunto de instrucciones (ISA). Este proceso reescribe los circuitos utilizando las puertas nativas del hardware y los optimiza para reducir la profundidad y minimizar los errores.
...
qrmi = QRMI()
resources = qrmi.resources()
quantum_resource = resources[0]
target = quantum_resource.target
pass_manager = generate_preset_pass_manager(
optimization_level=3,
target=target
)
isa_circuits = pass_manager.run(circuits)
Los circuitos transpilados y listos para el hardware se guardan en un nuevo archivo, isa_circuits.qpy.
Al igual que el script de mapeo, este trabajo Slurm también se ejecuta en una partición de CPU clásica (--partition=normal), ya que la transpilación es una tarea clásica.
#!/bin/bash
#
#SBATCH --job-name=sqd-mapping
#SBATCH --output=sqd-mapping.out
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=normal
srun python /data/ch4/sqd/mapping.pyEjecución (execution.py y execution.sh)
Esta es la única etapa en la que se utiliza un ordenador cuántico. Aquí, ejecutamos los circuitos optimizados y recopilamos muestras de medición.
Carga el execution.py archivo isa_circuits.qpy optimizado y, a continuación, inicializa una SamplerV2 primitiva conectada a un recurso cuántico. A continuación, llama sampler.run() a para ejecutar los circuitos en la QPU durante un número específico de disparos (shots=500).
...
qrmi = QRMI()
resources = qrmi.resources()
quantum_resource = resources[0]
# Sample from the circuits
noisy_sampler = Sampler(quantum_resource)
job = noisy_sampler.run(isa_circuits, shots=500)
Al final de la ejecución, se recopilan y combinan los resultados medidos (cadenas de bits) de todos los circuitos, y sus recuentos se guardan en un counts.json archivo.
El script Slurm execution.sh es diferente de los demás en esta etapa. Solicita ejecutarse en la partición cuántica (--partition=quantum) y solicita específicamente una QPU (--gres=qpu:1).
#!/bin/bash
#
#SBATCH --job-name=sqd-execution
#SBATCH --output=sqd-execution.out
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=quantum
#SBATCH --gres=qpu:1
srun python /data/ch4/sqd/execution.pyPostprocesamiento (postprocessing.py y postprocessing.sh)
En el último paso, volvemos a un ordenador clásico para analizar los datos del experimento cuántico y calcular el resultado final: la energía del estado fundamental de nuestro sistema objetivo.
El postprocessing.py programa lee primero el counts.json archivo que contiene los resultados de las mediciones. A continuación, reconstruye el hamiltoniano del modelo de Anderson (utilizando los mismos parámetros que en mapping.py). A continuación, pasa las cadenas de bits medidas y la definición hamiltoniana a la diagonalize_fermionic_hamiltonian función. Esta función realiza la lógica central de SKQD: utiliza las cadenas de bits para construir el hamiltoniano proyectado y lo diagonaliza para encontrar la energía del estado fundamental.
...
def callback(results: list[SCIResult]):
result_history.append(results)
iteration = len(result_history)
print(f"Iteration {iteration}")
for i, result in enumerate(results):
print(f"\tSubsample {i}")
print(f"\t\tEnergy: {result.energy}")
print(f"\t\tSubspace dimension: {np.prod(result.sci_state.amplitudes.shape)}")
rng = np.random.default_rng(24)
result = diagonalize_fermionic_hamiltonian(
h1e,
h2e,
bit_array,
samples_per_batch=300,
norb=n_modes,
nelec=nelec,
num_batches=3,
max_iterations=10,
symmetrize_spin=True,
callback=callback,
seed=rng,
)
Por último, esto imprime la energía SKQD calculada y la compara con la energía exacta conocida para este problema, mostrando el error absoluto final del cálculo.
El script final del trabajo se ejecuta en una partición clásica (--partition=normal), ya que todo el análisis es clásico. Para subespacios grandes, este paso podría requerir más recursos clásicos de HPC.
#!/bin/bash
#
#SBATCH --job-name=sqd-postprocessing
#SBATCH --output=sqd-postprocessing.out
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=1
#SBATCH --partition=normal
srun python /data/ch4/sqd/postprocessing.pyResumen
Esto es todo. Ahora hemos repasado varios conceptos y ejemplos que pueden ayudarle a empezar con la gestión de programas híbridos complejos. Por supuesto, esto es solo el comienzo de todo lo que se puede hacer con la combinación de recursos cuánticos y clásicos de HPC.
Para explorar más casos de uso y algoritmos, consulte nuestra documentación y tutoriales en IBM Quantum Platform, y no olvide visitar los recursos compartidos en la siguiente lección para obtener más información sobre algoritmos y software tanto para científicos computacionales como para administradores de centros de datos.
Referencias
[1] Algoritmo centrado en la cuántica para la diagonalización de Krylov basada en muestras. https://arxiv.org/abs/2501.09702
[2] Química más allá de la escala de la diagonalización exacta en un superordenador centrado en la cuántica. https://www.science.org/doi/10.1126/sciadv.adu9991