El Variational Quantum Eigensolver (VQE) es uno de los algoritmos cuánticos a corto plazo más importantes. Encuentra la energía del estado fundamental de una molécula o un material — un cálculo que es exponencialmente difícil para las computadoras clásicas pero abordable en dispositivos NISQ. Esta guía recorre una implementación completa de VQE en PennyLane.
Qué hace VQE
VQE encuentra el valor propio mínimo de un hamiltoniano H (que normalmente representa la energía de una molécula). Funciona mediante:
- Preparar un estado de prueba parametrizado |ψ(θ)⟩ usando un circuito cuántico
- Medir el valor esperado ⟨ψ(θ)|H|ψ(θ)⟩ en la QPU
- Usar un optimizador clásico para actualizar θ y minimizar la energía
- Repetir hasta la convergencia
El principio variacional garantiza que ⟨ψ(θ)|H|ψ(θ)⟩ ≥ E₀ para cualquier estado — por lo que minimizar esta cantidad da una cota superior de la verdadera energía del estado fundamental E₀.
Configuración
Instala PennyLane con su complemento de química:
pip install pennylane pennylane-qchem
Para la molécula de hidrógeno (H₂) — el benchmark clásico de VQE — necesitamos dos electrones y cuatro espín-orbitales (4 qubits):
import pennylane as qml
from pennylane import numpy as np
import pennylane.qchem as qchem
# H2 at equilibrium bond length (Angstrom)
symbols = ["H", "H"]
coordinates = np.array([[0.0, 0.0, -0.6614], [0.0, 0.0, 0.6614]])
# Build the qubit Hamiltonian
H, qubits = qchem.molecular_hamiltonian(
symbols,
coordinates,
basis="sto-3g"
)
print(f"Hamiltonian: {len(H.ops)} terms, {qubits} qubits")
# Hamiltonian: 15 terms, 4 qubits
Definiendo el ansatz
El ansatz es el circuito parametrizado que prepara el estado de prueba. Para problemas de química, el ansatz UCCSD (Unitary Coupled-Cluster Singles and Doubles) es el estándar:
# Get UCCSD circuit parameters
electrons = 2
singles, doubles = qchem.excitations(electrons, qubits)
s_wires, d_wires = qchem.excitations_to_wires(singles, doubles, wires=range(qubits))
# Initial Hartree-Fock state (reference state)
hf_state = qchem.hf_state(electrons, qubits)
dev = qml.device("default.qubit", wires=qubits)
@qml.qnode(dev)
def circuit(weights, wires, s_wires=[], d_wires=[], hf_state=hf_state):
# Prepare HF reference state
qml.BasisState(hf_state, wires=wires)
# Apply UCCSD excitations
qml.UCCSD(weights, wires, s_wires=s_wires, d_wires=d_wires, init_state=hf_state)
return qml.expval(H)
Ejecutando la optimización de VQE
Con la diferenciación automática de PennyLane, podemos usar optimizadores basados en gradientes directamente:
# Initial parameters (all zeros = Hartree-Fock state)
init_params = np.zeros(len(singles) + len(doubles), requires_grad=True)
# Adam optimizer (works well for VQE)
opt = qml.AdamOptimizer(stepsize=0.4)
# Optimization loop
energy_history = []
params = init_params.copy()
for step in range(200):
params, energy = opt.step_and_cost(
lambda p: circuit(p, range(qubits), s_wires=s_wires, d_wires=d_wires),
params
)
energy_history.append(energy)
if step % 20 == 0:
print(f"Step {step:3d}: E = {energy:.6f} Ha")
print(f"\nVQE ground state energy: {energy:.6f} Ha")
print(f"Reference (exact): -1.136189 Ha")
Salida típica:
Step 0: E = -1.117498 Ha
Step 20: E = -1.133254 Ha
Step 40: E = -1.135901 Ha
Step 60: E = -1.136140 Ha
...
VQE ground state energy: -1.136174 Ha
Reference (exact): -1.136189 Ha
VQE alcanza un valor dentro de ~0.015 mHa de la energía exacta — precisión química para H₂.
Usando un optimizador sin gradientes
Para hardware ruidoso, los optimizadores sin gradientes como COBYLA o SPSA suelen ser mejores, ya que los gradientes del hardware son ruidosos:
from scipy.optimize import minimize
# Objective function (no gradient needed)
def objective(params):
return float(circuit(params, range(qubits), s_wires=s_wires, d_wires=d_wires))
result = minimize(
objective,
x0=init_params,
method="COBYLA",
options={"maxiter": 500, "rhobeg": 0.1}
)
print(f"COBYLA energy: {result.fun:.6f} Ha")
Ejecutando VQE con HLQuantum
HLQuantum incluye una implementación de VQE integrada que funciona en todos los backends:
import hlquantum as hlq
# Define the Hamiltonian in HLQuantum's format
H = hlq.hamiltonians.h2_molecule(bond_length=1.32)
# Run VQE on any backend
result = hlq.algorithms.vqe(
hamiltonian=H,
ansatz="uccsd",
optimizer="adam",
max_iterations=200,
backend="pennylane", # or "qiskit", "cudaq"
)
print(f"Ground state energy: {result.energy:.6f} Ha")
print(f"Optimal parameters: {result.params}")
print(f"Converged in {result.iterations} iterations")
Consejos para hardware real
Al ejecutar VQE en QPUs reales (IBM Quantum, IonQ), se aplican varias consideraciones adicionales:
Usa menos shots por paso. 1000 shots por paso de optimización suele ser suficiente para la estimación del gradiente. No uses 10.000 shots en cada paso — desperdicia tiempo de QPU.
Empieza con circuitos poco profundos. Menos puertas CNOT = menos ruido. Para hardware, considera circuitos de ansatz eficientes en hardware en lugar de UCCSD.
Habilita la mitigación de errores. El error_mitigation="zne" de HLQuantum aplica Zero Noise Extrapolation, que puede mejorar significativamente los resultados en hardware ruidoso:
result = hlq.run(vqe_circuit, backend="qiskit", device="ibm_sherbrooke",
error_mitigation="zne", shots=2048)
Consulta la guía completa de PennyLane y la referencia de algoritmos de HLQuantum para más detalles sobre simulaciones de química cuántica.