mod dos¶
- module dos¶
A posterior over the density of minima, and acceptance by entropy rather than by energy. A posterior over the density of minima, and acceptance by entropy.
Measured negative as an acceptance rule. Flat-histogram acceptance built on this estimator was refuted against paired controls (numbers and mechanism in
docs/derivations/), and no acceptance rule here is part of the recommended configuration. The joint multi-sweep estimator itself stands, with its tests, as a density-of-states tool.Why the acceptance rule is the trap
Basin hopping samples minima from
pi(m) ∝ exp(-E~_m / T)on the quenched surface. Marginalised onto energy that isp(E~) ∝ g(E~) exp(-E~ / T)where
gcounts minima per unit quenched energy. On a multi-funnel landscapegis not flat and not slowly varying: the icosahedral funnel of a 38-point Lennard-Jones cluster holds exponentially more minima than the face-centred-cubic funnel that contains the global minimum. At a temperature loose enough to keep the chain moving,gdominates and the chain sits where the states are. At a temperature tight enough for energy to beat multiplicity, the chain stops moving. The obstruction is a property of the target distribution and no proposal fixes it, which is why better moves – Hamiltonian dynamics, tempering, surrogate-accelerated acceptance, escape along soft modes – all measure at chance on this landscape.Sampling flat in energy instead
Give each state weight
1 / g(E~)rather thanexp(-E~ / T). The marginal over energy becomesg(E~) / g(E~), a constant, so the chain spends as much time at each quenched energy as at any other, and the rare deep energies get the same share as the abundant shallow ones. WithS = ln gthe acceptance for a symmetric proposal isa = min(1, exp(-[S(E~_new) - S(E~_old)]))Metropolis with entropy where the energy was. This is the multicanonical construction of Berg and Neuhaus (doi:10.1016/0370-2693(91)90256-P) and the flat-histogram target of Wang and Landau (doi:10.1103/PhysRevLett.86.2050), applied to the density of minima rather than of configurations, which is the object Bogdan, Wales and Calvo estimate for cluster thermodynamics (doi:10.1063/1.2148958).
It is also the coarse-graining the landscape actually admits. Under this target two minima at the same quenched energy are the same state, so the state space collapses from an exponential number of minima onto a single energy axis, and no structural descriptor is needed to say which states are equivalent.
What is Bayesian about it
Sis unknown and has to be learned from the run. Wang and Landau learn it by a histogram with a multiplicative schedule f -> sqrt(f), whose error saturates rather than converging (Belardinelli and Pereyra, doi:10.1103/PhysRevE.75.046701). HereScarries a posterior instead:Likelihood. Under a frozen weight
w = exp(-S^)the chain’s stationary distribution over bins isp_k ∝ exp(S_k - S^_k), so a visit histogramn_kobserves the residual directly: n_k ~ Poisson(exp(S_k - S^_k + c)).Prior. A second-difference Gaussian random walk on
S, the intrinsic smoothing prior of Poisson density estimation. It is proper on second differences, improper on the two degrees of freedom it deliberately does not constrain – level and slope – which is what lets the posterior extrapolateSlinearly past the deepest energy yet seen, with variance that grows as it goes.Exploration. The sampling weight is a draw from the posterior rather than its mean, so a bin the run has little evidence about is over-weighted in proportion to how little is known, and gets visited. Thompson sampling on the weight function.
The unvisited bin is where this separates from Wang-Landau in kind rather than in degree: a histogram method has nothing to say about a bin with no counts, and the prior gives it a value with an honest error bar.
Validity
Changing the weight function invalidates detailed balance, so the weight is frozen for a fixed sweep and refreshed between sweeps. Each sweep is then an exact Markov chain for its own target and the sequence is the standard stochastic-approximation scheme, not an adaptive chain whose invariance has to be argued.
Variables
- const BINS: usize¶
Default number of energy bins.
Functions
- fn cut_from(seen: &[f64], quantile: f64) -> Option<(f64, f64)>¶
The cut and width implied by a sweep’s visited energies.
The cut is a quantile of what the chain saw and the width is a spread of the same sample, so both descend as the run descends and neither is a tuned constant. A run that has stopped improving keeps the cut where it is, which is the right behaviour: the schedule follows progress rather than the clock.
Structs and Unions
- struct CutWeight¶
A flat target below a cut and a Boltzmann one above it.
Flat sampling across a whole energy range is the wrong target for a search. It buys barrier crossing by giving every energy an equal share of the run, and most of the range holds nothing worth the share: measured on 38 points, flat over the visited range solved 3 seeds in 24 where the plain Metropolis rule solved 15, because the budget went to the high-energy sea.
Restricting the flat region fixes that, and the restriction cannot be a wall. Crossing between funnels requires rising in quenched energy, so forbidding the rise rebuilds the trap the flat target was adopted to dissolve. What works is flat below and suppressed above:
-ln pi(E~) = S(E~) + max(0, E~ - c) / wBelow
cthe multiplicity of a funnel does not decide anything, so a barrier inside the reachable region is invisible. Abovecthe cost grows linearly, so the chain can still climbw-worth to cross and does not walk off to the top. The cut descends with the chain, which is the annealing: the schedule is read off the run’s own visited energies rather than supplied as a cooling curve.- cut: f64¶
Energy above which the target stops being flat.
- width: f64¶
Width of the suppression above the cut, in energy.
Implementations
- struct DensityOfStates¶
A posterior over
S = ln gon a binned energy axis.- refreshes: usize¶
Refreshes performed.
- clamped: usize¶
Samples that fell outside the window and were clamped into an end bin.
Implementations
- impl DensityOfStates¶
Functions
- fn bins(&self) -> usize¶
Number of bins.
- fn centre(&self, k: usize) -> f64¶
Centre energy of bin
k.
- fn draw<R: Rng + ?Sized>(&mut self, rng: &mut R) -> Weight¶
A weight function drawn from the posterior, frozen for one sweep.
Thompson sampling on
S: one draw per bin, correlated through the same standard normal along the axis so the drawn curve is smooth rather than a bin-independent rattle, since an unsmooth weight would make the acceptance ratio between neighbouring bins meaningless.
- fn entropy(&self, e: f64) -> (f64, f64)¶
Posterior mean and standard deviation of
Sat an energy.Inside the window this reads the bin. Below the window it extrapolates linearly from the slope at the low end, with the standard deviation growing linearly in the distance extrapolated, which is the behaviour the second-difference prior implies: level and slope are unconstrained by it, so a step past the evidence keeps the slope and loses confidence at a constant rate.
- fn inside(&self, e: f64) -> bool¶
Whether an energy sits inside the binned window.
- fn mean_weight(&mut self) -> Weight¶
The posterior mean as a weight function, for runs that want no exploration term.
- fn new(lo: f64, hi: f64, bins: usize) -> Self¶
A posterior over
[lo, hi]split intobinsbins, flat and uncertain before any evidence arrives.The initial state is deliberately
S = 0everywhere with a wide spread: flatSmakes the first sweep plain random-walk acceptance, which is the least committed thing to do before the run has seen an energy.
- fn observe(&mut self, e: f64)¶
Records a visit at quenched energy
e.
- fn pending(&self) -> usize¶
Samples pending since the last refresh.
- fn refresh(&mut self) -> bool¶
Folds the pending histogram into the posterior and clears it.
Returns false when there is nothing to learn from.
- fn temperature(&self, e: f64) -> (f64, f64)¶
Posterior mean and standard deviation of the statistical temperature at an energy.
The statistical temperature is the reciprocal slope of the entropy,
T_S = (dS/dE~)^-1, and it is the temperature at which a chain standing at that energy is critically mobile: the Metropolis ratio for a typical move is order one, neither frozen nor free. Kim, Straub and Keyes use it to drive dynamics directly (doi:10.1103/PhysRevLett.97.050601).Its behaviour is what a search wants and is the opposite of a cooling curve. At a funnel floor few new minima appear per unit energy, so the slope is small, the temperature is high, and the chain climbs out. In the high-energy sea minima are dense, the slope is large, the temperature is low, and the chain descends. It is read off the run’s own entropy rather than scheduled against the clock, so a chain that has stopped finding new minima at its current depth heats itself.
The slope is taken by central difference on the fitted mean, which is smooth by construction under the second-difference prior, and its spread follows from the two bins it is taken across.
- fn top_seen(&self) -> usize¶
Highest bin index the run has ever visited.
- struct EnergyBias¶
A well-tempered bias deposited in quenched energy.
The per-basin bias this crate carries fills the basin the chain stands in. Measured at 38 points, that is not the shape of the trap: of 17 failing seeds, 12 end at exactly -173.252378 and 4 at -173.134317, the floor of the icosahedral funnel. A funnel holds exponentially many basins, so filling them one at a time cannot fill it, and coarsening the basin metric does not help because a single length in coordinate space cannot tell a funnel’s variants from a different funnel: at a radius of 0.7 the run registers 365 basins and solves 55 of 72, at 2.0 it registers 7 and solves 24.
Energy separates what the length cannot. The funnel floor is an energy the chain returns to, so depositing there fills the funnel rather than one of its basins, and the coordinate costs nothing because every hop computes it.
Deposits are well tempered (Barducci, Bussi and Parrinello, doi:10.1103/PhysRevLett.100.020603): height falls as
exp(-V/((gamma-1) T))where the bias already stands, so the sum converges instead of growing without bound, and the sampled distribution is the well-tempered ensemble of Bonomi and Parrinello (doi:10.1103/PhysRevLett.104.190601) rather than a flat one. This is the part the flat-histogram acceptance got wrong: it forced every energy to an equal share, where tempering only broadens what the chain already samples.- w0: f64¶
Initial deposit height.
- gamma: f64¶
Tempering factor. One is no bias; large is untempered metadynamics.
- sigma_bins: f64¶
Width of a deposit, in bins.
- deposits: usize¶
Deposits made.
Implementations
- impl EnergyBias¶
Variables
- const FILL_DEPOSITS: f64¶
The number of deposits that fill a well one standard deviation deep.
The one free number in this construction, and it is dimensionless: every other scale is taken from the run’s own quenched-energy distribution, so nothing here carries units of a particular system’s energy and nothing is set per system.
Functions
- fn at(&self, e: f64) -> f64¶
The bias at an energy, held flat outside the binned window so a chain that leaves the range is neither pushed back nor pulled out.
- fn delta(&self, e_old: f64, e_new: f64, temp: f64) -> f64¶
The bias difference a move carries, in units of temperature, ready to be added to a Metropolis exponent.
- fn deposit(&mut self, e: f64, temp: f64)¶
Deposits at an energy, at the well-tempered height for the bias already standing there.
- fn from_sample(seen: &[f64], temp: f64, bins: usize) -> Option<Self>¶
A bias whose scales come from a sample of quenched energies.
The tempering factor is set by
(gamma - 1) T = sigma, so the bias broadens the energy distribution by about its own width, and the deposit height byw0 = sigma / FILL_DEPOSITS, so a well one standard deviation deep fills in a fixed number of deposits whatever the system’s energy scale. The window spans the sample padded by a standard deviation on each side.Returns
Nonewhen the sample is too small or degenerate to set a scale, which is the honest answer rather than a default.
- fn new(lo: f64, hi: f64, bins: usize, w0: f64, gamma: f64) -> Self¶
A bias over
[lo, hi], empty until something is deposited.
- fn peak(&self) -> f64¶
Largest bias standing anywhere, for reporting how filled the range is.
- struct Weight¶
A frozen weight function: the entropy curve one sweep accepts against.
Implementations
- impl Weight¶
Functions
- fn accept_prob(&self, e_old: f64, e_new: f64) -> f64¶
Acceptance probability for a move between two quenched energies.
Metropolis against
1 / g: a move to a rarer energy is always taken, and a move to a more abundant one is taken with the ratio of multiplicities. Both directions in energy are treated alike, which is what makes the sampled histogram flat rather than funnel-weighted.
- fn at(&self, e: f64) -> f64¶
The entropy this weight assigns to an energy, extrapolated linearly outside the binned window.