Real molecular Hamiltonians, built on demand from actual atomic geometry
— no fabricated or hand-picked coefficients. _get_hamiltonian dispatches
per molecule: PennyLane's own qchem pipeline (Hartree-Fock +
Jordan-Wigner) when every requested element is in PennyLane's bundled
STO-3G table (H2/HeH+/H3+/LiH/H2O), or Dense-Evolution's own
native Hartree-Fock engine otherwise — currently backing
Si2 (real equilibrium R = 2.184 Å, Balamurugan & Prasad,
arXiv:cond-mat/0108426), whose
elements PennyLane's dhf can't reach at all (its table stops at Ne).
Both paths hand off to the same PennyLane fermionic_observable +
jordan_wigner step, so the qubit mapping is identical either way. Backs
Composer's molecular-energy panel, dashboard_core.vqe,
and dashboard_core.qmmm.
Honest caveat on Si2: with only 4 active electrons/orbitals (all 20
core electrons frozen across both atoms), this active space is too small
to reproduce 2.184 Å as its own energy minimum — a direct 10-point
bond-length scan found the minimum at the 1.9 Å edge of the scanned
range, not an interior point. Stated in the catalog entry's own comment
rather than silently picking a geometry that flatters the active-space
choice.
Real molecular Hamiltonians, built on demand from actual atomic geometry
via PennyLane's qchem module (Hartree-Fock + Jordan-Wigner fermion-to-
qubit mapping, method='dhf' -- native to PennyLane, no PySCF/OpenFermion
dependency needed).
Ported from feature/streamlit-dashboard (git branch), where this was
built and verified against known values before the dashboard rebuild.
Only the real molecular catalog comes along here -- the old diagonal
"toy model" library and the VQE optimization loop stay out for now
(kept minimal on purpose, brought back separately if/when needed).
For elements PennyLane's own bundled STO-3G table doesn't cover (row 3+,
e.g. Silicon), this falls back to dense_evolution.native_hf -- a
from-scratch, jax-vmap-vectorized Hartree-Fock engine (Obara-Saika
integrals, Roothaan-Hall SCF) that sources basis-set data from
basis_set_exchange instead, so any element it has STO-3G parameters for
works. Only the Hartree-Fock/integral stage is native; the resulting
converged result is still handed to PennyLane's own fermionic_observable
+ jordan_wigner for the qubit mapping (see native_hf/bridge.py), since
that stage is already fast and well-tested.
N atoms on a line, each bond_length_angstrom apart -- the real,
general shape behind every diatomic entry in the catalog (H2, HeH+,
LiH), extended to any atom count.
Source code in tools/dashboard_core/hamiltonians.py
deflinear_chain_geometry(n_atoms:int,bond_length_angstrom:float):"""N atoms on a line, each bond_length_angstrom apart -- the real, general shape behind every diatomic entry in the catalog (H2, HeH+, LiH), extended to any atom count."""ifn_atoms<1:raiseValueError("linear_chain_geometry needs at least 1 atom")returnnp.array([[0.0,0.0,i*bond_length_angstrom]foriinrange(n_atoms)])
N atoms on a regular polygon (equal bond_length_angstrom between
neighbors), circumradius R = bond_length / (2*sin(pi/n)) -- standard
regular-polygon geometry. At n_atoms=3 this is exactly an equilateral
triangle -- the same real D3h geometry H3+'s catalog entry uses, just
generalized to any ring size (still only meaningful up to whatever
qubit count this simulator's exact diagonalization / VQE range can
handle -- this function itself has no such limit, the caller does).
Source code in tools/dashboard_core/hamiltonians.py
defring_geometry(n_atoms:int,bond_length_angstrom:float):"""N atoms on a regular polygon (equal bond_length_angstrom between neighbors), circumradius R = bond_length / (2*sin(pi/n)) -- standard regular-polygon geometry. At n_atoms=3 this is exactly an equilateral triangle -- the same real D3h geometry H3+'s catalog entry uses, just generalized to any ring size (still only meaningful up to whatever qubit count this simulator's exact diagonalization / VQE range can handle -- this function itself has no such limit, the caller does)."""ifn_atoms<3:raiseValueError("ring_geometry needs at least 3 atoms")R=bond_length_angstrom/(2*np.sin(np.pi/n_atoms))angles=2*np.pi*np.arange(n_atoms)/n_atomsreturnnp.array([[R*np.cos(a),R*np.sin(a),0.0]forainangles])
Runs real Hartree-Fock + fermion-to-qubit mapping (PennyLane
qchem) on the given geometry and returns (H_dense, n_qubits). The
eigenvalue spectrum (and therefore the ground-state energy) is
mapping-invariant -- Jordan-Wigner and Bravyi-Kitaev represent the
identical physical Hamiltonian in a different qubit basis -- so this
only changes which qubit operators appear, never the energies this
function's callers report. Cached: Hartree-Fock isn't free, and the
UI can re-request the same molecule repeatedly.
The dense matrix itself is built by this project's own
dense_evolution.pauli_hamiltonian_to_matrix from PennyLane's real
Pauli decomposition, not qml.matrix() -- verified to match qml.matrix
exactly (same ground-state energy, same matrix, atol=1e-8) for every
catalog molecule.
Source code in tools/dashboard_core/hamiltonians.py
defbuild_molecular_hamiltonian(symbols,geometry,charge:int=0,mapping:str="jordan_wigner",active_electrons=None,active_orbitals=None):"""Runs real Hartree-Fock + fermion-to-qubit mapping (PennyLane qchem) on the given geometry and returns (H_dense, n_qubits). The eigenvalue spectrum (and therefore the ground-state energy) is mapping-invariant -- Jordan-Wigner and Bravyi-Kitaev represent the identical physical Hamiltonian in a different qubit basis -- so this only changes which qubit operators appear, never the energies this function's callers report. Cached: Hartree-Fock isn't free, and the UI can re-request the same molecule repeatedly. The dense matrix itself is built by this project's own dense_evolution.pauli_hamiltonian_to_matrix from PennyLane's real Pauli decomposition, not qml.matrix() -- verified to match qml.matrix exactly (same ground-state energy, same matrix, atol=1e-8) for every catalog molecule."""H,n_qubits=_get_hamiltonian(symbols,geometry,charge,mapping,active_electrons,active_orbitals)dense_key=(tuple(symbols),tuple(map(tuple,np.asarray(geometry).round(10))),charge,mapping,active_electrons,active_orbitals)ifdense_keyin_dense_hamiltonian_cache:return_dense_hamiltonian_cache[dense_key]# H_dense is dim x dim (dim = 2**n_qubits), not just dim, and its only# consumer (ground_state_energy) runs a full dense np.linalg.eigvalsh# on it -- LAPACK's own eigh workspace needs comparable scratch memory# on top of the matrix itself, and the geometry generators in the# Composer's UI (linear_chain_geometry/ring_geometry) let a visitor# build an arbitrarily long chain, with no smaller natural ceiling than# whatever PennyLane's own Hartree-Fock step tolerates. Same real# anti-OOM guard as dashboard_core.engine.run_circuit_from_qasm and# mitigation.py's ZNE panels, sized for what this actually allocates# (the x3 covers the matrix + its eigh scratch space + the cached copy# this function stores in _dense_hamiltonian_cache).dim=2**n_qubitsrequired_mb=dim*dim*16/1e6*3de.chunk.SafeMemoryGuard().check_allocation(required_mb,context=f"{n_qubits}-qubit molecular Hamiltonian")terms=_pennylane_hamiltonian_to_pauli_terms(H,n_qubits)H_dense=de.pauli_hamiltonian_to_matrix(terms,n_qubits)result=(H_dense,n_qubits)_dense_hamiltonian_cache[dense_key]=resultreturnresult
The qubit count a molecule's real Hamiltonian needs, without
paying for a dense matrix build -- cheap enough to call for every
catalog entry when just listing what's available.
Source code in tools/dashboard_core/hamiltonians.py
defget_molecule_n_qubits(symbols,geometry,charge=0,mapping="jordan_wigner",active_electrons=None,active_orbitals=None):"""The qubit count a molecule's real Hamiltonian needs, without paying for a dense matrix build -- cheap enough to call for every catalog entry when just listing what's available."""_,n_qubits=_get_hamiltonian(symbols,geometry,charge,mapping,active_electrons,active_orbitals)returnn_qubits
Every catalog molecule, each annotated with its real qubit count
under the given mapping -- unfiltered, so the UI can always show the
whole catalog and let the molecule choice drive the circuit's qubit
count (not the other way around).
Source code in tools/dashboard_core/hamiltonians.py
defget_all_molecules(catalog=None,mapping="jordan_wigner"):"""Every catalog molecule, each annotated with its real qubit count under the given mapping -- unfiltered, so the UI can always show the whole catalog and let the molecule choice drive the circuit's qubit count (not the other way around)."""catalog=catalogifcatalogisnotNoneelseMOLECULE_CATALOGout={}forname,specincatalog.items():geometry=spec["geometry"]()ifcallable(spec["geometry"])elsespec["geometry"]n_qubits=get_molecule_n_qubits(spec["symbols"],geometry,spec["charge"],mapping=mapping,active_electrons=spec.get("active_electrons"),active_orbitals=spec.get("active_orbitals"),)out[name]={"symbols":spec["symbols"],"geometry":np.asarray(geometry).tolist(),"charge":spec["charge"],"n_qubits":n_qubits,}returnout
Filters MOLECULE_CATALOG down to molecules whose real Hamiltonian
needs exactly n_qubits. Kept for callers that want a qubit-filtered
view; the main catalog UI uses get_all_molecules instead so every
molecule is always visible.
Source code in tools/dashboard_core/hamiltonians.py
defget_compatible_molecules(n_qubits,catalog=None,mapping="jordan_wigner"):"""Filters MOLECULE_CATALOG down to molecules whose real Hamiltonian needs exactly n_qubits. Kept for callers that want a qubit-filtered view; the main catalog UI uses get_all_molecules instead so every molecule is always visible."""catalog=catalogifcatalogisnotNoneelseMOLECULE_CATALOGifn_qubitsisNoneorn_qubits<=0:return{}all_molecules=get_all_molecules(catalog,mapping=mapping)return{name:catalog[name]forname,infoinall_molecules.items()ifinfo["n_qubits"]==n_qubits}
Resolves a MOLECULE_CATALOG entry by name to its (cached) dense
Hermitian Hamiltonian matrix, under the given fermion-to-qubit
mapping (spectrum is identical either way, see build_molecular_hamiltonian).
Source code in tools/dashboard_core/hamiltonians.py
defget_molecular_hamiltonian_matrix(name,catalog=None,mapping="jordan_wigner"):"""Resolves a MOLECULE_CATALOG entry by name to its (cached) dense Hermitian Hamiltonian matrix, under the given fermion-to-qubit mapping (spectrum is identical either way, see build_molecular_hamiltonian)."""catalog=catalogifcatalogisnotNoneelseMOLECULE_CATALOGspec=catalog[name]geometry=spec["geometry"]()ifcallable(spec["geometry"])elsespec["geometry"]H_dense,_=build_molecular_hamiltonian(spec["symbols"],geometry,spec["charge"],mapping=mapping,active_electrons=spec.get("active_electrons"),active_orbitals=spec.get("active_orbitals"),)returnH_dense
Exact ground-state energy via dense diagonalization -- a real,
checkable number (Hartree) for a Hamiltonian this small (H2/HeH+/H3+
all fit well within exact diagonalization), not an estimate.
Source code in tools/dashboard_core/hamiltonians.py
defground_state_energy(H_dense)->float:"""Exact ground-state energy via dense diagonalization -- a real, checkable number (Hartree) for a Hamiltonian this small (H2/HeH+/H3+ all fit well within exact diagonalization), not an estimate."""eigvals=np.linalg.eigvalsh(H_dense)returnfloat(eigvals.min())
Real weighted combination H_mix = weight_aH_a + weight_bH_b of
two molecular Hamiltonians acting on the same qubit space (same
electron/qubit count -- the only condition that makes the sum mean
anything, mirroring the old dashboard's own "mix molecules that
share an electron space" behavior). A real-weighted sum of two
Hermitian matrices is itself Hermitian, so H_mix is a real, valid
Hamiltonian with a real spectrum -- not a fabricated hybrid, just
linear algebra applied to two already-real operators.
Source code in tools/dashboard_core/hamiltonians.py
defmix_hamiltonians(H_a,H_b,weight_a:float=0.5,weight_b:float=0.5):"""Real weighted combination H_mix = weight_a*H_a + weight_b*H_b of two molecular Hamiltonians acting on the same qubit space (same electron/qubit count -- the only condition that makes the sum mean anything, mirroring the old dashboard's own "mix molecules that share an electron space" behavior). A real-weighted sum of two Hermitian matrices is itself Hermitian, so H_mix is a real, valid Hamiltonian with a real spectrum -- not a fabricated hybrid, just linear algebra applied to two already-real operators."""ifH_a.shape!=H_b.shape:dim_a,dim_b=H_a.shape[0],H_b.shape[0]raiseValueError(f"cannot mix: different qubit spaces ({int(np.log2(dim_a))} vs {int(np.log2(dim_b))} qubits)")H_mix=weight_a*H_a+weight_b*H_bifnotnp.allclose(H_mix,H_mix.conj().T,atol=1e-9):raiseValueError("mixed Hamiltonian is not Hermitian -- this should be unreachable")returnH_mix
See also: dashboard_core.vqe for the
ansatz circuits optimized against these Hamiltonians,
dashboard_core.qmmm for the Hellmann-Feynman
forces derived from them, and Native Hartree-Fock for the
engine backing Si2 and any other element outside PennyLane's own STO-3G
table.