API reference
Every public function and class in the four subpackages and the pygfmc.protocols module, with its docstring. The entries are generated from the source code, so they always match the installed version.
pygfmc.protocols
The interfaces of a Hamiltonian or off-diagonal observable, a diagonal observable and a guiding wavefunction, as typing.Protocol classes.
A class satisfies one by having the members; it does not inherit from it.
They are also available directly from pygfmc.
|
The rate protocol of a Hamiltonian or of an off-diagonal observable. |
An operator measured through its diagonal only ( |
|
|
A guiding wavefunction |
- class pygfmc.protocols.Operator
The rate protocol of a Hamiltonian or of an off-diagonal observable.
The operator is a sum
H = sum_i h_iofM_totalterms, each of which produces at most one new basis state from a given one. Term 0 is the diagonal (all diagonal operators are absorbed into it). An operator passed ashamiltonianor inoff_diag_operatorsmust be a JAX pytree, because it is passed into jitted functions.- get_rates(config, return_configs=False)
Matrix elements of all terms acting on
config.Returns
(rates, new_configs):rateshas shape(M_total,)with the diagonal element first.new_configsis the(M_total, N_sites)array of successor states (row 0 isconfigitself) whenreturn_configs=True, elseNone.
- generate_state(config, idx)
The basis state produced by term
idxacting onconfig.Required when
M_total > 1. The propagators use it to avoid materialising all successors.
- move(config, idx)
Optional. The sites changed by term
idxand their increments,(sites, deltas)of a fixed shape ((2,)each for the built-in classes; zero increments foridx == 0), so thatgenerate_state(config, idx)isconfigwithdeltasadded atsites. Needed only by a guide in the incremental mode (seeGuide), which updates its per-walker state from them.
- class pygfmc.protocols.DiagonalOperator
An operator measured through its diagonal only (
diag_operators).The built-in Hamiltonians provide
diagonal_weightas well, so they can be measured as diagonal observables.- diagonal_weight(config)
The diagonal element for
config: a scalar, or a vector of values for a vector-valued observable.
- class pygfmc.protocols.Guide
A guiding wavefunction
psi_T.The engine needs only the ratios
psi_T(x') / psi_T(x)to theM_totalsuccessors of the current configuration, so a guide implements at least one of the three methods below; the engine uses the first one it finds, in this order.build_cacheis optional. A guide that is passed into a run must be a JAX pytree.The class declares all four methods so that each is documented once; a guide that has only some of them is accepted at run time.
- log_diff_from_operator(config, op, cache=None, state=None)
log psi_T(x'_k) - log psi_T(x)for every termkofop, shape(M_total,), computed from the operator’s structure without materialising the successors. The fastest path.stateis the walker’s state of the incremental mode (passed asstate=only whenincrementalis set; seeinit_state()).
- log_diff_from_configs(config, new_configs, cache=None)
The same log-ratios from the materialised successors
new_configsof shape(M_total, N_sites).
- log_psi(config, cache=None)
log psi_T(config), a scalar. The last fallback: the engine then evaluates it on every successor.
- build_cache(op)
Optional. Precompute operator-specific data once per run; the result is passed as
cachetolog_diff_from_operator. The configuration-based fallbacks are called without a cache.
- incremental: bool
the engine keeps a per-walker state from
init_state(), updates it withupdate_state()after every move (the Hamiltonian must provideOperator.move()), carries it through reconfiguration, passes it asstate=tolog_diff_from_operator(), and rebuilds it every 100 chain steps so that rounding cannot accumulate. The built-in Jastrow guides keep their weighted neighbour sums this way (constructor flagincremental). A run in this mode is not bit-identical to the same run without it.- Type:
Optional. True switches on the incremental mode
- init_state(config, cache=None)
Optional (incremental mode). The per-walker state of
config, a pytree of arrays.
- update_state(state, config, sites, deltas, cache=None)
Optional (incremental mode).
stateafter the move that addsdeltastositesofconfig(the result ofOperator.move());log_diff_from_operatorthen receives it asstate=.
pygfmc.walkers
The walker population, its initializers and reconfiguration.
|
Walker population (or a single walker) as a JAX pytree. |
|
Initialize walkers by drawing every site independently from |
|
Initialize walkers in a fixed-charge sector drawn from |
|
Initialize walkers at user-supplied configurations. |
|
Systematic resampling of the walker population. |
- class pygfmc.walkers.Walker(config, log_weight, e_loc)
Walker population (or a single walker) as a JAX pytree.
- config
configurations, shape
(N_walkers, N_sites)for a batch ((N_sites,)for a single walker). Spin-1/2 models use the +-1 encoding.- Type:
- log_weight
accumulated log weight since the last reconfiguration, shape
(N_walkers,).- Type:
- save(path, rng_key=None, group='walkers', **attrs)
Save walker arrays + arbitrary metadata to an HDF5 file.
Walker arrays (
config,log_weight,e_loc) are stored as HDF5 datasets; the JAX PRNG key (if any) is also a dataset.configis stored in the smallest lossless dtype (int8for ±1 / small-integer configs, ~4x smaller than float32; original dtype otherwise) — see_compact_config().loadcasts it back to the compute dtype. Any additional keyword arguments — typicallyavg_log_weight=..., but anything scalar — are stored as group attributes (same convention asJastrowSpinHalf.save()).- Parameters:
path (str) – HDF5 file path (created or overwritten).
rng_key – JAX PRNG key to checkpoint alongside the walkers (a legacy
PRNGKeyor a typedjax.random.key; stored as raw key data and loaded back as a legacy key). Only Threefry keys with the default Threefry PRNG are supported; other implementations raise before writing. Pass the keyrun_gfmcreturned (stats['parameters/rng_key_next']): the key carries the chain position, so it is the whole resume state.Walker.loadgives it back, and passing it asrng_keycontinues the stream instead of replaying it.group (str) – HDF5 group name to write under.
**attrs – extra scalar metadata stored as group attributes.
- classmethod load(path, group='walkers', dtype=None, config_dtype=None)
Load a Walker (and optional RNG key / avg_log_weight) from an HDF5 checkpoint.
- Parameters:
group (str) – HDF5 group name to read from.
dtype – JAX dtype for weights and local-energy estimators.
None(the default) uses JAX’s default float – float32, or float64 whenjax_enable_x64is set – exactly as theprepare_walkers_*initializers do, so a loaded ensemble has the same width as a freshly prepared one.config_dtype – Optional JAX dtype for configurations. Defaults to
dtype.
- Returns:
(walker, rng_key, avg_log_weight)where rng_key and avg_log_weight areNonewhen not saved.
- pygfmc.walkers.prepare_walkers_random(N_walkers, N_sites, rng_key, states=(-1.0, 1.0))
Initialize walkers by drawing every site independently from
states.Each site of each walker is an independent uniform draw, so the total magnetization/occupation fluctuates binomially about
N_sites * mean(states). Use this for Hamiltonians that do NOT conserve the corresponding charge (TFIM, toric code with h_x); useprepare_walkers_sz()to start inside a fixed sector.- Parameters:
N_walkers (int) – number of walkers.
N_sites (int) – number of sites.
rng_key (Array) – JAX random key.
states (Sequence[float]) – local values to draw from –
(-1, 1)for the +-1 spin-1/2 encoding (the default),(-0.5, 0.5)for physical S^z,(-1, 0, 1)for S=1,(0, ..., n_max)for bosons. Drawn with equal probability.
Note there is no
dtypeargument: the configuration dtype is taken fromstatesitself, so an integer ladder such as(-1, 0, 1)yields integer configurations and(-0.5, 0.5)yields floating-point ones. The weights and local energies use JAX’s default float dtype (float32, or float64 whenjax_enable_x64is set); they never inherit an integer dtype.- Returns:
Walker batch with zero log-weights.
- Return type:
- pygfmc.walkers.prepare_walkers_sz(N_walkers, N_sites, rng_key, sz=0, states=(-1.0, 1.0))
Initialize walkers in a fixed-charge sector drawn from
states.szis simply the SUM over all sites of the local values, each of which is one ofstates:sz = sum_i sigma_i, sigma_i in states
so its meaning follows the encoding rather than being fixed to spin-1/2. With the default
states=(-1.0, 1.0)it is the sum of the +-1 config values, i.e. twice the physical S^z_tot, and the familiar constraint thatN_sites + szbe even is just the two-state case of the general reachability condition. Withstates=(-0.5, 0.5)the sameszis S^z_tot itself; with(-1, 0, 1)it is S^z_tot for S=1; with(0, ..., n_max)it is the total boson number.Every walker is an independent random permutation of one base multiset, so the sum is EXACT for every walker – which is what makes this usable to pin a conserved sector: for a Hamiltonian that conserves the charge, walkers started in the wrong sector can never leave it. A request outside the reachable range, or one the ladder cannot represent, raises rather than silently returning a config with the wrong sum.
- Parameters:
As in
prepare_walkers_random()there is nodtypeargument: the configuration dtype followsstates, while the weights and local energies use JAX’s default float dtype.- Returns:
Walker batch with zero log-weights.
- Return type:
- pygfmc.walkers.prepare_walkers_from_configs(configs)
Initialize walkers at user-supplied configurations.
Like
prepare_walkers_random()andprepare_walkers_sz(), this takes nodtype: the configurations keep the dtype ofconfigs, so anint8input staysint8. Cast the input yourself –jnp.asarray(configs, dtype=...)– to get something else. The weights and local energies use JAX’s default float dtype.Useful whenever a specific sector must be seeded exactly, e.g. the all-ones plaquette sector of the toric code (
prepare_walkers_from_configs(jnp.ones((N_walkers, N_sites)))), or to start from configurations prepared elsewhere.
- pygfmc.walkers.reconfiguration(walkers, rng_key)
Systematic resampling of the walker population.
Resamples walkers proportional to their weights using the systematic scheme (one shared random offset). After resampling all weights are reset to one (log-weight = 0).
- Parameters:
- Returns:
(walkers, log_G_n, E_n, indices).log_G_nis the logarithm of the mean walker weightG_n = (1/N) Σ_i w_i(the step’s branch weight, kept in log space because its product over a window spans hundreds of orders of magnitude),E_nthe weight-averaged local energyΣ_i w_i e_L(x_i) / Σ_i w_i, andindices[i]the parent of new walkeri(the parent map is needed to gather the new configurations, so it is always available and always returned).- Return type:
pygfmc.engine
The GFMC engine: propagation, estimators and HDF5 storage.
These functions are also available directly from pygfmc.
|
Auto-dispatch GFMC: run_gfmc_pmap on >1 devices, run_gfmc otherwise. |
|
Run the GFMC production loop on a single device. |
|
Run branching steps and collect diagnostic statistics. |
|
Re-evaluate the energy and the observables from raw per-step series. |
|
Append one GFMC run to an HDF5 file as a self-contained group. |
|
Continue a stored chain in one call, and return what |
|
Read a file's raw series back in the layout |
|
Validate a planned run against a stored file before walking. |
|
Assemble the |
- pygfmc.engine.run(walkers, n_steps, hamiltonian, step_size, guide, p_array, *, method='cont_time', rng_key=None, lam=None, **kwargs)
Auto-dispatch GFMC: run_gfmc_pmap on >1 devices, run_gfmc otherwise.
The recommended entry point. Drop-in replacement for
pygfmc.engine.driver.run_gfmc()— same positional arguments and keyword arguments (plusn_devices, defaultjax.device_count()), same return tuple.lamis needed only by the discrete methods. When more than one device is used it dispatches torun_gfmc_pmap(), which shards walkers across devices; otherwise it callsrun_gfmcunchanged.Runs with
diag_operatorsfall back to the single-device backend. Note that the two backends differ in a few details (seerun_gfmc_pmap()).
- pygfmc.engine.run_gfmc(walkers, n_steps, hamiltonian, step_size, guide, p_array, *, method='cont_time', rng_key=None, lam=None, diag_operators=None, off_diag_operators=None, return_raw=True, avg_log_weight=None, warmup_steps=None, verbosity=7, number_of_outputs=100, off_diag_stride=1, off_diag_batch_size=None, _step_offset=0)
Run the GFMC production loop on a single device.
- Sequence:
Warm-up: a 4-stage step-size ramp of
warmup_stepstotal steps (skipped whenwarmup_steps=0). It equilibrates the ensemble and, unlessavg_log_weightis supplied, also measures the normalisation shiftavg_log_weight.Production scan of n_steps branching steps (evolve -> reconfigure -> diagonal ancestry / off-diagonal forward walking), printing progress every n_steps // number_of_outputs steps.
Windowed estimation over
p_arrayand an end-of-runpygfmc.engine.estimators.statistics()pass (sampler health, offspring distribution, ancestor survival at m = p/2).
- Parameters:
walkers (Walker) – Initial Walker batch.
n_steps (int) – Number of production branching steps.
hamiltonian (Operator) – Any object implementing the Hamiltonian protocol (
M_total,get_rates,generate_state) — e.g. SpinHalf, Spin, BoseHubbard, ToricCode, or PlaquetteModel.step_size (int | float) – Per-Markov-step size. For the discrete methods this is the integer count of imaginary-time sub-steps (must be integer-valued); for
cont_timeit is the imaginary-time length $beta$ (any positive float).guide (Guide | None) – Guiding wavefunction or None.
p_array (Sequence[int] | ndarray) – Window sizes for the mixed/pure estimator (deduplicated and sorted). The projection depth per side is tau = (p/2)·step_size. When an off-diagonal operator has an off-diagonal transition (
M_total > 1) all p values must be even.method (str) – “single_step”, “multi_step”, or “cont_time” (default “cont_time”). This choice sets the meaning of
step_size(integer sub-step count for the discrete methods, imaginary time beta for cont_time) and ofavg_log_weight(log b per sub-step vs. minus the mean local energy per unit imaginary time); under cont_timelamhas no effect (it is the lambda -> infinity limit).rng_key (Array | None) – JAX PRNGKey or typed Threefry key.
None(default) draws a fresh one fromsecrets.randbits(32)(printed at verbosity >= 1, stored instats['parameters/rng_key']). The default PRNG must be Threefry; unsupported key implementations raise before running. THIS IS THE CHAIN POSITION, not just a seed: the production stream starts here and advances key_{t+1} = split(key_t) once per step, with no step index anywhere. Pass back the key a previous run returned (stats['parameters/rng_key_next'], whichWalker.save(rng_key=...)checkpoints andWalker.loadreturns) and the continuation is bit-identical to one uninterrupted run – that one array is the whole resume state. Everything else needing randomness draws from tagged side-streams, so it cannot shift the production sequence.lam (float | None) – GFMC shift λ, required by the discrete methods, where it must satisfy λ ≥ max_x H_xx over the reachable configurations (ValueError when omitted). Unused by
cont_time, the default method, so it defaults toNone.diag_operators (Mapping[str, DiagonalOperator] | None) – dict name → operator of diagonal operators, or None. Each needs
diagonal_weight(config)returning a scalar or an(n_vals,)vector. Measured with the ancestry-traced (forward-walking) pure estimator: the value at the ancestor m = p/2 steps back, weighted like the energy.off_diag_operators (Mapping[str, Operator] | None) – dict name → operator of off-diagonal operators, or None. Each must implement the operator protocol (
pygfmc.Operator), with non-negative rates. Measured via the forward-walking pure estimator (one shadow chain of depth max(p)/2 per operator per measurement). Every p must be even when an operator hasM_total > 1; a purely diagonal operator (M_total == 1) accepts odd p as well.return_raw (bool) – If True (default), append the
rawdict to the return tuple (see Returns). The raw series can then be stored and re-estimated over the whole concatenated chain (evaluate_observables), where windows span run boundaries, so every off-diagonal measurement is kept. With False the per-call windowed numbers are final, and the off-diagonal measurements are restricted to the band of steps usable by EVERY p (the first max(p) steps become a lead-in and the last max(p)/2 a tail), so each p rests on the identical set of measurements; n_steps > 1.5*max(p) is then REQUIRED (ValueError otherwise).avg_log_weight (float | None) – Pre-computed normalisation shift (e.g. from
stats['avg_log_weight']of a previous run). When supplied it is kept fixed; the warm-up ramp still runs (unlesswarmup_steps=0) but only equilibrates the walkers. When None it is measured by the warm-up (seewarmup_steps).warmup_steps (int | None) – Total warm-up steps split across 4 ramp stages (step_size/8 → step_size/4 → step_size/2 → step_size; integer division, floored at 1, for the discrete methods). Default None: 1000 steps (
DEFAULT_WARMUP_STEPS) when noavg_log_weightis given, which the warm-up then measures, and 0 when one is – right when continuing from an equilibrated ensemble withstats['avg_log_weight']of the previous run. A fresh start of a large system may need more, e.g.warmup_steps=10000. An explicitwarmup_steps=0withoutavg_log_weightraises ValueError.verbosity (int) –
Integer 0–10 controlling printed output:
0 — silent
1 — step/it/N_config progress (no energy)
3 — progress + Energy[p]
8 — progress + Energy[p] + first diag/off-diag observable (re-estimated over the whole series at every progress line, so it costs host time) + warmup lines
10 — full output (all observables)
The end-of-run statistics report prints for any verbosity >= 1. Default 7.
number_of_outputs (int) – How many progress lines to print during the production run (default 100). Actual cadence is n_steps // number_of_outputs.
off_diag_stride (int) –
Measure the off-diagonal operators only every
off_diag_stride-th production step (default 1 = every step). Each off-diagonal measurement launches a fresh shadow Markov chain m_max = max(p)/2 steps deep, so it dominates the per-step cost by ~m_max× — but consecutive main-chain steps are strongly correlated, so per-step measurement mostly averages redundant values. A stride ≈ the observable’s autocorrelation time keeps nearly all the statistical information at ~1/stride of the cost. Only the measured steps are stored (raw['off_diag']has one row per measurement,raw['off_diag_steps']gives each row’s step). Energy and diagonal observables are unaffected — they are cheap and stay per-step.The stride counts BRANCHING STEPS, so choose it against
step_size: decide the imaginary time between measurements and convert:off_diag_stride = round(tau_between/step_size)
since ensemble decorrelation is a process in imaginary time. A stride copied between runs with different
step_sizesilently changes meaning. A useful rule of thumb is tau_between ≈ tau_max/4 (i.e. stride ≈ m_max/4), bounded below by the observable’s autocorrelation time.off_diag_batch_size (int | None) – optional walker-chunk size for APPLYING each off-diagonal operator (not for the shadow chain, whose cost is independent of the operator’s width). None = apply to all walkers at once. An int cuts the peak (N_walkers, n_off, max_aff) intermediate by off_diag_batch_size/N_walkers and is BIT-IDENTICAL: the operator application is per-walker independent and each walker’s PRNG key comes from its absolute index. Must divide the walker count. Needed only for a WIDE operator (e.g. a pair sum over all sites). Costs sequential passes, so leave it None unless memory demands it.
_step_offset (int) – Internal. The number of steps the chain already holds;
run_from_HDF5passes it so that the fixed refresh schedule of a guide in the incremental mode counts absolute chain steps and a split run reproduces a contiguous one.
- Returns:
(walkers, energy, obs, stats, raw)by default (return_raw=True); withreturn_raw=Falsethe 4-tuple(walkers, energy, obs, stats).walkers: Walker batch after the production run (the production-final state; the extra lead-in and statistics steps are not applied to it).energy: dict p → float energy estimates.obs: dict name → dict p → value operator estimates (a float, or an(n_vals,)array for a vector-valued diagonal operator); empty when no operators are provided.
statsis a dict of run diagnostics — the end-of-run statistics() dict (health stats, offspring_k*, ancestor_m, ancestor_fraction, effective_sample_size) plus:stats['avg_log_weight']: the normalisation shift used in production; pass it back to reuse it.stats['it_per_sec']: production throughput.stats['runtime']: wall-clock seconds for the production loop.stats['total_wall_time']: complete call time, including warm-up, final estimation, and diagnostics.stats['number_of_devices']: 1.stats['autocorrelation/...']: integrated autocorrelation times of the energy, log weights and every operator series, in branching steps.stats['parameters/...']: the run’s inputs (n_steps, n_walkers, step_size, lam, method, p_array, m_array, off_diag_stride, operator names and content hashes, dtypes, …) and the RNG stream:parameters/rng_key(where production started) andparameters/rng_key_next(where it ended — pass it asrng_keyto continue).
rawholds the per-step GFMC values and the metadata needed to re-estimate them (evaluate_observables(raw)needs no further arguments):raw['log_weights']: (n_steps,) log branch weights log G_n.raw['energy']: (n_steps,) per-step energies E_n.raw['diag']: dict name -> (n_steps, n_p) for a scalar operator or (n_steps, n_vals, n_p) for a vector-valued one; None without diagonal operators. Operators of different widths may share a run.raw['additional_diag']: same layout, for the m_max steps FOLLOWING this run, traced with this run’s ancestry; the lead-in a continuation substitutes for its own first rows (seerun_from_HDF5).raw['off_diag']: dict name -> (n_meas, n_p) forward-walk values, one row per MEASURED step; None without off-diagonal operators.raw['off_diag_norm']: dict name -> (n_meas, n_p) log shadow-chain weight products (same rows).raw['off_diag_steps']: (n_meas,) 0-based production step (within this call) at which each row was measured.raw['p_array'],raw['m_array'],raw['diag_operators'],raw['off_diag_operators']: the window grid and operator names; the last axis of every operator series is positional inp_array.
- Return type:
- pygfmc.engine.statistics(walkers, hamiltonian, step_size, guide, method, rng_key, avg_log_weight=None, lam=None, M=10, quiet=False, m_array=None, cache=None)
Run branching steps and collect diagnostic statistics.
One evolution pass provides three families of diagnostics:
Sampler health (first M steps): per-step min/max of the diagonal weight, survival probability p_d, diagonal sub-step count k, unique-configuration counts before/after each reconfiguration, and the changed/overlap mixing rates.
Offspring distribution (every step): from each reconfiguration’s parent map, the fraction of walkers with exactly k children for k = 0..3 plus an overflow bin (>= 4); fractions are in [0, 1] and sum to 1. fraction[0] is the per-step lineage-death rate (= the duplicate rate sum_{k>=2}, since resampling conserves N) and scales ~linearly with step_size.
Ancestor survival (optional, when
m_arrayis given): each walker’s step-0 ancestor label is carried through the resampling indices; at every m in m_array the fraction of distinct surviving ancestors and the Kish effective sample size N^2 / sum_i c_i^2 (c_i = number of descendants of ancestor i) are recorded — the quantity that bounds the pure estimator’s effective sample size at projection depth m = p/2. The same ancestry gives the lineage correlation of the local energy: the correlation between a walker’s e_L at depth m and its own step-0 ancestor’s, which measures in branching steps how long the population remembers its state. The run is extended to max(M, max(m_array)) steps; steps beyond M skip the (expensive) sampler-health diagnostics.
Start from a stored, equilibrated population for meaningful ancestor curves (with near-uniform weights systematic resampling is the identity map, so the first steps of a fresh population show fraction 1.0).
- Parameters:
walkers (Walker) – Walker batch to use as the starting point.
hamiltonian (Operator) – Hamiltonian object.
step_size (int | float) – Per-Markov-step size; forwarded to evolve_walkers (integer n_bra for the discrete methods, float beta for
cont_time).guide (Guide | None) – Guiding wavefunction or None.
method (str) – “single_step”, “multi_step”, or “cont_time”.
rng_key (Array) – JAX PRNGKey.
avg_log_weight (float | None) – Normalisation shift per sub-step (discrete methods) or per unit imaginary time (
cont_time); evolve_walkers subtracts it * step_size.None(default) usesestimate_average_log_branch_weight()on the given walkers. Reconfiguration normalises the weights, so every diagnostic here is invariant to this shift up to float rounding; it only keeps the log weights near zero. Passstats['avg_log_weight']of a run to reproduce that run’s arithmetic exactly.lam (float | None) – GFMC shift λ; required by the discrete methods, unused by
cont_time.M (int) – Number of full-diagnostic branching steps (default 10).
quiet (bool) – If True, suppress printed output.
m_array (Sequence[int] | None) – Optional list of ancestor-survival depths (in steps; tau = m * step_size). None (default) disables the lineage tracking and keeps the run at M steps.
cache (Any) – Optional guide.build_cache(hamiltonian) cache; computed internally when None.
The diagnostics run on the full population on one device. A population that only fits when sharded over several devices can exhaust memory here; pass a slice of it (
jax.tree_util.tree_map(lambda x: x[:n], walkers)) in that case, noting thateffective_sample_sizethen describes the slice.- Returns:
walkers: Walker batch after max(M, max(m_array or [0])) steps.stats: dict with the keys below.n_unique_pre(M,): unique configs before each reconfiguration.n_unique_post(M,): unique configs after each reconfiguration.unique_fraction(M,): n_unique_post / N_walkers – the walker diversity rho plotted against the step size in the paper’s convergence figure. Around 0.95 is a healthy value.survival_rate(n_total,): 1 - offspring_k0, i.e. the fraction of walkers that produce at least one offspring. NOTE this is not the same as unique_fraction: it counts surviving parent slots, not distinct configurations, so two survivors carrying the same config count twice. The two agree only when the configuration space is large compared to N_walkers (they differ by a factor of ~3 for an L=8 chain with 256 walkers).diag_min(M,): per-step minimum diagonal weight.diag_max(M,): per-step maximum diagonal weight.p_d_min/p_d_max/p_d_mean/hold_steps_mean/hold_steps_max(M,): discrete methods only – the stay probability and the sampled geometric hold length in sub-steps.escape_rate_min/escape_rate_max/escape_rate_mean/hold_time_mean/hold_time_max/hops_per_step_mean/hops_per_step_max(M,):cont_timeonly – the guide-transformed escape rate Gamma, the sampled exponential hold time, and Gamma*step_size = the average number of configuration updates per branching step (see the block that sets them).changed_rate(M,): per-step fraction of walkers whose config changed from step 0.overlap_rate(M,): per-step fraction of step-t configs present in the step-0 population.H_diag_max_observed/H_diag_min_observed(1,): scalar extremes of the diagonal matrix element H_diag(x) over all walkers and all M health steps (diag_min/max are the raw rates[0] extremes; the max is the number lambda tuning needs: choose lam >= it + margin).offspring_k<j>(n_total,): fraction of walkers with exactly j children per step, j = 0..3.offspring_k4plus(n_total,): fraction with >= 4 children.ancestor_m/ancestor_fraction/effective_sample_size(len(m_array),): only when m_array is given. ancestor_fraction is a FRACTION in (0,1]: #{c_i > 0} / N. effective_sample_size is a COUNT in [1, N]: Kish’s ESS, n_eff = N^2 / sum_i c_i^2 (equivalently (sum_i c_i)^2 / sum_i c_i^2, since sum_i c_i = N), = 1 / P(two random walkers share an ancestor). At m = 0 it is N.ancestor_correlation(len(m_array),): only when m_array is given. Pearson correlation between the local energy of each walker at depth m and that of its step-0 ancestor (every walker paired with its own ancestor, so lineages with many descendants weigh more). 1 at m = 0, decaying towards 0 over the population’s decorrelation time; the per-walker counterpart of the run’sautocorrelation/energy_estimator. NaN when every walker has the same local energy.
- Return type:
(walkers, stats)
- pygfmc.engine.evaluate_observables(raw, p_array=None, n_blocks=None)
Re-evaluate the energy and the observables from raw per-step series.
Takes the
rawdictrun/run_gfmcreturn, or the oneload_raw_from_HDF5()reads back from a file; both carry the p grid (raw['p_array'],raw['m_array']) and the operator names alongside the series, so the call needs nothing else. Arawassembled by hand – an analysis script slicing the series – may carry neither, and thenp_arrayis required: the operator arrays are indexed by POSITION in the grid and nothing in them says what it was. Operators of such araware named by their mapping keys, or positionally (diag_0,off_diag_0, …) for dense arrays.The windowed values a run returns are fixed at the grid that run used. The raw series are not, and because a file holds one continuous chain they can be re-estimated over the whole of it – across run boundaries, which is legitimate here precisely because the boundaries are joins in one chain rather than seams between independent ones.
The energy takes any
p. The observables do not: their ancestry depth and forward-walking shadow chains are fixed while the chain is walking, so they are only ever available at the grid they were measured on, and asking for other values returns the energy there and the observables at that grid.Passing
n_blocksadditionally returns statistical errors from a blocked jackknife: the chain is cut inton_blockscontiguous blocks, each is deleted in turn and the whole estimator recomputed on the rest, and the error is the inflated scatter of those replicas,sqrt((nb-1)/nb * sum (x_b - mean)^2). Recomputing rather than averaging per-block values is what makes it correct for a ratio estimator likesum G*E / sum G, and it reuses_estimate_observablesso the replicas inherit its window bookkeeping unchanged. If the sample count is not divisible byn_blocks, trailing samples are discarded with a warning, from BOTH the point estimates and the error calculation.The block length is what matters, and it must exceed both the autocorrelation time and the window length: deleting one step from a 20000-step chain with tau_int = 9 underestimated the error 4-fold, and the estimate only settled once blocks were longer than ~4p. Choose
n_blocksso that the block lengthn_steps / n_blocksclearsmax(2*tau_int, p)comfortably, and keep at least ~20 blocks or the error on the error takes over. Two effects push the same way here: a replica is the surviving steps concatenated, so windows spanning the cut combine weights from either side of it, which understates the error by ~p/blen (measured 19 % at blen = 2p, under 5 % at blen = 10p). Clearing the block-length condition suppresses that along with the autocorrelation, and at the plateau it is below the ~1/sqrt(2*n_blocks) uncertainty of the error estimate itself. The reliable way to choose it is to scann_blocksand take the value where the error stops growing: on the chain above it rose 8.4e-3, 8.6e-3, 8.9e-3 for block lengths 40, 80, 160 and settled there. tau_int for each series is already in the file underrun_<n>/statistics/autocorrelation/.Note that this is the statistical error only. It says nothing about the systematic bias of a finite
p, which is the larger danger at deep projection: an over-projected estimate can be far off while carrying a small error bar, because the surviving lineages agree with each other. Independent runs across severalpremain the only check for that.- Parameters:
raw (Mapping[str, Any]) –
rawdict fromrun/run_gfmc, or fromload_raw_from_HDF5().energyandlog_weightsare required;diag/off_diagand their companions are used when present.p_array (Sequence[int] | ndarray | None) – Window sizes for the energy. Defaults to the grid the run used (
raw['p_array']), in which case energy and observables share one grid; any other grid re-windows the energy alone. For a hand-builtrawwithoutp_arrayit is also the grid the operator arrays are taken to sit on.n_blocks (int | None) – Number of jackknife blocks.
Noneskips the error estimate and returns the point estimates alone.
- Returns:
(energy, obs)inrun_gfmc’s own form –dictp -> energy anddictname ->dictp -> value – or(energy, obs, energy_err, obs_err)with the same key structure whenn_blocksis given.obsis empty whenrawholds no operator series.- Raises:
ValueError – when
rawholds no energy or log-weight series, when no p grid is available from eitherp_arrayorraw, when the operator arrays do not match the grid they are said to be on, or whenn_blocksleaves fewer than 2 blocks.- Return type:
- pygfmc.engine.write_to_HDF5(filename, stats, walkers=None, energy=None, obs=None, raw=None, walker_filename=None, verbose=True)
Append one GFMC run to an HDF5 file as a self-contained group.
Creates the file if it does not exist, otherwise appends the next
/run_<n>(0-based).filenameandstatsare required; every other argument is optional.MUST_MATCHparameters are validated against the file’s/parametersgroup (and the RNG stream against the stored runs: a replayed key raises, a non-continuing key warns) before anything is written, and the write is skipped entirely if validation fails.- Parameters:
filename (str) – Path to the HDF5 file.
stats (Mapping[str, Any]) –
statsdict fromrun_gfmc. Required. Itsparameters/entries carry everything that identifies the run, so without them a file has nothing to validate an append against and nothing to rebuild a continuation from — the two things the format exists for. The fixed ones go to/parameters, the per-run ones torun_<n>/parameters, and everything measured (autocorrelation/*,it_per_sec,runtime,total_wall_time, the sampler-health and lineage series,avg_log_weight) torun_<n>/statistics.walkers (Walker | None) – Final
Walkerbatch. Stored as the top-level/current_walkerscheckpoint, overwriting the previous one — only the newest ensemble can continue the chain.energy (Mapping[int, float] | None) –
dictp → energy, as returned byrun_gfmc.obs (Mapping[str, Mapping[int, Any]] | None) –
dictname →dictp → value.raw (Mapping[str, Any] | None) –
rawdict fromrun_gfmc. Appended to the top-level/rawseries. Storing it is what makes the run re-estimable afterwards (viaload_raw_from_HDF5()andevaluate_observables()); without it only the windowed values for this run’sp_arraysurvive.walker_filename (str | None) – Write the ensemble to this separate file instead of embedding it, and record the path. Useful when the ensemble dwarfs the measurements or lives on faster storage.
verbose (bool) – Print one line naming the group written (default True);
Falsekeeps the call silent.
- Returns:
The index
nof the group just written.- Return type:
- pygfmc.engine.run_from_HDF5(filename, hamiltonian, guide=None, diag_operators=None, off_diag_operators=None, n_steps=None, walker_filename=None, **run_kwargs)
Continue a stored chain in one call, and return what
run_gfmcreturns.Everything the continuation needs already sits in the file — the fixed parameters in
/parameters, the ensemble in/current_walkers, the stream in the newest run’srng_key_next. This reads all of it, verifies the objects handed in against the stored fingerprints, and runs:walkers, energy, obs, stats, raw = run_from_HDF5( "out.h5", H, guide, diag_ops, off_diag_ops) write_to_HDF5("out.h5", stats, walkers, energy, obs, raw)
No warmup is performed. The stored ensemble is already equilibrated and its
avg_log_weightis passed through, so re-warming it would discard that equilibration and leave a discontinuity mid-chain.The diagonal observables come out bit-identical to an uninterrupted run. A fresh ancestry map cannot trace back past its own start, so the first
p/2rows would otherwise be zeros; the previous run recorded exactly those values by walkingm_maxsteps further while it still had the history, and they are substituted here. The off-diagonal observables need no such repair — their shadow chains only look forward.lam,step_size,p_array,methodandoff_diag_stridecome from the file rather than from arguments, which is what makes the continuation the same calculation instead of a similar one. Anything inMAY_DIFFERmay still be passed throughrun_kwargs—verbosityandnumber_of_outputstypically are.- Parameters:
filename (str) – File written by
write_to_HDF5().hamiltonian (Operator) – The Hamiltonian. Compared against the stored class name and fingerprint.
guide (Guide | None) – Guiding wavefunction, compared the same way.
diag_operators (Mapping[str, DiagonalOperator] | None) – Diagonal operator dict, compared by name, order and fingerprint.
off_diag_operators (Mapping[str, Operator] | None) – As
diag_operators.n_steps (int | None) – Steps to run. Defaults to the newest stored run’s
n_steps, so a self-looping campaign repeats equal blocks without restating the length.walker_filename (str | None) – Read the ensemble from this file instead of the embedded checkpoint.
**run_kwargs – Passed to
run_gfmc, overriding what was read from the file. Raises on anyMUST_MATCHname, since overriding one of those is precisely what the file is there to prevent.
- Returns:
(walkers, energy, obs, stats, raw)— exactlyrun’s tuple (a 4-tuple withoutrawifreturn_raw=Falseis passed).- Raises:
ValueError – on a parameter or fingerprint mismatch, an RNG stream replay, a missing ensemble, or a
MUST_MATCHoverride.- Return type:
- pygfmc.engine.load_raw_from_HDF5(filename)
Read a file’s raw series back in the layout
runreturns them in.A file written by
write_to_HDF5()holds ONE continuous chain: every run appends to the same/rawgroup, so what comes back here is the whole chain and not any single run’s slice. That is what makes re-estimating over it legitimate – the run boundaries are joins in one chain rather than seams between independent ones.run_<n>’sraw_step_begin/raw_step_endattributes record which rows each run contributed, for a caller that does want one run’s slice.The keys are the ones
runputs inraw–log_weights,energy,diagandoff_diag/off_diag_norm(mappings keyed by operator name when the names are recorded) andoff_diag_steps, so the result goes straight intoevaluate_observables(). Keys the file does not hold are absent rather than None.The metadata
p_array,m_array,diag_operatorsandoff_diag_operatorsare read from the file’s/parametersgroup, as in therawthatrunreturns. They matter because the operator arrays are indexed by POSITION in the p grid –off_diag[name][:, j]is the value at windowp_array[j], and nothing in the array itself says so.- Parameters:
filename (str) – File written by
write_to_HDF5().- Returns:
dictof the raw series, plus the metadata named above.- Raises:
ValueError – when the file stores no raw series, i.e. it was written without
raw=...and there is nothing to re-estimate from.- Return type:
- pygfmc.engine.check_run_compatible(filename, stats=None, **parameters)
Validate a planned run against a stored file before walking.
write_to_HDF5performs the same check, but only once the run has finished — by which point a mismatch has already cost the wall-clock. Call this first with the parameters you are about to use and the job stops immediately instead:check_run_compatible(path, n_walkers=N, lam=lam, step_size=8, method="multi_step", p_array=[4, 8, 16], hamiltonian=H, guide=guide, diag_operators=diag_ops, off_diag_operators=off_ops)
hamiltonian,guideand the two operator dicts may be passed as the objects themselves, in which case their class names, operator names and content fingerprints (seepytree_fingerprint()) are all derived and compared — so a retrained guide or an edited coupling is caught even though every name still agrees. Names are checked, not only counts — two runs measuring the same number of different operators produce datasets of identical shape whose rows mean different things, which no later shape check can catch.Accepted either as keyword arguments (bare names, no
parameters/prefix) or as astatsdict from a previousrun_gfmc. Unknown andMAY_DIFFERnames are ignored, so passing a whole parameter set is fine. A missing file is compatible with anything.- Raises:
ValueError – on a
MUST_MATCHmismatch or an RNG stream replay.
- pygfmc.engine.resume_from_HDF5(filename, hamiltonian=None, guide=None, diag_operators=None, off_diag_operators=None, walker_filename=None, dtype=None, config_dtype=None)
Assemble the
run_gfmcarguments that continue a stored chain.run_from_HDF5()does the whole continuation in one call; use this lower-level function when you want the pieces, e.g. to pass the stored ensemble and key to a run with different operators.Reads the newest run’s embedded or external checkpoint, validates its walker hash when recorded, and returns it with that run’s parameters. An explicit
walker_filenameoverrides the stored source but must still match the newest ensemble. Returns keyword arguments:kw = resume_from_HDF5("out.h5") walkers, E, obs, stats, raw = run_gfmc( kw.pop("walkers"), n_steps, H, step_size, guide, p_array, method=method, lam=lam, **kw) # the stored run's settings write_to_HDF5("out.h5", stats, walkers, E, obs, raw)
The returned
rng_keyis the stored production-final key, so the step keys continue past everything already walked, andwarmup_steps=0because the stored ensemble is already equilibrated — re-warming it would discard that equilibration and put a discontinuity in the middle of the chain, which is exactly what a single-chain-per-file layout is meant to prevent. The storedavg_log_weightis passed along for the same reason.Pass the objects the continuation will use and they are verified against the stored run before anything is returned, so a mismatch costs nothing:
kw = resume_from_HDF5("out.h5", hamiltonian=H, guide=guide, diag_operators=diag_ops)
This is the natural place for that check.
write_to_HDF5performs it as well, but only once the run has finished. Omitting the objects leaves the contents unverified and warns as much — the file records what the earlier runs used, and only the caller holds what this one is about to use.- Parameters:
filename (str) – File written by
write_to_HDF5().hamiltonian (Operator | None) – Hamiltonian the continuation will use. Verified by class name and content fingerprint against the stored run when given.
guide (Guide | None) – Guiding wavefunction, verified the same way.
diag_operators (Mapping[str, DiagonalOperator] | None) – Diagonal operator dict, verified by name, order and fingerprint.
off_diag_operators (Mapping[str, Operator] | None) – As
diag_operators.walker_filename (str | None) – Read the ensemble from this file instead of the embedded checkpoint (defaults to the path recorded at write time, if any).
dtype (Any) – JAX dtype for weights/local energies. Default: the
weight_dtyperecorded in the file.config_dtype (Any) – JAX dtype for configurations. Default: the recorded
config_dtype.
- Returns:
walkers,avg_log_weight,rng_key(the stored production-final key, so the continuation resumes the stream) andwarmup_steps.- Return type:
dictofrun_gfmckeyword arguments
Building blocks
The functions the drivers are made of, for custom production loops and diagnostics.
They are imported from their modules, e.g. from pygfmc.engine.evolution import evolve_walkers.
|
Evolve the full walker batch (vmapped over walkers). |
|
|
|
Forward-walking pure estimator for a single (off-diagonal) operator. |
|
Return |
|
Raise |
|
Raise |
- pygfmc.engine.evolution.evolve_walkers(walkers, hamiltonian, step_size, guide, method, rng_key, avg_log_weight, lam=None, cache=None, rates=None, state=None, return_rates=False)
Evolve the full walker batch (vmapped over walkers).
- Parameters:
walkers (Walker) – Walker batch, shape (N_walkers, …)
hamiltonian (Operator) – Hamiltonian
step_size (int | float) – Per-Markov-step size. Interpreted as the number of imaginary-time sub-steps for the discrete methods
single_stepandmulti_step(must be a positive integer-valued float), and as the imaginary-time length $beta$ for the continuous methodcont_time(any positive float).guide (Guide | None) – guiding wavefunction or None
method (str) –
"single_step","multi_step", or"cont_time".rng_key (Array) – JAX PRNGKey
avg_log_weight (float) – normalisation shift subtracted from log_weight (per substep for the discrete methods, per unit imaginary time for
cont_time).lam (float | None) – GFMC shift λ. Required by the discrete methods, unused by
cont_time(the defaultNone).cache (Any) – Optional
guide.build_cache(hamiltonian)result, forwarded to the guide’s ratio routine.rates (Array | None) – Optional
(N_walkers, M_total)guide-transformed rates of the current configurations, as returned by the previous step withreturn_rates; the first rate evaluation of every walker is then skipped.state (Any) – Optional per-walker guide state of the incremental mode (see
pygfmc.Guide), updated after every move.return_rates (bool) – Also return the rates and the guide state of the final configurations.
- Returns:
Updated Walker batch with new config, log_weight, e_loc; with
return_ratesthe tuple(walkers, rates, state).- Return type:
- pygfmc.engine.evolution.evolve_carried(walkers, carried, hamiltonian, step_size, guide, method, rng_key, avg_log_weight, lam=None, cache=None)
evolve_walkers()with the carried pair(rates, state)in and out:(walkers, carried). With nothing carried the plain propagator runs and the final rates are not materialised (see_current_rates()); an entry that was not carried in stays None, so a scan carry keeps its pytree structure. The caller gathers the result with the resampling indices (gather_carried()).
- pygfmc.engine.evolution.forward_walk_operator(walkers, op, h_main, step_size, guide, method, rng_key, avg_log_weight, m_array, lam=None, cache_h=None, cache_op=None, shadow_key=None, op_batch_size=None)
Forward-walking pure estimator for a single (off-diagonal) operator.
opmay be any object implementing the operator protocol (pygfmc.Operator:M_total,get_rates, andgenerate_statewhenM_total > 1) — e.g. SpinHalf, PlaquetteModel, or ToricCode instances used as observables — whose guide-transformed rates are all non-negative.run_gfmccalls it at every measured production step (everyoff_diag_stride-th step), after reconfiguration. The drivers require evenp(m = p // 2shadow steps per window) for every operator withM_total > 1; a purely diagonal operator (M_total == 1) may use oddp, and atm = 0its value is the plain mean of the diagonal elements (log_norm = 0).- Apply op to the current walker ensemble:
each walker x_i picks a new config x’_i proportional to the operator transition weights w(x_i, x’)
vals[i] = Σ_{x’} w(x_i, x’) (total operator weight, ≥ 0)
Propagate the operator-applied ensemble along a shadow chain for max(m_array) steps with h_main. After every step the shadow ensemble is reconfigured, vals is remapped through the parent map, and the log of the reconfiguration weight G_t is accumulated.
Record, at each m in m_array, mean(vals) and the accumulated log weight product.
- Parameters:
walkers (Walker) – post-reconfiguration Walker batch (uniform weights)
op (Operator) – operator to measure (see above)
h_main (Operator) – main Hamiltonian (governs forward evolution)
step_size (int | float) – per-Markov-step size (integer sub-step count for the discrete methods, imaginary time beta for cont_time); forwarded to evolve_walkers.
guide (Guide | None) – guiding wavefunction or None
method (str) – “single_step”, “multi_step”, or “cont_time” (forwarded to evolve_walkers for the shadow chain)
rng_key (Array) – JAX PRNGKey for the operator application (and for the shadow chain when
shadow_keyis None)avg_log_weight (float) – log branch-weight normalisation scalar
m_array (Array) – list of shadow-chain depths (one per p value, m = p // 2; m = 0 reads the un-propagated ensemble)
lam (float | None) – GFMC shift for h_main; required by the discrete methods, unused by
cont_timecache_h (Any) – optional
guide.build_cache(h_main)resultcache_op (Any) – optional
guide.build_cache(op)resultshadow_key (Array | None) – optional PRNG key to start the shadow chain from.
run_gfmcpasses the main chain’s NEXT production key: the shadow runs the same key recurrence, so from the (near-identical) operator-applied ensemble it replays the main chain’s future draws, and the population-level weight noise largely cancels between the estimator’s numerator and denominator (correlated sampling). Keys are state-independent, so expectations are unchanged. None derives an independent stream fromrng_key(same expectation, larger variance at deep windows).op_batch_size (int | None) – optional walker-chunk size for the OPERATOR APPLICATION (step 1). None (default) applies the operator to all walkers at once. An int splits the walker batch into chunks of that size via
jax.lax.map, cutting the peak (N_walkers, n_off, max_aff) guide intermediate by op_batch_size/N_walkers. Bit-identical — the per-walker work and its PRNG key are unchanged, so no float reassociation is introduced. Must divide N_walkers. Use it when a wide operator (e.g. a site sum with many terms) would otherwise not fit in memory; it costs sequential passes.
- Returns:
pair of jnp.ndarray, each shape (len(m_array),), ordered by sorted m.
- val[j] = mean(vals after m_array[j] shadow steps) — the
operator measurement alone, linear and SIGNED.
- log_norm[j] = log of the shadow chain’s accumulated weight
product after m_array[j] steps. It drifts roughly linearly with m and can reach -100 and beyond at deep windows, which is why it is kept in log space.
The two channels are kept separate so that the large, compensating factors of the estimator cancel in the exponent before any exp() (a single linear product underflows float32 at deep windows), while the sign lives only in
val(a diagonal operator routed through this pipeline has a signed rates[0])._estimate_observablesconsumes the pair at measurement step k with m = p/2 asnumerator term = G_main^{-m} · G_aux^{+m} · val denominator term = G_main^{-m} · G_main^{+m}
i.e.
log_norm= log G_aux^{+m} is a numerator FACTOR: the ket half of the window is supplied by the shadow chain because that half carries the operator, while the denominator is main chain only. G_aux does not cancel — its ratio to G_main^{+m} is what converts the mixed estimator into the pure one.- Return type:
(val, log_norm)
- pygfmc.engine.evolution.require_lam(method, lam)
Return
lamvalidated formethod: a float for the discrete methods, which need the shift λ, andNoneforcont_time, which does not use it. RaisesValueErrorwhen a discrete method is called without a shift.
- pygfmc.engine.evolution.require_operator_protocol(op, role='hamiltonian')
Raise
TypeErrorunlessopimplementspygfmc.Operator.generate_stateis required for any operator that can actually produce a transition (M_total > 1). It is NOT required atM_total == 1: such an operator is purely diagonal, the propagators short-circuit it, and there is no successor state to generate – a measurement-only identity is the canonical example.
- pygfmc.engine.evolution.require_guide_protocol(guide)
Raise
TypeErrorunlessguideis None or implementspygfmc.Guide, i.e. at least one oflog_diff_from_operator,log_diff_from_configsandlog_psi.
|
Returns a scalar estimate of the average log branch weight across walkers. |
|
Check off-diagonal operator weights are non-negative. |
|
Number of distinct configurations in the walker population (on the host). |
|
Integrated autocorrelation time of a per-step series. |
- pygfmc.engine.estimators.estimate_average_log_branch_weight(walkers, hamiltonian, guide, method='cont_time', lam=None, cache=None)
Returns a scalar estimate of the average log branch weight across walkers.
Used as the starting value of
avg_log_weightbefore the warm-up ramp.- For the discrete methods (
single_step,multi_step;lamrequired): returns mean( log(lam - sum(rates)) ) — the average log branch weight per step.
- For
cont_time(the default;lamunused): returns mean( -sum(rates) ) — the average negative local energy per unit imaginary time, which is the correct per-unit-time normalisation for the continuous-time propagator (
avg_log_weight * betais subtracted from each walker’s accumulated log-weight). Using the discrete formula here would give a value ~log(lam - E_diag) ≪ -E_L, causing float32 overflow in the display and a bad initial estimate that the warmup ramp would have to correct.
- For the discrete methods (
- pygfmc.engine.estimators.check_off_diag_operator_positivity(walkers, off_diag_operators, guide, caches=None, verbose=True)
Check off-diagonal operator weights are non-negative.
M_total == 1 (diagonal only): always passes, any sign allowed. M_total > 1: all guide-transformed rates must be >= 0 (the estimator samples the successor by inverse CDF over the rates).
- Parameters:
walkers (Walker) – Walker batch; its first 100 configurations are tested.
off_diag_operators (Mapping[str, Operator]) – dict name -> operator.
guide (Guide | None) – guiding wavefunction or None.
caches (Mapping[str, Any] | None) – optional dict name ->
guide.build_cache(op).verbose (bool) – print a one-line report when the check passes (the drivers pass
verbosity >= 1).
- Raises:
ValueError – if any rate is negative
- pygfmc.engine.estimators.count_unique_configs(walkers)
Number of distinct configurations in the walker population (on the host).
- pygfmc.engine.estimators.autocorrelation(series, c=5.0)
Integrated autocorrelation time of a per-step series.
Returns
(tau_int, window).tau_intconverts steps into independent samples –N_eff = N / (2 tau_int)– and inflates the error on a mean bysqrt(2 tau_int).rho(t) is estimated by FFT in O(N log N). The self-consistent window is the smallest M with
M >= c * (0.5 + sum(abs(rho[1:M+1]))). The absolute correlation envelope prevents premature termination for oscillating correlations; the returned time uses the SIGNED sum. Anticorrelated series can legitimately have0 < tau_int < 0.5.tau_intis NaN for insufficient/constant/nonfinite data, when no window is found, or when the truncated signed sum is nonpositive. These cases do not support a reliable effective-sample-count estimate.NOTE this is the tau for statistics and budgeting. The initial transient decays with the SLOWEST MODE instead, which is longer, so it is the wrong number for choosing a warm-up length.
|
Multi-device GFMC main loop. |
|
Reconfiguration that resamples globally across all devices. |
|
Reshape walker arrays for pmap: (N, ...) → (n_devices, N//n_devices, ...). |
- pygfmc.engine.multi_gpu.run_gfmc_pmap(walkers, n_steps, hamiltonian, step_size, guide, p_array, *, method='cont_time', rng_key=None, lam=None, diag_operators=None, off_diag_operators=None, avg_log_weight=None, warmup_steps=None, verbosity=7, number_of_outputs=100, n_devices=None, return_raw=True, off_diag_stride=1, off_diag_batch_size=None, _step_offset=0)
Multi-device GFMC main loop. Energy + off-diagonal operators.
Args mirror
pygfmc.engine.driver.run_gfmc(), with the same defaults, so thatrun()behaves the same whatever the device count. In addition:- Parameters:
n_devices (int | None) – number of devices to shard walkers across (default: all visible devices). The walker count must be divisible by it.
- Differences from
run_gfmc: diag_operatorsraise NotImplementedError (ancestry tracing is single-device only).Off-diagonal measurement gating (
off_diag_stride > 1and the equal windows implied byreturn_raw=False) works as inrun_gfmc: the same steps are measured, unmeasured steps skip the shadow chain, andraw['off_diag_steps']gives each row’s step.Progress output: a progress line is printed from the first chunk on (
run_gfmcstarts once a full progress window has been walked), and the per-operator lines ofverbosity >= 8are not printed.
The warm-up follows
run_gfmc: the ramp runs wheneverwarmup_steps > 0; a suppliedavg_log_weightis kept fixed (the ramp then only equilibrates the walkers). The defaultwarmup_steps=Noneruns 1000 warm-up steps without anavg_log_weightand none with one; an explicitwarmup_steps=0withoutavg_log_weightraises.statscarries the sameparameters/*entries asrun_gfmc, includingparameters/rng_key_next.Off-diagonal operators use the forward-walking pure estimator with global reconfiguration of the shadow ensemble.
rng_keyis the production stream’s starting position, as inrun_gfmc: keys advancekey_{t+1} = split(key_t)in the scan carry and the warm-up, positivity check and statistics draw from side-streams, so the shadow chain replays the main chain’s own future draws (correlated sampling) and passingstats['parameters/rng_key_next']back asrng_key(with the returned walkers,warmup_steps=0and the returnedavg_log_weight) continues the chain bit-identically on the same device count. The chain is NOT the single-device chain at equal seed: the evolve key carries afold_in(device_id)and reconfiguration is global over all shards, so sharding changes the trajectory — as a different seed would, not as a bias.statscarries the samepygfmc.engine.estimators.statistics()fields asrun_gfmc— sampler health, the offspring distribution and ancestor survival /effective_sample_size— so a consumer ofrun()sees one contract whatever the device count. That pass runs on the UNSHARDED ensemble and therefore on a single device: a population that only fits when sharded can OOM there, after production has finished.Returns the same tuple shape as
pygfmc.engine.driver.run_gfmc(): four values whenreturn_raw=False–statsalways comes back – and five (addingraw) whenreturn_raw=True.
- pygfmc.engine.multi_gpu.reconfiguration_global(walkers, rng_key, axis_name='devices', aux=None)
Reconfiguration that resamples globally across all devices.
Each device contributes its walker shard. Weights are gathered globally, a single systematic resampling is performed, and each device picks the slice of resampled walkers it owns.
The rng_key must be identical on all devices so they agree on the random offset of the systematic resample.
If aux is given (a pytree of arrays of shape (N_per_device, …)), it is also gathered, remapped through the resampled indices, and the per-device slice is returned as a fourth element. Useful for carrying per-walker auxiliary state through reconfiguration (forward-walking values, the carried rates).
Must be called inside
jax.pmapwith the matchingaxis_name.- Returns:
(walkers_shard, log_G_n, E_n[, aux_shard])withlog_G_nthe log of the global mean weight andE_nthe global weighted local energy, as inpygfmc.walkers.reconfiguration().- Return type:
- pygfmc.engine.multi_gpu.shard_walkers(walkers, n_devices=None)
Reshape walker arrays for pmap: (N, …) → (n_devices, N//n_devices, …).
Each leaf array gains a leading device axis. N must be divisible by n_devices.
pygfmc.hamiltonian
Hamiltonians, lattices, measurement operators and exact diagonalization.
|
Spin-1/2 XYZ model with longitudinal and transverse fields. |
|
Spin-S Hamiltonian; see the module docstring for H and the rate layout. |
Bose-Hubbard model; see the module docstring for H and the rate layout. |
|
Toric code Hamiltonian on an Lx × Ly torus. |
|
Plaquette ring-exchange / Rokhsar–Kivelson Hamiltonian. |
|
|
The k-resolved correlator |
|
\(S^{zz}(\vec k)\) for one or many wavevectors, from positions. |
|
Whether each wavevector lies in the reciprocal lattice of the system. |
|
A finite lattice: a site count, an edge list and optional metadata. |
|
Nearest-neighbour chain of |
|
Pyrochlore lattice built from explicit FCC unit-cell edge templates (O(N)). |
|
Kagome lattice of |
|
Lowest eigenpairs of |
Hamiltonians
- class pygfmc.hamiltonian.SpinHalf
Spin-1/2 XYZ model with longitudinal and transverse fields.
- H = Σ_<ij> [J_x S^x_i S^x_j + J_y S^y_i S^y_j + J_z S^z_i S^z_j]
− Σ_i [h_z S^z_i + h_x S^x_i]
The fields enter with a MINUS sign (the same convention as
ToricCode), so a positiveh_xis the sign-problem-free one.The exchange term is rewritten per edge as
J_pm (S+_i S-_j + S-_i S+_j) + J_pmpm (S+_i S+_j + S-_i S-_j) + J_z S^z_i S^z_jwithJ_pm = (J_x + J_y)/4andJ_pmpm = (J_x - J_y)/4. Configurations are ±1 floats, σ_i = 2 S^z_i. All couplings may be uniform or per-edge / per-site. Build instances withcreate().Layout of the rates returned by
get_rates():rates[0] : diagonal H_{σσ} rates[1 : 1+n_hx] : single-site flips, −h_x/2 each (n_hx = N if h_x != 0) rates[1+n_hx : M_total] : one slot per edge, J_pm if the two spins are anti-parallel, J_pmpm if parallel (n_jpm = n_edges if J_pm or J_pmpm != 0)Sign-problem-free convention:
J_pm ≤ 0,J_pmpm ≤ 0andh_x ≥ 0;J_zandh_zare unrestricted.createenforces all three by raising (never by swapping couplings). An off-diagonal measurement operator needs the opposite sign (rates ≥ 0) and is built withsign_check=False: for+Σ_i S^x_ithat iscreate([], N=N, h_x=-1.0, sign_check=False), whose rates−h_x/2are non-negative.- property edges
Reconstruct (n_edges, 2) from edge_i/edge_j (Python-side only).
- property n_hx
Number of transverse-field off-diagonal elements (static Python int).
- property n_jpm
Number of edge off-diagonal elements (static Python int).
- property n_off
Total number of off-diagonal elements (static Python int).
- property M_total
n_off + 1.
- Type:
Total number of transitions including diagonal (static Python int)
- classmethod create(edges, J_x=0, J_y=0, J_z=0, h_z=None, h_x=None, N=None, *, dtype=jnp.float32, sign_check=True)
Build a spin-1/2 XYZ Hamiltonian.
- Parameters:
edges – (n_edges, 2) site index pairs (may be empty).
J_x – scalar or (n_edges,) exchange couplings. They enter only through
J_pm = (J_x+J_y)/4andJ_pmpm = (J_x-J_y)/4;J_pmpm > 0raises whensign_checkis True (pass J_x and J_y the other way round yourself if that is a symmetry of your model – it is not whenh_x != 0).J_y – scalar or (n_edges,) exchange couplings. They enter only through
J_pm = (J_x+J_y)/4andJ_pmpm = (J_x-J_y)/4;J_pmpm > 0raises whensign_checkis True (pass J_x and J_y the other way round yourself if that is a symmetry of your model – it is not whenh_x != 0).J_z – scalar or (n_edges,) S^z S^z coupling (any sign).
h_z – scalar, (N,) or None; longitudinal field, entering as
−h_z Σ S^z_i(any sign).h_x – scalar, (N,) or None; transverse field, entering as
−h_x Σ S^x_i. Must be ≥ 0 whensign_checkis True.N – number of sites. Inferred from
edgesand any per-site array when None; give it explicitly for an operator without two-body terms (edges=[]).dtype – JAX dtype of the couplings.
sign_check – enforce the sign-problem-free convention: raise on
J_pm > 0,J_pmpm > 0orh_x < 0. Pass False for measurement operators, which needJ_pm >= 0and are never propagated.
- diagonal_weight(config)
Return just the diagonal transition weight (scalar).
Equivalent to
get_rates(config)[0][0]but skips the (M_total,) rates allocation and the off-diagonal branches — useful for diagonal-operator measurement in hot loops.
- get_rates(config, return_configs=False)
Return transition rates, and optionally the full connected-configs tensor.
The default skips the (M_total, N) connected-configs tensor to avoid an O(n_walkers * M_total * N) allocation inside vmapped evolution loops; use
generate_state(config, idx)to materialize the single selected config. Passreturn_configs=Trueto obtain the full tensor.- Returns:
rates: shape (M_total,) — rates[0] = diagonal, rates[1:] = off-diagonal.new_configs: shape (M_total, N) whenreturn_configs=True(new_configs[0] = config unchanged, new_configs[1:] = connected configs for each off-diagonal element), else None.
- Return type:
(rates, new_configs)
- generate_state(config, idx)
Generate new config by applying off-diagonal transition.
idx == 0: return config unchanged (diagonal) idx >= 1: apply the (idx-1)-th off-diagonal flip
- property local_hilbert_space
Per-site values, ordered so that position i is basis digit i.
Matches this class’s own basis convention: digit = (1 - sigma)/2, so sigma = +1 is digit 0.
- class pygfmc.hamiltonian.Spin
Spin-S Hamiltonian; see the module docstring for H and the rate layout.
Build instances with
create().- property edges
(n_edges, 2) numpy array of site pairs.
- property n_edges
Number of edges (static Python int).
- property n_off
Number of off-diagonal transitions (static Python int).
- property M_total
n_off + 1.
- Type:
Total number of transitions including the diagonal
- property d
Local Hilbert-space dimension = 2S + 1.
- classmethod create(edges, S=0.5, J_z=0.0, J_pm=0.0, J_pmpm=0.0, h_z=None, h_pm=None, N=None, *, dtype=jnp.float32, sign_check=True)
Build a spin-S Hamiltonian.
- Parameters:
edges – (n_edges, 2) site index pairs.
S – spin per site (half-integer ≥ 0); 2S must be a non-negative int.
J_z – scalar or (n_edges,) Sz Sz coupling. Any sign (diagonal).
J_pm – scalar or (n_edges,) S+S- + h.c. coupling. ≤ 0 (SPF).
J_pmpm – scalar or (n_edges,) S+S+ + h.c. coupling. ≤ 0 (SPF).
h_z – scalar, (N,), or None. Any sign (diagonal); enters H as -h_z * sum_i S^z_i.
h_pm – scalar, (N,), or None. ≥ 0 (SPF). ONE amplitude multiplying both S^+ and S^-, i.e. the transverse field -h_pm * sum_i (S^+_i + S^-_i) = -2 h_pm * sum_i S^x_i. A single shared amplitude keeps H Hermitian.
N – number of sites. Inferred from
edgesand any per-site array when None; give it explicitly for an operator without two-body terms (edges=[]).dtype – JAX dtype for amplitudes.
sign_check – enforce SPF conventions if True.
- diagonal_weight(config)
Scalar H_{σσ}: J_z Σ Sz_i Sz_j + h_z Σ Sz_i.
- get_rates(config, return_configs=False)
Return
(rates, new_configs|None).rateshas shape(M_total,). See module docstring for layout.
- generate_state(config, idx)
Return the configuration after applying transition
idx.Every transition changes at most two sites, so the target sites and their increments are selected as scalars and the configuration is updated once, instead of building one full copy per transition block.
- move(config, idx)
The sites changed by transition
idxand their increments,(sites, deltas)of shape(2,)each (zero increments for the diagonalidx == 0);generate_stateisconfigwithdeltasadded atsites. Lets a guide update per-walker state after a move (seepygfmc.Guide).
- property local_hilbert_space
Per-site values, ordered so that position i is basis digit i.
Matches this class’s own basis convention: digit = m + S, so m = -S is digit 0.
- class pygfmc.hamiltonian.BoseHubbard
Bose-Hubbard model; see the module docstring for H and the rate layout.
Build instances with
create().- property edges
(n_edges, 2) numpy array of site pairs.
- property n_edges
Number of edges (static Python int).
- property n_off
Number of off-diagonal transitions (static Python int).
- property M_total
n_off + 1.
- Type:
Total number of transitions including the diagonal
- classmethod create(edges, t=0.0, eta=0.0, U=0.0, mu=0.0, h_pm=None, N=None, n_max=4, *, dtype=jnp.float32, sign_check=True)
Build a Bose-Hubbard Hamiltonian.
- Parameters:
edges – (n_edges, 2) array of (i, j) site index pairs.
t – scalar or (n_edges,) hopping amplitude. Must be ≥ 0 for sign-problem-free GFMC (the Hamiltonian carries the explicit minus sign in front of t).
eta – scalar or (n_edges,) nearest-neighbour pairing amplitude η, coupling the term -η (b†_i b†_j + b_i b_j) on each edge (same explicit minus-sign convention as
t). Must be ≥ 0 for sign-problem-free GFMC.U – scalar or (N,) onsite repulsion. Any sign (diagonal).
mu – scalar or (N,) chemical potential. Any sign (diagonal).
h_pm – scalar or (N,) amplitude multiplying BOTH b^dagger_i and b_i, i.e. -h_pm * sum_i (b^dagger_i + b_i) (minus sign as for the fields of SpinHalf and Spin; stored negated).
Noneto disable. One shared amplitude keeps H Hermitian. Must be ≥ 0 for sign-problem-free GFMC.N – number of sites. Inferred from
edgesand any per-site array when None; give it explicitly for an operator without two-body terms (edges=[]).n_max – hard cutoff on per-site occupation (≥ 1).
dtype – JAX dtype for amplitudes.
sign_check – if True, enforce the sign-problem-free convention.
- diagonal_weight(config)
Scalar H_{σσ}: (U/2) Σ n(n-1) − μ Σ n.
- get_rates(config, return_configs=False)
Return
(rates, new_configs|None).rateshas shape(M_total,)withrates[0]the diagonal and the off-diagonal layout described in the module docstring. Rates for moves that would exceed n_max (or go negative) are masked to 0.new_configs(whenreturn_configs=True) has shape(M_total, N)withnew_configs[0] == config.
- generate_state(config, idx)
Return the configuration after applying transition
idx.idx == 0returnsconfigunchanged. Off-diagonal indices index into the layout documented at the module level.Every transition changes at most two sites, so the target sites and their increments are selected as scalars and the configuration is updated once (the same scheme as
Spin.generate_state()), instead of building one full candidate configuration per block.
- move(config, idx)
The sites changed by transition
idxand their increments,(sites, deltas)of shape(2,)each (zero increments for the diagonalidx == 0);generate_stateisconfigwithdeltasadded atsites. Lets a guide update per-walker state after a move (seepygfmc.Guide).
- property local_hilbert_space
Per-site values, ordered so that position i is basis digit i.
Matches this class’s own basis convention: the occupation is itself the digit.
- class pygfmc.hamiltonian.ToricCode
Toric code Hamiltonian on an Lx × Ly torus.
- H = −A_s Σ_v ∏_{i∈star(v)} σ^x_i
− B_p Σ_p ∏_{i∈plaq(p)} σ^z_i − h_z Σ_i σ^z_i − h_x Σ_i σ^x_i
- Sites are the 2·Lx·Ly edges of the underlying square lattice:
horizontal edge h(x,y): index x + Lx·y (x∈[0,Lx), y∈[0,Ly)) vertical edge v(x,y): index Lx·Ly + x + Lx·y (x∈[0,Lx), y∈[0,Ly))
Spin values: config[i] ∈ {+1, −1} = σ^z eigenvalue.
Star (vertex) at (x,y) acts on edges: h(x,y), h(x−1,y), v(x,y), v(x,y−1). Plaquette (face) at (x,y) acts on edges: h(x,y), h(x,y+1), v(x,y), v(x+1,y).
Sign-problem-free convention: A_s ≥ 0, h_x ≥ 0 (off-diagonal rates −A_s, −h_x ≤ 0). No restriction on B_p or h_z (both are diagonal).
Choosing lam: it must exceed the largest diagonal element H_{σσ} visited.
lam = |B_p|·Lx·Ly + |h_z|·N, the worst case, is always safe. Equilibrated walkers sit near the bottom of the spectrum, so a smaller value often suffices; check the diagonal statistics.Couplings may be scalars or per-object arrays (see
create()).- Layout of rates returned by get_rates:
rates[0] : diagonal H_{σσ} rates[1 : 1+n_stars_off] : star moves (−A_s each; active for all σ) rates[1+n_stars_off : M_total] : single-site X moves (−h_x each)
- property n_off
Number of off-diagonal transitions (static Python int).
Required for a ToricCode instance to be usable as an
off_diag_operatorinrun_gfmc(which gates onop.n_off > 0); the star flips and the single-site X moves are the off-diagonal ones.
- property M_total
Total transitions including diagonal (static Python int).
- classmethod create(Lx, Ly, A_s=1.0, B_p=1.0, h_x=0.0, h_z=0.0, *, dtype=jnp.float32, sign_check=True)
Build the toric code Hamiltonian on an Lx × Ly torus.
- Parameters:
Lx – lattice dimensions
Ly – lattice dimensions
A_s – star (vertex) coefficient; scalar or (n_stars,) = (Lx*Ly,) per star. ≥ 0 for sign-problem-free GFMC.
B_p – plaquette coefficient; scalar or (n_plaq,) = (Lx*Ly,) per plaquette (diagonal, any sign).
h_x – σ^x field; scalar or (N,) per site. ≥ 0 for sign-problem-free GFMC.
h_z – σ^z field; scalar or (N,) per site (diagonal, any sign).
dtype – JAX dtype (default float32)
sign_check – raise ValueError if any A_s < 0 or h_x < 0 (default True)
Every coupling may be uniform (a scalar) or spatially varying (an array of the matching length), so disorder and boundary-modified stabilizers can be expressed without subclassing.
- diagonal_weight(config)
Scalar H_{σσ}: −B_p Σ_p ∏_{i∈p} σ^z_i − h_z Σ_i σ^z_i.
- get_rates(config, return_configs=False)
Rates (M_total,) and optionally the (M_total, N) connected-configs tensor.
Off-diagonal rates are (−A_s, −h_x) independent of config, because σ^x is always active (no flippability constraint). With per-star / per-site couplings they are config-independent but differ from slot to slot.
- generate_state(config, idx)
Apply off-diagonal transition idx to config.
idx == 0 : return config unchanged (diagonal) 1 <= idx <= n_stars_off : flip the 4 sites of star (idx−1) n_stars_off < idx : flip site (idx − 1 − n_stars_off)
- property local_hilbert_space
Per-site values, ordered so that position i is basis digit i.
Matches this class’s own basis convention: digit = (1 - sigma)/2, so sigma = +1 is digit 0.
- class pygfmc.hamiltonian.PlaquetteModel
Plaquette ring-exchange / Rokhsar–Kivelson Hamiltonian.
H = −g · Σ_p (flip term on plaquette p) + mu · Σ_p |flippable_p⟩⟨flippable_p|Each plaquette contributes
one off-diagonal element (the flip S⁺S⁻S⁺S⁻ … around the loop), active only when the plaquette is flippable — i.e. its spins strictly alternate as (+,−,+,−,…) or (−,+,−,+,…) in the order supplied to
create().one diagonal element (the RK counting potential μ_p) added when the plaquette is flippable.
- Layout of the rates returned by
get_rates: rates[0] : diagonal, Σ_p μ_p · 1[flippable_p] rates[1 : N_p + 1] : one entry per plaquette, −g_p if flippable else 0
The site count
Nis inferred from the plaquettes (max index + 1); configurations are ±1 floats. Plaquettes must have EVEN length (create()raises otherwise): an odd loop cannot alternate all the way round, so its “flip” would changeSz_totand is no ring exchange.- Fields:
- plaquettes int array (N_plaquette, plaquette_size) — site indices
per plaquette, listed consecutively around the loop.
- g flip coupling, shape (1,) uniform or (N_plaquette,).
Must be ≥ 0 (sign-problem-free convention): the off-diagonal matrix element is −g ≤ 0. The check can be disabled with
sign_check=Falsewhen constructing measurement operators — note that an off-diagonal measurement operator needs rates ≥ 0, i.e. g ≤ 0, the opposite restriction.- mu RK counting coupling (diagonal, any sign), shape
(1,) uniform or (N_plaquette,).
- property M_total
N_plaquette + 1.
- Type:
Total transitions incl. diagonal (static Python int)
- property n_off
Number of off-diagonal elements (static Python int).
- classmethod create(plaquettes, g=None, mu=None, *, dtype=jnp.float32, sign_check=True)
Build a plaquette ring-exchange / RK Hamiltonian.
- Parameters:
plaquettes – (N_plaquette, plaquette_size) site indices, listed consecutively around each loop.
N = max index + 1, and every site from 0 to N-1 must appear in at least one plaquette (a gap raisesValueError). The plaquette length must be EVEN (odd raises, whateversign_check).g – scalar, (N_plaquette,) or None; flip amplitude in
-g Σ flip. Must be ≥ 0 whensign_check.mu – scalar, (N_plaquette,) or None; RK potential on flippable plaquettes (any sign).
dtype – JAX dtype of the couplings.
sign_check – enforce
g ≥ 0. Pass False for an off-diagonal measurement operator, which instead needsg ≤ 0.
- property local_hilbert_space
Per-site values, ordered so that position i is basis digit i.
Matches this class’s own basis convention: spin-1/2 degrees of freedom encoded as +-1.
- diagonal_weight(config)
Scalar diagonal: Σ_p μ_p · 1[flippable_p]. Matches get_rates(c)[0][0].
- get_rates(config, return_configs=False)
Return rates (M_total,) and optionally the (M_total, N) connected-configs tensor.
- generate_state(config, idx)
Apply off-diagonal transition.
idx == 0 : return config unchanged (diagonal). idx >= 1 : flip all sites of plaquette (idx - 1).
Measurement operators
- pygfmc.hamiltonian.StructureFactor(positions, k, channel='SpSm', dtype=jnp.float32)
The k-resolved correlator
AB(0) + AB(k)as ONESpinHalf.O = sum_{i<j} [1 + cos(k.(r_i - r_j))] (A_iB_j + h.c.) O = AB(0) + AB(k) AB(q) = sum_{i<j} cos(q.(r_i - r_j)) (A_iB_j + h.c.)
Returns a SpinHalf with
M_total = 1 + n_kept, wheren_keptis the number of pairsi < jwith nonzero weight, for use as an off-diagonal measurement operator (off_diag_operatorsofrun_gfmc). Combined with the separately measured k = 0 operator it gives the second-moment correlation lengthxi^2 = [S(0)/S(k) - 1] / khat^2.Positions, as in
SzSzStructureFactor. The displacement of a pair isr_i - r_jof the positions as given; no periodic wrap is applied and no image is chosen. For akin the reciprocal lattice of the simulation cell (k.L = 2*pi*integerfor every periodic vectorLof the system, seeis_valid_k()) every periodic image gives the samecos(k.dr), so the result does not depend on which image the positions encode.kis not checked here.``channel=”SzSz”`` is diagonal and warns.
A = B = S^zgivesA_iB_j + h.c. = 2 S^z_iS^z_j, so the operator issum_{i<j} 2[1 + cos(k.dr)] S^z_iS^z_j, which is exactlySzSzStructureFactor(positions, [0, k])summed over its two wavevectors, i.e. the sameAB(0) + AB(k)structure as the other channels. The result hasM_total = 1and goes indiag_operators. AUserWarningpoints out thatSzSzStructureFactormeasures the same correlator for manykat once inO(N n_k)and needs neither the1 + cosshift nor thek = 0subtraction, since a diagonal operator has no sign constraint.- Parameters:
positions – (N, d) site coordinates; row
iis the position of the site at indexiinconfig, soNmust match the Hamiltonian’sN.dis arbitrary; it only has to matchk.k – (d,) wavevector, in the same units as
positions. One operator measures one wavevector.channel – one of the four below; case- and space-insensitive.
dtype – JAX dtype of the couplings.
channel
per-pair operator
"SpSm"S+S- + S-S+=2(S^xS^x + S^yS^y)(transverse)"SpSp"S+S+ + S-S-"SpSm+SpSp"4 S^xS^x"SzSz"2 S^zS^z(diagonal, warns)Why these off-diagonal three. get_rates emits one off-diagonal slot per edge,
where(anti_parallel, J_pm, J_pmpm), so per edge onlyPM = S+S- + S-S+andPP = S+S+ + S-S-exist, and an off-diagonal measurement needs every rate >= 0. The reachable set is therefore the non-negative conea*PM + b*PP(entered viaJ_x = 2(a+b)W,J_y = 2(a-b)W). SincePM + PP = 4 S^xS^xandPM - PP = 4 S^yS^y, YY needs a negative coefficient: build it as the"SpSm"operator minus the"SpSp"one.S.S = PM/2 + ZZmixes the diagonal and off-diagonal paths: measure"SpSm"(off-diagonal) and"SzSz"(diagonal) separately, then add. Mixed species (XY, XZ, …) are not in the span.The weight is ``1 + cos``, not ``cos``. Off-diagonal measurement samples a successor by inverse CDF over the rates, so mixed-sign weights are impossible; the
+1shift makes every weight non-negative at the price of the additiveAB(0), which the caller removes by subtracting the separately measured k = 0 operator. Pairs whose weight vanishes (cos(k.dr) = -1, to within1e-12, since an image other than the minimum one leaves a rounding residue) are dropped, soM_totaldoes not reflect the pair count.Memory. The operator has one transition slot per pair, about
N^2/2, and applying it to the ensemble materialises(n_walkers, M_total)rates (timesNfor a guide that needs the successor configurations). Passoff_diag_batch_sizetorun_gfmcto apply it in walker chunks; the result is bit-identical.Sign conditions.
create’sJ_pm <= 0/J_pmpm <= 0are propagator (sign-problem) conditions, whereas a measurement operator needs rates >= 0, i.e.J_pm >= 0on every channel andJ_pmpm > 0on the two withb > 0. Every channel therefore builds the operator withsign_check=False.Normalisation. For
"SpSm"and"SzSz",AB(q)above equals the ordered sumRe sum_{i!=j} exp(-i q.r_ij) A_iB_j, with no factor 2, because the adjoint ofA_iB_jis its i<->j partner (S+_jS-_i, resp.S^z_jS^z_i): listing each pair once and adding+h.c.reproduces the ordered sum. For"SzSz",AB(q)is therefore theS^{zz}(q)ofSzSzStructureFactor. The identity with the ordered sum fails for"SpSp", whose ordered sum is not Hermitian; the definition ofAB(q)above holds for every channel.
- class pygfmc.hamiltonian.SzSzStructureFactor(positions, k_vectors, dtype=jnp.float32)
\(S^{zz}(\vec k)\) for one or many wavevectors, from positions.
The observable is the pair sum
\[S^{zz}(\vec k) = \sum_{i<j} 2\cos(\vec k\cdot\vec{dr}_{ij}) S^z_i S^z_j ,\]evaluated through the equivalent single sum
\[\rho_{\vec k} = \sum_i e^{i\vec k\cdot\vec r_i}\sigma^z_i , \qquad |\rho_{\vec k}|^2 = \sum_{ij}\cos(\vec k\cdot(\vec r_i-\vec r_j)) \sigma^z_i\sigma^z_j ,\]so that (with \(\sigma^z=\pm1\), \(S^z=\sigma^z/2\))
diagonal_weightreturns \((|\rho_{\vec k}|^2 - N)/4\). Thei == jterms are always dropped: they are the constant \(N/4\) (sigma^2 = 1), carry nokdependence, and would shift every wavevector by the same amount.Why the single sum. It is an exact algebraic rewriting, holding configuration by configuration, and costs
O(N n_k)per walker instead of the pair sum’sO(N^2); measuring manykin one operator is therefore nearly free compared with a run that measures nothing.It is exact only for ``k`` in the reciprocal lattice of the simulation cell, i.e.
k . L = 2 pi x integerfor every supercell vectorL, which is exactly the set of wavevectors a finite periodic cell has. There every periodic image of a pair carries the same phase, so the result agrees with a pair sum built from minimum-image or absolute displacements. Off that grid the minimum-image pair sum is not a structure factor (it can even be negative, whereas \(|\rho_k|^2 \geq 0\)).Supplying valid wavevectors is the caller’s job; nothing here checks it (use
is_valid_k()). The positions alone cannot establish validity, because folded coordinates do not record the boundary identification (x = 0..7is equally an L=8 ring, a half-empty L=16 cell or an open chain). Generate the grid rather than typing it:cell = lat.extras['supercell_dims'][:, None] * lat.extras['primitive_vectors'] ks = 2 * np.pi * np.asarray(ms) @ np.linalg.inv(cell).T # ms: integer rows
(
pyrochloreships bothpositionsand that cell inextras.)``diagonal_weight`` returns one value per wavevector, which the GFMC pipeline carries as a trailing axis:
raw['diag'][name]is(n_steps, n_k, n_p),obs[name][p]is an(n_k,)array and the HDF5 datasetrun_<n>/observables/<name>is(n_p, n_k). Thekaxis carries no labels – storeop.k_vectorsyourself, e.g. asstats['parameters/k_vectors']beforewrite_to_HDF5(it is not aMUST_MATCHkey, so it cannot break a later append).stats['autocorrelation/diag/<name>']is resolved per wavevector, shape(n_k, n_p). The operator may share a run with diagonal operators of any other width; memory of its ancestry buffer grows linearly inn_k.- Parameters:
positions – Site coordinates, shape
(N, d). Rowiis the position of the site at indexiinconfig.k_vectors – One wavevector
(d,)or several(n_k, d).dtype – Dtype of the stored phase tables (default
jnp.float32). They are always built in float64, sincecos/sinof a largek.ris where the precision is lost, not the contraction.
Example
>>> ks = 2 * np.pi * np.arange(L) / L # allowed grid >>> sf = SzSzStructureFactor(positions, ks[:, None]) >>> *_, obs, stats, raw = run_gfmc(..., diag_operators={"Szz": sf}) >>> obs["Szz"][p] # (n_k,)
- diagonal_weight(config)
(n_k,)for a single(N,)config,(n, n_k)for a batch.
- pygfmc.hamiltonian.is_valid_k(k_vectors, lattice_vectors, atol=1e-8)
Whether each wavevector lies in the reciprocal lattice of the system.
A wavevector is valid when
k . L_j = 2 pi x integerfor every periodic vectorL_jof the system. Exactly then every periodic image of a pair carries the same phase: shifting a site to a periodic image,r -> r + sum_j m_j L_jwith integersm_j, changesk . (r_i - r_j)by a multiple of2 piand leavescos(k . (r_i - r_j))unchanged. The direct displacementsr_i - r_jof the positions as given therefore suffice, without choosing periodic images, soSzSzStructureFactorandStructureFactor()do not depend on which image the positions encode.- Parameters:
k_vectors – One wavevector
(d,)or several(n_k, d).lattice_vectors –
(d, d), rows the periodic vectors of the whole system, e.g.supercell_dims[:, None] * primitive_vectorsor kagome’sextras['torus']. They are the vectors the boundary identifies, not the primitive vectors of the unit cell.atol – Absolute tolerance on
k . L_j / (2 pi)being an integer.
- Returns:
boolfor a single(d,)wavevector, a boolean(n_k,)array otherwise.
Example
>>> L = np.array([[8.0]]) # L = 8 ring >>> is_valid_k(2 * np.pi * np.arange(3)[:, None] / 8, L) array([ True, True, True]) >>> is_valid_k([1.0], L) False
Lattices
- class pygfmc.hamiltonian.Lattice(name, N, edges, J_x=None, J_y=None, J_z=None, extras=<factory>)
A finite lattice: a site count, an edge list and optional metadata.
- J_x, J_y, J_z
optional suggested couplings (scalar or per-edge list); only
chain()sets them, and nothing in the package reads them automatically.
- pygfmc.hamiltonian.chain(n_sites, periodic=True)
Nearest-neighbour chain of
n_sitessites.The closing bond
(n_sites-1, 0)is added whenperiodicandn_sites > 2. Sets the suggested couplingsJ_x = J_y = -1,J_z = 1.
- pygfmc.hamiltonian.pyrochlore(nx, ny, nz, periodic=True)
Pyrochlore lattice built from explicit FCC unit-cell edge templates (O(N)).
4 sites per FCC unit cell. N = 4*nx*ny*nz, n_edges = N*3 (periodic). Cell index: z + y*nz + x*ny*nz (z changes fastest). Periodic boundaries require nx, ny, nz >= 2. Positions use a conventional cubic cell of side 1 (NN distance 1/(2√2));
extrascarries hexagons, positions and the supercell (seeLattice). With open boundaries only the hexagons lying completely inside the lattice are listed.12 edge templates per cell:
Within A-tetrahedron (offset 0,0,0): (0,1), (0,2), (0,3), (1,2), (1,3), (2,3) Inter-cell / B-tetrahedron connections: src=1 → tar=0, offset(+x, 0, 0) src=2 → tar=0, offset( 0,+y, 0) src=3 → tar=0, offset( 0, 0, +z) src=2 → tar=1, offset(-x,+y, 0) src=3 → tar=1, offset(-x, 0, +z) src=3 → tar=2, offset( 0,-y, +z)
- pygfmc.hamiltonian.kagome(Lx, Ly, periodic=True)
Kagome lattice of
LxxLyunit cells (O(N)).A triangular Bravais lattice with primitive vectors
a1 = (2, 0),a2 = (1, sqrt(3))and three sites per cell at0,a1/2anda2/2(nearest-neighbour distance 1), soN = 3*Lx*Lyandn_edges = 2N(periodic). Site index:3*(x + Lx*y) + bfor cell(x, y)and sublatticeb. Periodic boundaries requireLx, Ly >= 2.extrascarriespositions(N, 2),primitive_vectors,supercell_dims,sublattice_offsets, the torus vectorstorus(rowsLx*a1andLy*a2), and one hexagon per cell:hexagons(n_hex, 6), the site indices in counter-clockwise order starting at angle 0, andhexagon_centers(n_hex, 2). With open boundaries only the hexagons fully inside the lattice are listed.
Exact diagonalization
- pygfmc.hamiltonian.exact_diagonalization(hamiltonian, local_hilbert_space=None, N=None, diag_operators=None, off_diag_operators=None, number_of_states=1, return_states=False)
Lowest eigenpairs of
hamiltonian, with observables on each.- Parameters:
hamiltonian – Any object implementing the Hamiltonian protocol (
M_total,get_rates,generate_state). Its matrix is rebuilt fromget_ratesalone.local_hilbert_space – The per-site values, in basis-digit order, e.g.
(1, -1)for spin-1/2,(-1, 0, 1)for S = 1,(0, 1, 2)for bosons withn_max = 2. The order fixes the basis indexing of the returned states (energies do not depend on it). Defaults tohamiltonian.local_hilbert_space, which the pre-implemented classes provide.N – Number of sites; the basis has
len(local_hilbert_space) ** Nstates. Defaults tohamiltonian.N, so it only has to be given for a class that exposes noN– a user-defined Hamiltonian implementing just the protocol.diag_operators –
dictname → operator, measured asΣ_x |ψ_x|² O(x)viadiagonal_weight.off_diag_operators –
dictname → operator, measured asψᵀ O ψwithObuilt from its ownget_rates.number_of_states – How many lowest eigenpairs to solve for.
return_states – Also return the eigenvectors, shape
(dim, number_of_states). Needed byExactGroundState, which turns them into guide amplitudes.
- Returns:
(energies, diag_obs, off_diag_obs), or that tuple withstatesappended whenreturn_statesis True.energieshas shape(number_of_states,); the two dicts mirror the input dicts key for key, each value carrying one entry per state along the leading axis.
Note
The routine works in float64 throughout, whatever precision the caller is running in. The one residual it cannot remove is the Hamiltonian’s own dtype: a
dtype=jnp.float32model stores float32 couplings, and the spectrum inherits that ~1e-7 relative error; build the model withdtype=jnp.float64when it matters.- Raises:
ValueError – on a basis larger than 2**24 states, an
Norlocal_hilbert_spacecontradicting what the Hamiltonian reports (N,dor the local values), alocal_hilbert_spacewith repeated or non-finite values,number_of_statesoutside[1, dim], a transition of nonzero amplitude that leaves the basis, a non-Hermitian or non-finite operator matrix, or an estimated allocation above_MAX_BYTES.
pygfmc.wavefunction
Guiding wavefunctions and their training. train_with_netket needs the optional extra netket: pip install "pygfmc[netket]", or from source pip install -e "./codes[netket]" in the root of the cloned repository (see Package and Installation).
|
Jastrow guiding wavefunction with sparse two-body couplings. |
|
Cluster-CPS guide composed from one or more patch groups. |
|
Per-cluster MLP guide for SpinHalf, with one shared tanh MLP per cluster group. |
|
Guiding wavefunction with amplitudes from the exact ground state. |
|
Jastrow guide for boson occupations. |
|
Jastrow guide for spin-S configurations. |
|
Train |
|
A NetKet Hilbert space whose local states are exactly these values. |
- class pygfmc.wavefunction.JastrowSpinHalf(N, pairs, beta, h=None, dtype=jnp.float32)
Jastrow guiding wavefunction with sparse two-body couplings.
ψ_T(x) = exp(Σ_{(i,j) ∈ pairs} J_ij x_i x_j + Σ_i h_i x_i)
The constructor is Hamiltonian-agnostic: pass
N(number of sites), a(n_pairs, 2)pair list and the couplingsbeta. No reference to aSpinHalfoperator is kept on the guide. The coupling follows the conventionlog ψ = -Σ_<ij> β_ij x_i x_j + Σ_i h_i x_iand is stored internally asJ = -beta(exposed as the attributebeta).- Parameters:
N – int, total number of sites in the system.
pairs – (n_pairs, 2) int array of site index pairs (i, j). Order within a pair does not matter (canonicalised to (min, max) internally).
beta – required; a scalar (one tied coupling for every pair) or an array of length
n_pairs(one coupling per pair).h – one-body fields — a scalar (one tied field on every site), an (N,) float array (one per site), or None (no field).
dtype – JAX dtype for amplitudes.
The guide keeps the parameters in the form they were given, and
train_with_netket()trains exactly those: a scalarbetastays a single coupling,h=Nonestays without a field, a scalarhstays a single field.Operators:
log_diff_from_operator()andbuild_cache()index their output by theSpinHalftransition layout, so they accept aSpinHalf(Hamiltonian or measurement operator, e.g.StructureFactor()) or any operator exposingn_hx_static,n_jpm_static,edge_iandedge_j; any other operator (ToricCode,PlaquetteModel, …) raisesTypeError.log_psi()andlog_diff_from_configs()take configurations of any spin-1/2 operator.- property site_J
Couplings
J = -betaper site and partner slot,(N, max_deg).
- property h
One-body field per site,
(N,)(zeros when constructed withh=None).
- property tied_beta
True when
betawas a single coupling shared by all pairs.
- property beta
Per-pair coupling in the paper’s convention,
beta = -J.Read from the stored couplings (a tied scalar is broadcast to every pair), so it follows NetKet training or a
tree_mapand survives a pytree round trip / jit. The order is the order ofpairsgiven to the constructor.
- save(path, group='jastrow_spin_half', **attrs)
Write the Jastrow couplings to an HDF5 file.
Stores the pair list (in constructor order), per-pair J, the one-body field h, and N, so
load()rebuilds the same guide withbetain the same order. Any extra keyword arguments are written as scalar HDF5 attributes on the group, e.g.E_vmc=...,E_vmc_err=....- Parameters:
path – HDF5 file path (created or overwritten).
group – HDF5 group name to write under.
**attrs – extra metadata stored as group attributes.
- classmethod load(path, hamiltonian=None, group='jastrow_spin_half', dtype=jnp.float32)
Load a Jastrow saved with
save().- Parameters:
path – HDF5 file path written by
save().hamiltonian – optional
SpinHalf. If provided,Nis taken as the largest ofsaved_N,hamiltonian.Nand the highest edge index + 1, andhis zero-padded when the new system is larger; otherwiseN=saved_N.group – HDF5 group name to read from.
dtype – JAX dtype for the rebuilt guide.
- classmethod from_jax(dtype, N, site_other, site_J, site_valid_mask, h, pair_site, pair_slot, site_pair)
Rebuild a guide from its internal arrays (used by pytree unflatten).
Bypasses
__init__.pair_site/pair_slotlocate each pair’s coupling insite_J(they definebeta).
- log_psi(config, cache=None)
log ψ_T(config) = Σ_pairs J_ij x_i x_j + Σ_i h_i x_i.
Accepts a single
(N,)configuration or a(n, N)batch, matching the guide protocol’s third routine.cacheis accepted for protocol parity and unused: the sum is already O(N·max_deg) and there is nothing operator-specific to cache.Evaluated through
site_other/site_J, which store each pair from both endpoints – hence the 1/2 on the pair term.GFMC itself never calls this; it uses the ratio routines (
log_diff_from_operator/log_diff_from_configs). It exists for diagnostics and for testing those ratios against an explicit amplitude.
- log_diff_from_configs(config, new_configs, cache=None)
log ψ(x′) − log ψ(x)for each row ofnew_configs.Returns shape
(new_configs.shape[0],). Generic: works for anynew_configsregardless of how they were produced.cacheis accepted for protocol parity and unused.
- log_diff_from_operator(config, op, cache=None)
Return log-amplitude ratios indexed by a SpinHalf operator’s layout.
Layout matches
op.get_ratesof aSpinHalf:[diag (=0), op.n_hx site flips at sites 0..N-1, op.n_jpm op-edge flips]. No successor configurations are materialised.cache=None(default) rebuilds the per-edge external-partner mask on the fly via_edge_log_diff_for_edges. Pass aJastrowOpCache(obtained fromself.build_cache(op)) to use the cached fast path instead.opmust expose the SpinHalf layout (n_hx_static,n_jpm_static,edge_i,edge_j); any other operator raisesTypeError.
- build_cache(op)
Build and return the external-partner cache for
op’s edges.Returns a
JastrowOpCachepytree of shape-(n_edges_op, 2, max_deg)(edge_other, edge_J)arrays — the result of joiningself.site_other/self.site_Jwithop.edge_i/op.edge_jand masking out the i↔j bond endpoints themselves. Pass the returned cache tolog_diff_from_operator(..., cache=...).Results are memoised by the content of
op’s edge layout and of the couplingsJ(whichedge_Jbakes in), so repeated calls with the same operator (e.g. once fromrun_gfmcand again fromstatistics()) skip the rebuild, while a guide whose couplings changed (e.g. a trained copy) gets its own tables.opmust expose the SpinHalf layout (n_hx_static,n_jpm_static,edge_i,edge_j); any other operator raisesTypeError.
- class pygfmc.wavefunction.ClusterCPS(groups, amplitudes=None, init='zero', seed=None, dtype=jnp.float32)
Cluster-CPS guide composed from one or more patch groups.
- Parameters:
groups – list of patch-group specs (see the module docstring), or a single
(n_patches, m)cluster array for a one-group guide.amplitudes – optional LIST of amplitude tables, one per group. Each table has shape
(n_types, M_g)(or(M_g,)if the group has one table) after that group’s symmetry and parity reduction. If omitted, amplitudes are initialised according toinit. Whengroupsis a bare cluster array a single table may be passed without the list.init –
'zero'(default; the uniform statelog psi == 0) or'gaussian'(std 0.01) whenamplitudesis omitted.seed – RNG seed for Gaussian initialisation;
Nonedraws one, kept asguide.seed(Noneon a guide rebuilt from its pytree, e.g. after training).dtype – JAX dtype for amplitude arrays.
Attributes:
groups(tuple ofClusterCPSGroup),n_groups,N(largest site index + 1),n_params.Operators:
log_diff_from_operator()andbuild_cache()index their output by theSpinHalftransition layout, so they accept aSpinHalf(Hamiltonian or measurement operator) or any operator exposingn_hx_static,n_jpm_static,edge_iandedge_j; any other operator (ToricCode,PlaquetteModel, …) raisesTypeError.log_psi()andlog_diff_from_configs()take configurations of any spin-1/2 operator.- property n_params
Total number of amplitude-table entries over all groups.
- log_psi(configs, cache=None)
Evaluate
log psifor one config(N,)or a batch(..., N).cacheis accepted for protocol parity and ignored.
- log_diff_from_configs(config, new_configs, cache=None)
log psi(sigma') - log psi(sigma)for each row ofnew_configs.
- build_cache(op)
Build affected-patch caches for SpinHalf-like site and edge flips.
opmust expose the SpinHalf layout (n_hx_static,n_jpm_static,edge_i,edge_jand optionallyN); any other operator raisesTypeError. Returns aClusterCPSCache. The cache bakes in the current amplitudes, so rebuild it after changing them. Not memoised.
- log_diff_from_operator(config, op, cache=None)
Log-amplitude ratios in the SpinHalf transition layout.
Layout matches
op.get_rates:[diag (=0), n_hx site flips, n_jpm edge flips]. With aClusterCPSCachefrombuild_cache()each affected patch index is updated by index arithmetic (idx_new = idx_old ± 2^slot) without materialising any successor configuration; withcache=Nonethe successors are built viaop.get_rates(config, return_configs=True).opmust expose the SpinHalf layout either way; any other operator raisesTypeError.
- save(path, group='cps', **attrs)
Write the guide to
path(overwritten) undergroup.Each cluster group goes to a subgroup
group_<i>(clusters,amplitudes,cluster_type, orbit tables, optionalautomorphisms);n_groups,N,n_paramsand any extra**attrsare written as attributes ofgroup.
- class pygfmc.wavefunction.ClusterMLP(groups, params=None, seed=None, dtype=jnp.float32, N=None)
Per-cluster MLP guide for SpinHalf, with one shared tanh MLP per cluster group.
The log-amplitude is summed over groups
gand their clustersc:log ψ(x) = Σ_g Σ_{c ∈ clusters_g} MLP_g( x[clusters[g][c]] )
- Parameters:
groups – list of
[clusters, hidden]specs, one per group:clustersis an(n_clusters_g, k_g)int array (all clusters of a group share the sizek_g) andhiddena list of hidden-layer widths ([]orNonefor a linear model; a bare int is rejected).params – optional list, one entry per group, each a list of
(W_i, b_i)pairs for layersi = 0 … n_layers-1. The last layer is the output (no activation). If omitted, weights are drawn from a Gaussian of std 0.01 and the biases are zero; if given, the layer shapes must matchgroups.seed – RNG seed for the initialisation;
Nonedraws one, kept asguide.seed(Noneon a guide rebuilt from its pytree, e.g. after training).dtype – JAX dtype.
N – number of sites; defaults to the largest site index + 1 (pass it if trailing sites belong to no cluster).
Operators:
log_diff_from_operator()andbuild_cache()index their output by theSpinHalftransition layout, so they accept aSpinHalf(Hamiltonian or measurement operator) or any operator exposingn_hx_static,n_jpm_static,edge_iandedge_j; any other operator (ToricCode,PlaquetteModel, …) raisesTypeError.log_psi()andlog_diff_from_configs()take configurations of any spin-1/2 operator.- property n_params
Total number of weights and biases over all groups.
- log_psi(config, cache=None)
log ψ(x) = Σ_g Σ_{c ∈ clusters_g} MLP_g(x[clusters[g][c]]).
configmay be a single(N,)config or a batch(n_configs, N); the return is a scalar or(n_configs,)accordingly (same convention asClusterCPS.log_psi()).cacheis accepted for protocol parity but unused — the cluster cache only accelerateslog_diff_from_operator().
- log_diff_from_configs(config, new_configs, cache=None)
log ψ(x') − log ψ(x)for each row of new_configs.Brute-force path: runs full log_psi per row. Memory: O(M·N) for new_configs. Use
log_diff_from_operator(..., cache=cache)for the memory- and compute-light path when M can be huge.
- log_diff_from_operator(config, op, cache=None)
Log-amplitude ratios indexed by
op’s transition layout.- Layout matches
op.get_ratesof aSpinHalf: [diag (=0), n_hx site flips at sites 0..N-1, n_jpm op-edge flips]
With
cache=None: falls back tolog_diff_from_configs(config, op.get_rates(config, return_configs=True)[1]). With aClusterMLPCachefromself.build_cache(op): uses the affected-cluster lookup to recompute MLPs only for clusters whosex_cactually changed under each transition.opmust expose the SpinHalf layout (n_hx_static,n_jpm_static,edge_i,edge_j) either way; any other operator raisesTypeError.- Layout matches
- build_cache(op)
Build a
ClusterMLPCachefor fastlog_diff_from_operator(op).- For each group g, computes:
site_to_clusters[g]of shape(N, max_aff_g)— for each site, the cluster indices of group g containing that site (padded with -1 to a uniform max length).pair_to_clusters[g]of shape(n_jpm, max_aff_pair_g)— for each op-edge(edge_i[e], edge_j[e]), the union of cluster indices containing either endpoint.
Memoised by the byte content of
op’s edge layout.opmust expose the SpinHalf layout (n_hx_static,n_jpm_static,edge_i,edge_j); any other operator raisesTypeError.
- save(path, group='cluster_mlp', **attrs)
Write clusters + per-group variable-depth MLP params to HDF5.
- class pygfmc.wavefunction.ExactGroundState(hamiltonian, *, log_amp=None, dtype=jnp.float32, local_hilbert_space=None, N=None)
Guiding wavefunction with amplitudes from the exact ground state.
- Parameters:
hamiltonian – any Hamiltonian implementing the protocol (
M_total,get_rates,generate_state), e.g. a SpinHalf / Spin / BoseHubbard / ToricCode instance.log_amp – optional precomputed
log |ψ_0|, shape(d^N,). IfNone, ψ_0 is computed withpygfmc.hamiltonian.exact.exact_diagonalization()and the ground-state energy is stored asE0(NaN otherwise). The diagonalization returns ONE lowest eigenvector. When the ground state is degenerate – e.g. across sectors the Hamiltonian does not connect, such as the flippability sectors ofPlaquetteModelor theB_psectors ofToricCode– that vector is an arbitrary element of the degenerate subspace and can vanish on part of the basis (an entire sector), where|ψ_0|is floored at1e-30before the log. For a run inside one sector, supply that sector’s ground state throughlog_amp(e.g. fromexact_diagonalization(..., number_of_states=k, return_states=True)restricted or combined as needed). No degeneracy check is made.dtype – JAX dtype for the stored
log_amp.local_hilbert_space – Per-site values, e.g.
(-1, 1)for spin-1/2. Defaults tohamiltonian.local_hilbert_space.N – Number of sites. Defaults to
hamiltonian.N.
- basis_index(config)
Index of
configin the amplitude vector.
- log_diff_from_configs(config, new_configs=None, cache=None)
Log amplitude ratios for every transition, shape
(M_op,).When
new_configsis given (rows = successor configs of some operator, as produced byop.get_rates(config, return_configs=True)), the ratios follow that operator’s transition layout — this is the path GFMC takes (pygfmc.engine.evolution.transition_ratesalways passes the successors for this guide). Whennew_configsisNonethe successors are generated from the bound Hamiltonian:x_k_new = hamiltonian.generate_state(x, k), and element 0 is pinned to exactly0.0(diagonal, x unchanged).For Hamiltonians with bounded local Hilbert space (e.g. Spin S = 1, BoseHubbard) successor configs may be out of range for transitions whose rate is zero (e.g. S+ acting on m = +S). Those produce out-of-range basis indices, which we clip into the valid range so the lookup is well-defined. The corresponding rate is 0, so the looked-up amplitude is multiplied by zero and never affects the dynamics.
cacheis accepted for protocol parity and unused.
- save(path, group='exact_ground_state', **attrs)
Write
log_ampplus metadata to an HDF5 file.Datasets:
log_ampandlocal_hilbert_space. Group attributes:N,family(e.g."SpinHalf"),E0, plus any user keyword arguments.load()checksfamilyandNagainst the Hamiltonian it is rebound to.
- classmethod load(path, hamiltonian, group='exact_ground_state', dtype=jnp.float32)
Load a guide saved with
save()and bind it tohamiltonian.Unlike the other guides’
load,hamiltonianis required: the guide keeps a reference to it (forM_totalandgenerate_state). RaisesValueErrorif the saved family or N does not match the Hamiltonian.
- class pygfmc.wavefunction.JastrowBoseHubbard(N, pairs, beta, h=None, d=None, dtype=jnp.float32, incremental=False)
Jastrow guide for boson occupations.
\[\log\psi_T(x) = -\sum_{(i,j)} \beta_{ij} x_i x_j + \sum_i h_i x_i - \sum_i d_i x_i^2\]- Parameters:
N – number of sites.
pairs –
(n_pairs, 2)site pairs of the two-body term, e.g. the Hamiltonian edges. Order within a pair does not matter.beta – a scalar (one coupling shared by all pairs) or an array of length
n_pairs.beta > 0makes occupied neighbours less likely.h – one-body field (fugacity): a scalar (shared), an
(N,)array, or None (no field). It sets the mean density when the Hamiltonian does not conserve the particle number; at fixed particle number it is a constant and can be left out.d – on-site (Gutzwiller) term: a scalar (shared), an
(N,)array, or None (no term).d > 0suppresses multiple occupancy.dtype – JAX dtype of the amplitudes.
incremental – carry the weighted neighbour sums of every walker through the run and update them after each move instead of rebuilding them from all pairs at every rate evaluation (rebuilt every 100 chain steps against rounding drift). Not bit-identical to the default; the physics is unchanged.
The guide keeps every parameter in the form it was given, and
train_with_netket()trains exactly those: a scalar stays a single tied value,Nonestays absent.- property beta
Coupling of every pair,
(n_pairs,), in constructor order.
- property h
One-body field per site,
(N,)(zeros when constructed withh=None).
- property d
On-site term per site,
(N,)(zeros when constructed withd=None).
- log_psi(config, cache=None)
log ψ_Tof one(N,)configuration or an(n, N)batch.
- build_cache(op)
The walker-independent tables of
log_diff_from_operator()for aBoseHubbardoperator, as aJastrowBoseHubbardOpCache(None for any other operator). Memoised per operator layout and parameter values.
- init_state(config, cache=None)
The per-walker state of the incremental mode: the weighted neighbour sums of
config(weight-betaper pair, or 1 for a tiedbeta), shape(N,).
- update_state(state, config, sites, deltas, cache=None)
stateafter the move that addsdeltastosites(shape(2,)each, aspygfmc.hamiltonian.BoseHubbard.move()returns): every neighbourjof a moved sitesgetsw_sj Δ_s. Needs the operator’sbuild_cache()result, which holds the neighbour tables.
- log_diff_from_operator(config, op, cache=None, state=None)
log ψ_T(x') - log ψ_T(x)for every transition ofop.Fast path for
BoseHubbardoperators (entry 0, the diagonal, is 0), from the tables ofcachewhen it is the operator’sbuild_cache()result and built on the fly otherwise.stateare the walker’s neighbour sums frominit_state()/update_state()(rebuilt here when None). Any other operator falls back to its successor configurations.
- class pygfmc.wavefunction.JastrowSpin(N, pairs, beta, h=None, d=None, dtype=jnp.float32, incremental=False)
Jastrow guide for spin-S configurations.
\[\log\psi_T(x) = -\sum_{(i,j)} \beta_{ij} x_i x_j + \sum_i h_i x_i - \sum_i d_i x_i^2\]- Parameters:
N – number of sites.
pairs –
(n_pairs, 2)site pairs of the two-body term, e.g. the Hamiltonian edges.beta – a scalar (one coupling shared by all pairs) or an array of length
n_pairs.beta < 0favours aligned neighbours.h – one-body field: a scalar (shared), an
(N,)array, or None (no field).d – single-ion term: a scalar (shared), an
(N,)array, or None (no term).d > 0favours small|x_i|.dtype – JAX dtype of the amplitudes.
incremental – carry the weighted neighbour sums
W_i = Σ_j β_ij x_jof every walker through the run and update them after each move (two sites at most) instead of rebuilding them from all pairs at every rate evaluation. The sums are rebuilt every 100 chain steps, so float rounding cannot accumulate. The run is then no longer bit-identical to the default, which rebuilds the sums every time; the physics is unchanged.
The guide keeps every parameter in the form it was given, and
train_with_netket()trains exactly those: a scalar stays a single tied value,Nonestays absent.- property beta
Coupling of every pair,
(n_pairs,), in constructor order.
- property h
One-body field per site,
(N,)(zeros when constructed withh=None).
- property d
Single-ion term per site,
(N,)(zeros when constructed withd=None).
- log_psi(config, cache=None)
log ψ_Tof one(N,)configuration or an(n, N)batch.
- build_cache(op)
The walker-independent tables of
log_diff_from_operator()for aSpinoperator, as aJastrowSpinOpCache(None for any other operator). Memoised per operator layout and parameter values, so repeated calls with the same operator skip the rebuild.
- init_state(config, cache=None)
The per-walker state of the incremental mode: the weighted neighbour sums
W_i = Σ_j β_ij x_jofconfig, shape(N,).
- update_state(state, config, sites, deltas, cache=None)
stateafter the move that addsdeltastosites(shape(2,)each, aspygfmc.hamiltonian.Spin.move()returns): every neighbourjof a moved sitesgetsβ_sj Δ_s. Needs the operator’sbuild_cache()result, which holds the neighbour tables.
- log_diff_from_operator(config, op, cache=None, state=None)
log ψ_T(x') - log ψ_T(x)for every transition ofop.Fast path for
Spinoperators (entry 0, the diagonal, is 0), from the tables ofcachewhen it is the operator’sbuild_cache()result and built on the fly otherwise.stateare the walker’s neighbour sums frominit_state()/update_state()(rebuilt here when None). Any other operator falls back to its successor configurations.
- pygfmc.wavefunction.train_with_netket(hamiltonian, guide, local_hilbert_space=None, N=None, sampler=None, initial_states=None, n_iter=100, learning_rate=0.02, n_samples=1024, n_chains=16, n_discard_per_chain=8, sr=False, diag_shift=1e-3, qgt=None, seed=0, show_progress=False, return_history=False)
Train
guideonhamiltonianwith NetKet VMC and return a new guide.Needs only
guide.log_psiplus the guide’s pytree registration, andhamiltonian.get_rates. Nothing is mutated: a new guide of the same class comes back, with parameters cast to their original dtypes.- Parameters:
hamiltonian – Any object exposing
M_totalandget_rates.guide – Any registered-pytree guide exposing
log_psi.local_hilbert_space – Per-site values. Defaults to
hamiltonian.local_hilbert_space.N – Number of sites. Defaults to
hamiltonian.N.sampler – A ready-made NetKet sampler. It carries its own Hilbert space, so
local_hilbert_space/Nare then ignored. The default isMetropolisExchange, which swaps the values of two sites and so conserves their sum (S^z_totfor spins, the particle number for bosons); it is refused, with aValueError, for a Hamiltonian that changes this sum. It samples one sector: that ofinitial_stateswhen given, else the balanced one (S^z_tot = 0, or one particle per site), and swaps along the Hamiltonian’sedges(all site pairs if it has none). For a Hamiltonian that does not conserve this sum, e.g. a transverse field or a pair term, passnk.sampler.MetropolisLocal(hilbert), which changes single sites. For local spaces with more than two values, an explicit ergodic sampler is required: swaps preserve the full value histogram, not just the conserved sum. Use suitable transfer proposals for a constrained higher-spin/boson space.initial_states –
(n, N)configurations seeding the Metropolis chains, resampled to the chain count. This is the argument that changes results rather than just cost: chains started from a random product state can take very long to random-walk into a constrained manifold such as the spin-ice 2-in-2-out states, and they must lie in the sector of interest to begin with.n_iter – VMC settings.
learning_rate – VMC settings.
n_samples – VMC settings.
n_chains – VMC settings.
n_discard_per_chain – VMC settings.
sr – Stochastic reconfiguration, its regularisation and the quantum-geometric-tensor implementation.
srdefaults to False because NetKet’s default on-the-fly QGT can fail with recent JAX versions (JAX registersTraceras ajax.Array, so plum cannot disambiguate NetKet’s twois_scalaroverloads and SR dies withAmbiguousLookupErrorinsideqgt_onthefly_logic). With NetKet 3.21,qgt=nk.optimizer.qgt.QGTJacobianDenseworks, whileQGTJacobianPyTreeand the default fail. The optimiser isoptax.sgd(learning_rate).diag_shift – Stochastic reconfiguration, its regularisation and the quantum-geometric-tensor implementation.
srdefaults to False because NetKet’s default on-the-fly QGT can fail with recent JAX versions (JAX registersTraceras ajax.Array, so plum cannot disambiguate NetKet’s twois_scalaroverloads and SR dies withAmbiguousLookupErrorinsideqgt_onthefly_logic). With NetKet 3.21,qgt=nk.optimizer.qgt.QGTJacobianDenseworks, whileQGTJacobianPyTreeand the default fail. The optimiser isoptax.sgd(learning_rate).qgt – Stochastic reconfiguration, its regularisation and the quantum-geometric-tensor implementation.
srdefaults to False because NetKet’s default on-the-fly QGT can fail with recent JAX versions (JAX registersTraceras ajax.Array, so plum cannot disambiguate NetKet’s twois_scalaroverloads and SR dies withAmbiguousLookupErrorinsideqgt_onthefly_logic). With NetKet 3.21,qgt=nk.optimizer.qgt.QGTJacobianDenseworks, whileQGTJacobianPyTreeand the default fail. The optimiser isoptax.sgd(learning_rate).seed – Seeds parameter init and the sampler.
show_progress – Show NetKet’s progress bar.
return_history – Also return the per-iteration energy array.
- Returns:
The trained guide, or
(guide, history)whenreturn_history.
- pygfmc.wavefunction.make_hilbert(local_hilbert_space, N, total=None)
A NetKet Hilbert space whose local states are exactly these values.
Returns
nk.hilbert.HomogeneousHilbert(StaticRange(...), N). Requires an evenly spaced ladder, which every convention in use is ((-1, 1),(-S, ..., S),(0, ..., n_max)). Withtotal, the space is restricted to the configurations whose site values sum tototal.