Foundation Models#
A foundation model is a single network trained on a family of Hamiltonians at once, in place of one network per Hamiltonian. Samples from every member of the family are mixed into one Monte Carlo batch, and the network conditions its output on the couplings of the system each sample came from, so one trained model covers the whole family — every value of a coupling constant that would otherwise be swept one run at a time.
Formulation#
The construction follows Rende, Viteritti, Becca, Scardicchio, Laio & Carleo, “Foundation neural-network quantum states as a unified Ansatz for multiple Hamiltonians”, Nature Communications (2025).
The wavefunction is \(\psi_\theta(\sigma|\gamma)\): one network with parameters \(\theta\), taking a configuration \(\sigma\) and a coupling vector \(\gamma\) (for instance \(\gamma = J\) or \(\gamma = U\)).
Training minimizes the energy averaged over an ensemble of Hamiltonians, drawn from some distribution \(P(\gamma)\) over coupling space:
A training step estimates this by Monte Carlo: \(\mathcal{R}\) values \(\gamma_1, \dots, \gamma_\mathcal{R}\) discretize \(P(\gamma)\), \(M_k = M/\mathcal{R}\) configurations are sampled from \(|\psi_\theta(\sigma|\gamma_k)|^2\) for each, and the local energy \(E_L\) is averaged over the mixed batch of \(M\) samples.
The gradient requires per-system averages rather than a single average over the mixed batch, for instance the per-system observable average \(\bar A_k = \frac{1}{M_k}\sum_{j \in k} \langle\sigma_j|\hat A_{\gamma_k}|\psi_\theta(\gamma_k)\rangle / \langle\sigma_j|\psi_\theta(\gamma_k)\rangle\), the sum running over the samples of system \(k\). Each Hamiltonian’s energy is normalized by its own \(\langle\psi_\theta(\gamma_k)|\psi_\theta(\gamma_k)\rangle\), so its gradient is a covariance over that system’s samples alone. For real parameters, with \(O(\sigma,\gamma) = \nabla_\theta \log\psi_\theta(\sigma|\gamma)\),
The local energies and the log-derivatives are therefore centered about the mean of their own system, not about the mean of the mixed batch, and the metric used by stochastic reconfiguration is centered the same way.
The data: two extra fields#
Two per-sample arrays are added:
system_couplings, shape(N_mc, n_couplings)— \(\gamma\), the Hamiltonian parameters of the system each sample came from.system_ids, shape(N_mc,)— an integer in[0, n_systems)identifying that system.
FoundationState is a PyTreeNode holding these two fields. Combined with
SpinState or FermionState by multiple inheritance, it gives
SpinFoundationState and FermionFoundationState; both remain States, so
sample, compute_expectation and the optimizer apply to them unchanged (see
Core Concepts).
The batch is built once, before the loop: one Hamiltonian per system from a
common template, combine_systems merges them into a single operator whose
couplings vary per sample, and extract_system_couplings returns that
operator’s couplings as the array FoundationState.system_couplings
expects.
Example: the Hubbard model at several values of U#
One network trained on the Hubbard model at four values of the on-site interaction, from the non-interacting limit (\(U=0\)) to the strongly correlated regime (\(U=8\)), on a 4×4 lattice at fixed filling.
import jax
import jax.numpy as jnp
from tachys.lattice.lattice_database import square
from tachys.lattice.fermions.fermion_state import init_config_spinful
from tachys.lattice.fermions.hamiltonians.hubbard import hubbard_square_pbc
from tachys.lattice.foundation.foundation_state import FermionFoundationState
from tachys.lattice.foundation.operators import combine_systems, extract_system_couplings
from tachys.lattice.ansatz.rbm_foundation import FermionFoundationRBM
from tachys.lattice.bond_exchange import BondExchange
from tachys.wavefunction import WaveFunction
from tachys.optimizer import SR
from tachys.montecarlo import sample
from tachys.lattice.operator.local_estimator import compute_expectation
L = 4
lattice = square(shape=(L, L))
N = lattice.Ns
Ne = 10 # fixed filling, shared by every system
Us = [0.0, 2.0, 4.0, 8.0] # gamma: one Hamiltonian per system, same template
n_systems = len(Us)
n_mc_per_system = 8
N_mc = n_systems * n_mc_per_system
Hs = [hubbard_square_pbc(L, U=U) for U in Us]
H = combine_systems(Hs, n_mc_per_system)
system_couplings = extract_system_couplings(H)
system_ids = jnp.repeat(jnp.arange(n_systems), n_mc_per_system)
key = jax.random.key(0)
key, subkey = jax.random.split(key)
occupations, N_up, N_down = init_config_spinful(subkey, Ns=N, Ne=Ne, N_mc=N_mc)
state = FermionFoundationState(
occupations=occupations, Ne=Ne, lattice=lattice,
system_couplings=system_couplings, system_ids=system_ids, n_systems=n_systems,
)
model = FermionFoundationRBM(hidden_units=64)
key, subkey = jax.random.split(key)
params = model.init(subkey, state)
wf = WaveFunction(params=params, apply_fn=model.apply)
action = BondExchange.create(lattice, Nbands=2)
optimizer = SR(diag_shift=1e-3, mode="real")
opt_state = optimizer.init(wf.params)
N_steps, lr = 30, 5e-3
for step in range(N_steps):
key, subkey = jax.random.split(key)
mc_keys = jax.random.split(subkey, N_mc)
state, log_amps, acceptance = sample(1, state, action, mc_keys, wf)
E_L, e_mean, e2_mean = compute_expectation(H, wf, state, log_amps)
updates, opt_state = optimizer(E_L, opt_state, state, wf)
wf = wf.apply_gradients(updates, lr)
print(f"step {step:3d} E/N (ensemble avg) = {jnp.real(e_mean) / N: .4f}")
tests/lattice/foundation/test_main_foundation_setup.py runs this pipeline
as a regression test, with expected values for the local energies, the sampled
log-amplitudes and the optimizer updates.
Three properties of the example are worth stating explicitly.
Couplings. system_couplings has shape (32, 1): only \(U\) varies across
the four systems, the hopping \(t\) being shared, so extract_system_couplings
finds one varying column. In general there is one column per distinct varying
numeric value, not one per physical parameter — heisenberg_hamiltonian
with \(J\) and \(J/2\) terms yields two.
Ansatz. The network reads system_couplings from the state alongside the
configuration. FermionFoundationRBM and SpinFoundationRBM concatenate it
onto the input of the backflow/RBM layer, and are otherwise identical to
FermionRBM and SpinRBM.
Loop. The training loop is unchanged from Core Concepts: sample and
compute_expectation are indifferent to the state subtype, and the optimizer
uses system_ids to center energies per system.