Dashboard Core — VQE¶
Real, dynamically-generated VQE ansatz circuits for molecular
Hamiltonians — no fixed/hardcoded rotation angles. Every circuit this
module returns is built from the actual molecule's own qubit count and
Hamiltonian, run through dense_evolution.autodiff's
gradient engine.
vqe ¶
Real, dynamically-generated VQE ansatz circuits for molecular Hamiltonians -- no fixed/hardcoded rotation angles. Every circuit this module returns is produced by an actual classical optimization run against the real molecular Hamiltonian for the requested geometry/mapping, not a stored constant.
Two real ansatz families:
- hardware-efficient (Kandala et al., Nature 2017): a Hartree-Fock computational-basis initial state, then n_layers of single-qubit RY rotations followed by a linear CNOT entangling ladder. Generic -- doesn't know anything about the molecule's own fermionic structure, just a NISQ-friendly template. Optimized entirely on dense_evolution's own engine: the ansatz is built as real OpenQASM, parsed with dense_evolution.QASMParser, and turned into a JAX-differentiable energy function via dense_evolution.autodiff.circuit_to_energy_fn (the exact pattern already used and tested in this project's own feature/streamlit-dashboard history, dashboard_core/vqe_engine.py -- reused here without its unrelated QM/MM-telemetry code, not reinvented). A hand-rolled Adam loop (jax.value_and_grad, jax.jit) optimizes it -- no PennyLane device/QNode/optimizer involved at all for this ansatz; PennyLane's only remaining role anywhere in this module is the real Hartree-Fock + Jordan-Wigner mapping itself (dashboard_core.hamiltonians), which isn't something worth reimplementing (see research/quantum_chemistry_vqe_pipeline.md). Verified to match the PennyLane-optimized version's convergence (same order of residual error against the exact energy, same physics).
- UCCSD (Unitary Coupled-Cluster Singles and Doubles): the standard chemically-motivated VQE ansatz. Built from the molecule's real single/double fermionic excitation operators (qml.qchem.excitations), applied to the Hartree-Fock reference via qml.UCCSD (which internally exponentiates each excitation as a FermionicSingleExcitation / FermionicDoubleExcitation -- Givens-rotation-equivalent operators, not a generic template). Fewer parameters than hardware-efficient for the same molecule (H2: 3 vs 32), and converges to the exact energy faster because the ansatz form actually matches the physics. Also optimized entirely on dense_evolution's own engine, same as hardware-efficient -- the obstacle was that PennyLane's own decomposition of qml.UCCSD reuses each of the few real weights across several RX/RZ gates per excitation (Trotter exponentiation of that excitation's several Pauli-string terms), whereas circuit_to_energy_fn treats every parametric gate occurrence as an independent free parameter. Solved with an affine parameter expansion (_uccsd_native_expansion): probing PennyLane's own decomposition at weights=0 and at each basis vector gives a fixed (baseline, expansion_matrix) pair such that full_gate_values = baseline + expansion_matrix @ real_weights exactly reproduces PennyLane's own per-gate values for any weights (verified by direct probing, not derived from theory) -- composed with circuit_to_energy_fn this is still JAX-differentiable in the small real weight vector by ordinary chain rule, so the same hand-rolled Adam loop optimizes it with no PennyLane device/QNode/optimizer involved.
The Hartree-Fock initial state (computed via qml.qchem.hf_state) only has a simple X-gate encoding under the Jordan-Wigner mapping, so VQE generation here is JW-only. Bravyi-Kitaev stays available for exact ground-state-energy queries in hamiltonians.py, where the eigenvalue spectrum is mapping-invariant.
UCCSD's FermionicSingleExcitation/FermionicDoubleExcitation don't have a
one-line OpenQASM equivalent, so the converged circuit is decomposed via
PennyLane's own tape.expand() into RX/RY/RZ/CNOT (verified: executing
the resulting QASM on dense_evolution.DenseSVSimulator and recomputing
run_vqe ¶
run_vqe(
symbols,
geometry,
charge=0,
ansatz_type="hardware_efficient",
n_layers=8,
maxiter=200,
step_size=0.1,
beta1=0.9,
beta2=0.999,
active_electrons=None,
active_orbitals=None,
seed=0,
)
Runs a real VQE optimization (hand-rolled Adam over dense_evolution's own JAX-differentiable circuit_to_energy_fn, no PennyLane optimizer/device involved) for the molecule's Jordan-Wigner qubit Hamiltonian. step_size/beta1/beta2 are Adam's own real hyperparameters (learning rate and first/second moment decay), not cosmetic -- they change the real optimization trajectory computed below, the same way they would in any other Adam implementation. ansatz_type is "hardware_efficient" (generic, n_layers deep) or "uccsd" (chemically motivated, real fermionic single/double excitations -- n_layers is ignored, the parameter count comes from the molecule's own occupied/ virtual orbital structure). Returns a dict with the real energy convergence trace, the final variational energy, the exact ground- state energy (dense diagonalization -- feasible for every qubit count this function is meant to be called with, capped by the caller's active-space choice), and the OpenQASM circuit for the converged parameters.
maxiter=0 (or, for hardware_efficient, n_layers=0) is a real fast path, not a special case faked up separately: with zero ansatz parameters there's nothing for Adam to optimize, so this returns the bare Hartree-Fock reference circuit and its (real, exact) HF energy immediately -- the "pick a molecule, get a circuit" mechanic the UI uses before committing to a minutes-long optimization.
Source code in tools/dashboard_core/vqe.py
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | |
See also: dashboard_core.hamiltonians
for where the molecular Hamiltonian this ansatz optimizes against comes
from.