Evolution Module
evolution
Evolution module for lattice field theory.
Provides symplectic leapfrog integrators for:
- Pure gauge fields (flat + expanding space)
- Coupled scalar-gauge fields (flat + expanding space)
- Simple scalar fields (flat space only)
Also provides RK4 (4th-order Runge-Kutta) integrators as a high-accuracy alternative.
All leapfrog integrators preserve energy, symplectic structure, and constraints. RK4 offers higher accuracy but does not preserve symplectic structure.
make_radiation_rho(rho_initial, a_initial)
Create a radiation-era density function with \(\rho(a) \propto a^{-4}\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rho_initial
|
float
|
Reference density at |
required |
a_initial
|
float
|
Reference scale factor. |
required |
Returns:
| Type | Description |
|---|---|
|
Callable |
Source code in jaxlatt/core/cosmology/frw.py
compute_energy_densities_expanding(lattice, a)
Compute COMOVING energy densities in expanding universe.
Phase A: Returns only comoving densities (energy per comoving volume). Physical densities with correct a-scaling deferred to Phase B.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Field state |
required |
a
|
float
|
Scale factor (unused in Phase A, kept for API compatibility) |
required |
Returns:
| Type | Description |
|---|---|
dict
|
dict with: "total": total comoving energy density "kinetic": scalar kinetic energy density (comoving) "gradient": scalar gradient energy density (comoving) "potential": scalar potential energy density (comoving) "electric": electric energy density (comoving) "magnetic": magnetic energy density (comoving) |
Source code in jaxlatt/evolution/leapfrog.py
coupled_evolve(lattice, dt, steps, save_every=1, verbose=False)
Evolve coupled system over multiple time steps.
Eager ergonomics layer over :func:_coupled_evolve_core, which is the entry
point to use inside a JAX transformation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Initial state |
required |
dt
|
float
|
Time step |
required |
steps
|
int
|
Number of steps |
required |
save_every
|
int
|
Save snapshot every N steps |
1
|
verbose
|
bool
|
Show progress bar |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
times |
Array
|
Saved snapshot times |
snapshots |
list[CoupledLattice]
|
List of saved lattice states |
Source code in jaxlatt/evolution/leapfrog.py
coupled_evolve_expanding(lattice, universe, rho_func, pressure_func, dt, steps, save_every=1, verbose=False)
Evolve coupled fields in expanding universe.
Eager ergonomics layer over :func:_coupled_evolve_expanding_core: it adds
the host-side overflow diagnostics and unstacks the trajectory into Python
lists. Being eager is deliberate — to trace, jit/grad/vmap the
core instead. To keep peak memory bounded on long runs, use
:func:coupled_evolve_expanding_chunked.
rho_func and pressure_func must be JAX-traceable (no Python control flow on their argument).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Initial field configuration |
required |
universe
|
FRWUniverse
|
Initial cosmological state |
required |
rho_func
|
Callable[[float], float]
|
Function rho(a) — must be JAX-traceable |
required |
pressure_func
|
Callable[[float], float]
|
Function p(a) — must be JAX-traceable |
required |
dt
|
float
|
Time step (conformal time) |
required |
steps
|
int
|
Number of steps |
required |
save_every
|
int
|
Save snapshot every N steps |
1
|
verbose
|
bool
|
If True, use Python loop instead of lax.scan (for debugging) |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
times |
Array
|
Conformal times at each snapshot |
lattices |
list[CoupledLattice]
|
List of saved lattice states |
universes |
list[FRWUniverse]
|
List of saved universe states |
r
Source code in jaxlatt/evolution/leapfrog.py
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 | |
coupled_evolve_expanding_chunked(lattice, universe, rho_func, pressure_func, dt, steps, save_every=1, chunk_segments=10)
Stream an expanding-universe run chunk by chunk.
Yields (times, stacked_lattices, stacked_universes) for at most
chunk_segments snapshots at a time, so peak device memory is set by
chunk_segments rather than by steps. The eager evolvers stack the
whole trajectory, which at N=256 with 100 snapshots is tens of GB.
Only the first chunk includes the initial state, so concatenating the chunks
reproduces :func:coupled_evolve_expanding exactly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chunk_segments
|
int
|
Snapshots materialised per chunk. |
10
|
Yields:
| Type | Description |
|---|---|
Array
|
|
CoupledLattice
|
out (see :func: |
Example
Source code in jaxlatt/evolution/leapfrog.py
coupled_leapfrog_step(lattice, dt)
Single leapfrog step for coupled scalar-gauge evolution.
Equations of motion:
- \(d\phi/dt = \pi\)
- \(d\pi/dt = F_\phi\)
- \(dA_i/dt = E_i\)
- \(dE_i/dt = F_A\) (with scalar current)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Current state |
required |
dt
|
float
|
Time step |
required |
Returns:
| Type | Description |
|---|---|
CoupledLattice
|
CoupledLattice at t+dt |
Source code in jaxlatt/evolution/leapfrog.py
coupled_leapfrog_step_expanding(lattice, universe, rho_func, pressure_func, dt)
Leapfrog step with cosmological expansion and Hubble friction.
Modified equations in conformal time:
- \(\phi' = \pi\)
- \(\pi' = F_\phi/a^2 - 2H\pi\) (Hubble friction on \(\pi\), treated via PC)
- \(A'_i = E_i\)
- \(E'_i = -F_E/a^2 - 2H E_i\) (Hubble friction on \(E\), treated via PC)
- \(a' = a^2 H\) (scale factor evolution)
The Hubble friction terms use a predictor-corrector (trapezoidal) scheme: \(H_n \cdot \pi_n\) at step \(n\) and \(H_{n+1} \cdot \pi_\mathrm{pred}\) at the predicted step, giving 2nd-order accuracy in \(dt \cdot H\). Forces are evaluated at the half-step position as usual.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Current field state (comoving coordinates) |
required |
universe
|
FRWUniverse
|
Current cosmological state |
required |
rho_func
|
Callable[[float], float]
|
Function \(\rho(a)\) giving physical energy density |
required |
pressure_func
|
Callable[[float], float]
|
Function \(p(a)\) giving physical pressure |
required |
dt
|
float
|
Time step (conformal time) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
lattice_new |
CoupledLattice
|
Updated field state |
universe_new |
FRWUniverse
|
Updated cosmological state |
Source code in jaxlatt/evolution/leapfrog.py
coupled_leapfrog_step_rescaled(lattice, universe, rho_func, pressure_func, dt)
Leapfrog step using rescaled field \(\chi = a\phi\) eliminating explicit friction.
Algorithm:
- \(\chi = a\phi\)
- \(\pi_\chi = \chi'\)
- \(F_\chi = (1/a^2)\nabla^2\chi - a^2 m^2 \chi - \lambda|\chi|^2\chi + (a''/a)\chi\)
- \(\chi_{n+1/2} = \chi_n + (dt/2)\,\pi_{\chi,n}\)
- \(\pi_{\chi,n+1} = \pi_{\chi,n} + dt\, F_\chi(\chi_{n+1/2})\)
- \(\chi_{n+1} = \chi_{n+1/2} + (dt/2)\,\pi_{\chi,n+1}\)
- Back-transform: \(\phi = \chi/a\), \(\pi = (\pi_\chi - a'\phi)/a\)
Gauge fields still evolved with friction (future: rescale).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Current field state |
required |
universe
|
FRWUniverse
|
Current cosmological state |
required |
rho_func
|
Callable[[float], float]
|
Function \(\rho(a)\) for scale factor evolution |
required |
pressure_func
|
Callable[[float], float]
|
Function \(p(a)\) for scale factor evolution |
required |
dt
|
float
|
Time step |
required |
Returns:
| Name | Type | Description |
|---|---|---|
lattice_new |
CoupledLattice
|
Updated field state |
universe_new |
FRWUniverse
|
Updated cosmological state |
Source code in jaxlatt/evolution/leapfrog.py
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 | |
gauge_evolve(lattice, dt, steps, save_every=1, verbose=False)
Evolve gauge field using leapfrog integrator.
Eager ergonomics layer over :func:_gauge_evolve_core, which is the entry
point to use inside a JAX transformation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
GaugeLattice
|
Initial gauge lattice state |
required |
dt
|
float
|
Time step |
required |
steps
|
int
|
Number of steps |
required |
save_every
|
int
|
Save snapshot every N steps |
1
|
verbose
|
bool
|
Show progress bar |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
times |
Array
|
Array of snapshot times |
snapshots |
list[GaugeLattice]
|
List of GaugeLattice states |
Source code in jaxlatt/evolution/leapfrog.py
gauge_leapfrog_step(lattice, dt)
Single leapfrog step for pure gauge U(1) evolution.
Algorithm
- E(t+dt/2) = E(t) - (dt/2) * F[U(t)]
- U(t+dt) = U(t) * exp(i * dt * E(t+dt/2))
- E(t+dt) = E(t+dt/2) - (dt/2) * F[U(t+dt)]
Preserves unitarity, Gauss constraint, and energy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
GaugeLattice
|
Current gauge lattice state |
required |
dt
|
float
|
Time step |
required |
Returns:
| Type | Description |
|---|---|
GaugeLattice
|
Updated GaugeLattice at t+dt |
Source code in jaxlatt/evolution/leapfrog.py
scalar_force_expanding(phi, links, m, lambda_, dx, a)
Compute force on scalar field in expanding universe.
Force for \(\pi\) update (excluding friction \(-2H\pi\) applied separately):
Uses autodiff via scalar_potential_force for correct Wirtinger derivatives
on complex fields.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
phi
|
Array
|
Scalar field (comoving) |
required |
links
|
Array
|
Gauge links |
required |
m
|
float
|
Mass parameter |
required |
lambda_
|
float
|
Self-coupling |
required |
dx
|
float
|
Lattice spacing (comoving) |
required |
a
|
float
|
Scale factor |
required |
Returns:
| Type | Description |
|---|---|
Array
|
\(F_\phi\): Force on \(\phi\) (excluding friction term) |
Source code in jaxlatt/evolution/leapfrog.py
scalar_force_rescaled(chi, links, m, lambda_, dx, a, addot)
Force for rescaled field \(\chi = a\phi\) eliminating explicit Hubble friction.
Derived equation for \(\chi\):
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chi
|
Array
|
Rescaled field \(\chi = a\phi\) |
required |
links
|
Array
|
Gauge links |
required |
m, lambda_
|
Scalar parameters |
required | |
dx
|
float
|
Lattice spacing (comoving) |
required |
a
|
float
|
Scale factor |
required |
addot
|
float
|
Conformal second derivative \(a''\) (from Friedmann) |
required |
Returns:
| Type | Description |
|---|---|
Array
|
\(F_\chi\) array same shape as \(\chi\) |
Source code in jaxlatt/evolution/leapfrog.py
coupled_rk4_step(lattice, dt)
Single RK4 step for coupled scalar-gauge field evolution (flat space).
This is a 4th-order accurate integrator that requires 4 force evaluations per step, compared to 2 for leapfrog. It does not preserve the symplectic structure but offers higher accuracy for smooth dynamics.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
CoupledLattice
|
Current coupled lattice state |
required |
dt
|
float
|
Time step |
required |
Returns:
| Type | Description |
|---|---|
CoupledLattice
|
Updated CoupledLattice at t+dt |
Note
For gauge fields, link updates use exponential map to preserve unitarity. Unlike leapfrog, this does NOT guarantee energy conservation.
Source code in jaxlatt/evolution/rk4.py
gauge_rk4_step(lattice, dt)
Single RK4 step for pure gauge field evolution (flat space).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
GaugeLattice
|
Current gauge lattice state |
required |
dt
|
float
|
Time step |
required |
Returns:
| Type | Description |
|---|---|
GaugeLattice
|
Updated GaugeLattice at t+dt |
Source code in jaxlatt/evolution/rk4.py
evolve(lattice, potential, dt, steps, save_every=1, verbose=False, H=0.0, use_spectral=False)
Evolve a scalar field lattice for multiple time steps.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
Lattice
|
Initial lattice state |
required |
potential
|
Callable[[Array], Array]
|
Potential function \(V(\phi)\) |
required |
dt
|
float
|
Time step |
required |
steps
|
int
|
Number of time steps |
required |
save_every
|
int
|
Save snapshot every N steps (default: 1) |
1
|
verbose
|
bool
|
Print progress updates (default: False) |
False
|
H
|
float
|
Hubble parameter for cosmological friction (default: 0.0, no friction) |
0.0
|
use_spectral
|
bool
|
Use FFT-based spectral Laplacian (default: False) |
False
|
Returns:
| Type | Description |
|---|---|
Array
|
Tuple of (times, snapshots) where: |
list[Lattice]
|
|
tuple[Array, list[Lattice]]
|
|
Example
from jaxlatt.core import ScalarPotential
from jaxlatt.utils import create_initial_lattice_1d
# Flat spacetime evolution
lattice = create_initial_lattice_1d(size=128, length=10.0)
potential = ScalarPotential.quadratic(m=1.0)
times, snapshots = evolve(lattice, potential, dt=0.01, steps=1000)
# Cosmological evolution with Hubble friction
times, snapshots = evolve(lattice, potential, dt=0.01, steps=1000, H=0.1)
Source code in jaxlatt/evolution/scalar.py
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 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 | |
evolve_step(lattice, potential, dt, H=0.0, use_spectral=False)
Perform a single leapfrog time step for a scalar field.
Uses the leapfrog (Verlet) scheme with optional Hubble friction:
- \(\dot{\phi}_{n+1/2} = \dot{\phi}_n + (dt/2)\,F(\phi_n)\)
- \(\phi_{n+1} = \phi_n + dt\,\dot{\phi}_{n+1/2}\)
- \(\dot{\phi}_{n+1} = \dot{\phi}_{n+1/2} + (dt/2)\,F(\phi_{n+1})\)
where \(F(\phi) = \nabla^2\phi - dV/d\phi\).
For cosmological evolution with Hubble friction (\(H > 0\)), the equation becomes:
The friction term is treated semi-implicitly for numerical stability, applied once at the full-step velocity update:
- \(\dot{\phi}_{n+1/2} = \dot{\phi}_n + (dt/2)\,F_n\) (no friction at half-step)
- \(\dot{\phi}_{n+1} = [\dot{\phi}_{n+1/2} + (dt/2)\,F_{n+1}]\,/\,(1 + 2H\,dt)\)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lattice
|
Lattice
|
Current lattice state |
required |
potential
|
Callable[[Array], Array]
|
Potential function \(V(\phi)\) |
required |
dt
|
float
|
Time step |
required |
H
|
float
|
Hubble parameter (conformal). If H=0 (default), no friction is applied. |
0.0
|
use_spectral
|
bool
|
If True, use FFT-based spectral Laplacian for exact \(k^2\) dispersion. Recommended for validation and periodic domains. Default: False (finite-difference). |
False
|
Returns:
| Type | Description |
|---|---|
Lattice
|
New lattice state at time t + dt |