Ising Models#
Ising-specific model, program, and training utilities.
For most problems ising_sample is the entry point: it goes from
(biases, edges, weights) to samples in one call, building the coloring,
autotuning the ladder, and drawing from the target. IsingEBM is the
lower-level construction — the factor graph from a list of nodes, edges,
biases, and coupling weights — for when you want to drive the sampler yourself.
Front door#
hamon.ising_sample(biases: Shaped[Array, n], edges: Shaped[Array, 'm 2'], weights: Shaped[Array, m], *, key: Key[Array, ''], beta: float | str = 1.0, n_samples: int = 1000, n_warmup: int = 500, steps_per_sample: int = 1, target_acceptance: float = 0.5, max_chains: int = 128, device: str | jaxlib._jax.Device | None = 'auto') -> tuple[Bool[Array, 'n_samples n'], dict]
#
Sample from an Ising model Boltzmann distribution via fully autotuned NRPT.
A thin Ising-specific front end over :func:hamon.autosample: it builds and
colors the graph, then autotunes the full NRPT configuration — chain
count, local-exploration count (gibbs_steps_per_round), and schedule —
before drawing from the cold chain. Unlike earlier versions, the
exploration count is no longer a fixed argument; it is discovered (and
device-calibrated) automatically.
A warning is logged if all coupling weights are zero (NRPT is unnecessary) or if all biases are identical (the model has no per-variable preference).
The energy function is
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
biases
|
Shaped[Array, n]
|
per-node bias array of shape |
required |
edges
|
Shaped[Array, 'm 2']
|
integer index pairs of shape |
required |
weights
|
Shaped[Array, m]
|
per-edge coupling of shape |
required |
key
|
Key[Array, '']
|
JAX PRNG key. |
required |
beta
|
float | str
|
inverse temperature for the target distribution, or |
1.0
|
n_samples
|
int
|
number of samples to return. |
1000
|
n_warmup
|
int
|
warmup steps before collecting samples. |
500
|
steps_per_sample
|
int
|
Gibbs sweeps between recorded samples. |
1
|
target_acceptance
|
float
|
desired per-pair swap acceptance rate for the chain-count search. Default 0.5 — the round-trip-optimal r = 1/2 (N ≈ 2Λ; Syed et al.). |
0.5
|
max_chains
|
int
|
ceiling on the discovered chain count. |
128
|
device
|
str | Device | None
|
where to run — |
'auto'
|
Returns:
| Type | Description |
|---|---|
Bool[Array, 'n_samples n']
|
A tuple |
dict
|
shape |
tuple[Bool[Array, 'n_samples n'], dict]
|
with keys |
tuple[Bool[Array, 'n_samples n'], dict]
|
|
tuple[Bool[Array, 'n_samples n'], dict]
|
|
tuple[Bool[Array, 'n_samples n'], dict]
|
class: |
Choosing β from the model#
beta="auto" reads the coldest useful temperature off the model's own
excitation-cost spectrum instead of guessing. Both halves of that estimator are
available on their own.
hamon.ising_estimate_beta(biases, edges, weights, *, gap_tol: float = 0.001, n_replicas: int = 64, seed: int = 0)
#
Estimate the coldest useful β for ground-state search on an Ising model.
A thin front end over :func:hamon.estimate_beta_max: extracts the
excitation-cost spectrum (exact on field-free forests, greedy-descent
probe elsewhere) and selects the smallest β whose predicted equilibrium
excess energy is at most gap_tol of the ground-state scale. Runs on
the host in milliseconds — no tuning, no compiles. Returns a
:class:hamon.BetaEstimate.
hamon.ising_excitation_costs(biases, edges, weights, *, n_replicas: int = 64, seed: int = 0) -> tuple[numpy.ndarray, float, str]
#
Elementary excitation-cost spectrum of an Ising landscape.
Returns (costs, energy_scale, method). Field-free forests are exact:
bond defects are independent with cost 2|J| and |E_GS| = Σ|J|.
Anything else uses the greedy-descent probe (2|local field| per-site
minima across replicas, energy scale = best minimum found). On highly
regular graphs the probe minima can have a zero local field at every site
(a genuine degeneracy), leaving no positive costs; there we fall back to
the coupling-magnitude spectrum 2|J|.
Model construction#
hamon.models.IsingEBM
#
An EBM with the energy function,
where \(S_1\) and \(S_2\) are the sets of biases and weights that make up the model, respectively. \(b_i\) represents the bias associated with the spin \(s_i\) and \(J_{ij}\) is a weight that couples \(s_i\) and \(s_j\). \(\beta\) is the usual temperature parameter.
Attributes:
nodes: the nodes that have an associated bias (i.e \(S_1\))biases: the bias associated with each node innodes.edges: the edges that have an associated weight (i.e \(S_2\))weights: the weight associated with each pair of nodes inedges.beta: the scalar temperature parameter for the model.
nodes and edges are stored as immutable, identity-hashed
sequences (a single pytree leaf each): the EBM is passed to jitted
functions (hinton_init, the NRPT round loop), and flattening plain
lists would visit and hash every node and edge endpoint — O(|graph|)
host work — on every call. They still index, iterate, and len()
like lists; with_beta passes the same objects through, which is
what keeps the jit cache hitting.
__init__(nodes: collections.abc.Sequence[hamon.pgm.AbstractNode], edges: collections.abc.Sequence[tuple[hamon.pgm.AbstractNode, hamon.pgm.AbstractNode]], biases: Array, weights: Array, beta: Array)
#
hamon.models.IsingSamplingProgram
#
Thin wrapper specializing :class:ModelSamplingProgram to an Ising model.
__init__(ebm: IsingEBM, free_blocks: list[tuple[hamon.block_management.Block, ...] | hamon.block_management.Block], clamped_blocks: list[hamon.block_management.Block], *, _gibbs_spec: hamon.block_sampling.BlockGibbsSpec | None = None)
#
hamon.models.hinton_init(key: Key[Array, ''], model: IsingEBM, blocks: list[hamon.block_management.Block[hamon.pgm.AbstractNode]], batch_shape: tuple[int, ...]) -> list[Bool[Array, 'batch_size block_size']]
#
Initialize the blocks according to the marginal bias.
Each binary unit \(i\) in a block is sampled independently as
where \(h_i\) is the bias of unit i and \(\beta\) is the inverse-temperature scaling factor. See Hinton (2012) for a discussion of this initialization heuristic.
Units are drawn independently across all blocks at once; blocks may have different sizes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Key[Array, '']
|
the JAX PRNG key to use |
required |
model
|
IsingEBM
|
the Ising model to initialize for |
required |
blocks
|
list[Block[AbstractNode]]
|
the blocks that are to be initialized |
required |
batch_shape
|
tuple[int, ...]
|
the pre-pended batch dimension |
required |
Returns:
| Type | Description |
|---|---|
list[Bool[Array, 'batch_size block_size']]
|
the initialized blocks as a list of bool arrays, one per block |
Training#
estimate_kl_grad computes the contrastive-divergence gradient of the KL
objective — the positive phase clamped to data, the negative phase free — for an
IsingTrainingSpec that pairs the model with its two sampling programs. Pass
return_negative_state=True and feed the returned state into the next step for
persistent contrastive divergence.
hamon.models.IsingTrainingSpec
#
Contains a complete specification of an Ising EBM that can be trained using sampling-based gradients.
Defines sampling programs and schedules that allow for collection of the positive and negative phase samples required for Monte Carlo estimation of the gradient of the KL-divergence between the model and a data distribution.
__init__(ebm: IsingEBM, data_blocks: list[hamon.block_management.Block], conditioning_blocks: list[hamon.block_management.Block], positive_sampling_blocks: list[tuple[hamon.block_management.Block, ...] | hamon.block_management.Block], negative_sampling_blocks: list[tuple[hamon.block_management.Block, ...] | hamon.block_management.Block], schedule_positive: SamplingSchedule, schedule_negative: SamplingSchedule)
#
hamon.models.estimate_moments(key: Key[Array, ''], first_moment_nodes: list[hamon.pgm.AbstractNode], second_moment_edges: list[tuple[hamon.pgm.AbstractNode, hamon.pgm.AbstractNode]], program: BlockSamplingProgram, schedule: SamplingSchedule, init_state: list[Array], clamped_data: list[Array], *, return_state: bool = False, device: str | jaxlib._jax.Device | None = 'auto')
#
Estimates the first and second moments of an Ising model Boltzmann distribution via sampling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Key[Array, '']
|
the jax PRNG key |
required |
first_moment_nodes
|
list[AbstractNode]
|
the nodes that represent the variables we want to estimate the first moments of |
required |
second_moment_edges
|
list[tuple[AbstractNode, AbstractNode]]
|
the edges that connect the variables we want to estimate the second moments of |
required |
program
|
BlockSamplingProgram
|
the |
required |
schedule
|
SamplingSchedule
|
the schedule to use for sampling |
required |
init_state
|
list[Array]
|
the variable values to use to initialize the sampling |
required |
clamped_data
|
list[Array]
|
the variable values to assign to the clamped nodes |
required |
return_state
|
bool
|
when True, also return the final free-block chain state (the state at the last recorded sample), so callers can continue the chain — e.g. persistent-chain (PCD) training. |
False
|
Returns:
the first and second moment data, plus the final chain state when
return_state is set.
hamon.models.estimate_kl_grad(key: Key[Array, ''], training_spec: IsingTrainingSpec, bias_nodes: list[hamon.pgm.AbstractNode], weight_edges: list[tuple[hamon.pgm.AbstractNode, hamon.pgm.AbstractNode]], data: list[Array], conditioning_values: list[Array], init_state_positive: list[Array], init_state_negative: list[Array], *, return_negative_state: bool = False, device: str | jaxlib._jax.Device | None = 'auto') -> tuple
#
Estimate the KL-gradients of an Ising model with respect to its weights and biases.
Uses the standard two-term Monte Carlo estimator of the gradient of the KL-divergence between an Ising model and a data distribution.
The gradients are:
Here, \(\langle\cdot\rangle_{+}\) denotes an expectation under the positive phase (data-clamped Boltzmann distribution) and \(\langle\cdot\rangle_{-}\) under the negative phase (model distribution).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Key[Array, '']
|
the JAX PRNG key |
required |
training_spec
|
IsingTrainingSpec
|
the Ising EBM for which to estimate the gradients |
required |
bias_nodes
|
list[AbstractNode]
|
the nodes for which to estimate the bias gradients |
required |
weight_edges
|
list[tuple[AbstractNode, AbstractNode]]
|
the edges for which to estimate the weight gradients |
required |
data
|
list[Array]
|
The data values to use for the positive phase of the gradient estimate. Each array has shape [batch nodes] |
required |
conditioning_values
|
list[Array]
|
values to assign to the nodes that the model is conditioned on. Each array has shape [nodes] |
required |
init_state_positive
|
list[Array]
|
initial state for the positive sampling chain. Each array has shape [n_chains_pos batch nodes] |
required |
init_state_negative
|
list[Array]
|
initial state for the negative sampling chain. Each array has shape [n_chains_neg nodes] |
required |
return_negative_state
|
bool
|
when True, append the negative chains' final
states (same structure as |
False
|
Returns:
the weight gradients and the bias gradients (plus the final negative
chain state when return_negative_state is set)