Estimación de la energía del estado fundamental de la cadena de Heisenberg con VQE
Estimación de uso: Dos minutos en un procesador Eagle r3 (NOTA: Esto es solo una estimación. Su tiempo de ejecución puede variar.)
Contexto
Este tutorial muestra cómo construir, desplegar y ejecutar un Qiskit pattern para simular una cadena de Heisenberg y estimar su energía del estado fundamental. Para obtener más información sobre los Qiskit patterns y cómo se puede utilizar Qiskit Serverless para desplegarlos en la nube para una ejecución gestionada, visite nuestra página de documentación en IBM Quantum® Platform.
Requisitos
Antes de comenzar este tutorial, asegúrate de tener instalado lo siguiente:
- Qiskit SDK v1.2 o posterior, con soporte de visualización
- Qiskit Runtime v0.28 o posterior (
pip install qiskit-ibm-runtime) - Qiskit Serverless (pip install qiskit_serverless)
- IBM Catalog (pip install qiskit-ibm-catalog)
Configuración
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-catalog qiskit-ibm-runtime scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
from typing import Sequence
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives.base import BaseEstimatorV2
from qiskit.circuit.library import XGate
from qiskit.circuit.library import efficient_su2
from qiskit.transpiler import PassManager
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.transpiler.passes.scheduling import (
ALAPScheduleAnalysis,
PadDynamicalDecoupling,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session, Estimator
from qiskit_ibm_catalog import QiskitServerless, QiskitFunction
def visualize_results(results):
plt.plot(results["cost_history"], lw=2)
plt.xlabel("Iteration")
plt.ylabel("Energy")
plt.show()
def build_callback(
ansatz: QuantumCircuit,
hamiltonian: SparsePauliOp,
estimator: BaseEstimatorV2,
callback_dict: dict,
):
def callback(current_vector):
# Keep track of the number of iterations
callback_dict["iters"] += 1
# Set the prev_vector to the latest one
callback_dict["prev_vector"] = current_vector
# Compute the value of the cost function at the current vector
current_cost = (
estimator.run([(ansatz, hamiltonian, [current_vector])])
.result()[0]
.data.evs[0]
)
callback_dict["cost_history"].append(current_cost)
# Print to screen on single line
print(
"Iters. done: {} [Current cost: {}]".format(
callback_dict["iters"], current_cost
),
end="\r",
flush=True,
)
return callback
Paso 1: Mapear las entradas clásicas a un problema cuántico
- Entrada: Número de espines
- Salida: Ansatz y hamiltoniano que modelan la cadena de Heisenberg
Construye un ansatz y un hamiltoniano que modelen una cadena de Heisenberg de 10 espines. Primero, importamos algunos paquetes genéricos y creamos un par de funciones auxiliares.
num_spins = 10
ansatz = efficient_su2(num_qubits=num_spins, reps=3)
# Remember to insert your token in the QiskitRuntimeService constructor
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, min_num_qubits=num_spins, simulator=False
)
coupling = backend.target.build_coupling_map()
reduced_coupling = coupling.reduce(list(range(num_spins)))
edge_list = reduced_coupling.graph.edge_list()
ham_list = []
for edge in edge_list:
ham_list.append(("ZZ", edge, 0.5))
ham_list.append(("YY", edge, 0.5))
ham_list.append(("XX", edge, 0.5))
for qubit in reduced_coupling.physical_qubits:
ham_list.append(("Z", [qubit], np.random.random() * 2 - 1))
hamiltonian = SparsePauliOp.from_sparse_list(ham_list, num_qubits=num_spins)
ansatz.draw("mpl", style="iqp")

Paso 2: Optimizar el problema para la ejecución en hardware cuántico
- Entrada: Circuito abstracto, observable
- Salida: Circuito y observable objetivo, optimizados para la QPU seleccionada
Utiliza la función generate_preset_pass_manager de Qiskit para generar automáticamente una rutina de optimización para nuestro circuito con respecto a la QPU seleccionada. Elegimos optimization_level=3, que proporciona el nivel más alto de optimización de los gestores de pases predefinidos. También incluimos los pases de planificación ALAPScheduleAnalysis y PadDynamicalDecoupling para suprimir errores de decoherencia.
target = backend.target
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
pm.scheduling = PassManager(
[
ALAPScheduleAnalysis(durations=target.durations()),
PadDynamicalDecoupling(
durations=target.durations(),
dd_sequence=[XGate(), XGate()],
pulse_alignment=target.pulse_alignment,
),
]
)
ansatz_ibm = pm.run(ansatz)
observable_ibm = hamiltonian.apply_layout(ansatz_ibm.layout)
ansatz_ibm.draw("mpl", scale=0.6, style="iqp", fold=-1, idle_wires=False)

Paso 3: Ejecutar utilizando primitivas de Qiskit
- Entrada: Circuito y observable objetivo
- Salida: Resultados de la optimización
Minimiza la energía estimada del estado fundamental del sistema optimizando los parámetros del circuito. Utiliza la primitiva Estimator de Qiskit Runtime para evaluar la función de costo durante la optimización.
Para esta demostración, ejecutaremos en una QPU utilizando las primitivas de qiskit-ibm-runtime. Para ejecutar con las primitivas basadas en Statevector de qiskit, reemplace el bloque de código que utiliza las primitivas de Qiskit IBM Runtime con el bloque comentado.
# SciPy minimizer routine
def cost_func(
params: Sequence,
ansatz: QuantumCircuit,
hamiltonian: SparsePauliOp,
estimator: BaseEstimatorV2,
) -> float:
"""Ground state energy evaluation."""
return (
estimator.run([(ansatz, hamiltonian, [params])])
.result()[0]
.data.evs[0]
)
num_params = ansatz_ibm.num_parameters
params = 2 * np.pi * np.random.random(num_params)
callback_dict = {
"prev_vector": None,
"iters": 0,
"cost_history": [],
}
# Evaluate the problem on a QPU by using Qiskit IBM Runtime
with Session(backend=backend) as session:
estimator = Estimator()
callback = build_callback(
ansatz_ibm, observable_ibm, estimator, callback_dict
)
res = minimize(
cost_func,
x0=params,
args=(ansatz_ibm, observable_ibm, estimator),
callback=callback,
method="cobyla",
options={"maxiter": 100},
)
visualize_results(callback_dict)
Paso 4: Post-procesar y devolver el resultado en el formato clásico deseado
- Entrada: Estimaciones de la energía del estado fundamental durante la optimización
- Salida: Energía estimada del estado fundamental
print(f'Estimated ground state energy: {res["fun"]}')
Desplegar el patrón de Qiskit en la nube
Para hacer esto, mueva el código fuente anterior a un archivo, ./source/heisenberg.py, encapsule el código en un script que reciba entradas y devuelve la solución final, y finalmente cárguelo a un clúster remoto utilizando la clase QiskitFunction de qiskit-ibm-catalog. Para obtener orientación sobre cómo especificar dependencias externas, pasar argumentos de entrada y más, consulta las guías de Qiskit Serverless.
La entrada al patrón es el número de espines en la cadena. La salida es una estimación de la energía del estado fundamental del sistema.
# Authenticate to the remote cluster and submit the pattern for remote execution
serverless = QiskitServerless()
heisenberg_function = QiskitFunction(
title="ibm_heisenberg",
entrypoint="heisenberg.py",
working_dir="./source/",
)
serverless.upload(heisenberg_function)
Ejecutar el patrón de Qiskit como un servicio gestionado
Una vez que hemos cargado el patrón en la nube, podemos ejecutarlo fácilmente utilizando el cliente QiskitServerless.
# Run the pattern on the remote cluster
ibm_heisenberg = serverless.load("ibm_heisenberg")
job = serverless.run(ibm_heisenberg)
solution = job.result()
print(solution)
print(job.logs())
Encuesta del tutorial
Por favor, realiza esta breve encuesta para proporcionar comentarios sobre este tutorial. Sus opiniones nos ayudarán a mejorar nuestro contenido y la experiencia de usuario.