mod minima_hopping

module minima_hopping

History-conditioned escape feedback, after Goedecker’s minima hopping.

Goedecker, J. Chem. Phys. 120, 9911 (2004).

The campaign’s own measurements say what is missing. From the structure a 75-point search settles into, none of 1800 single moves across the whole kernel set reaches anything lower, so crossing needs a sequence of accepted uphill quenches and a reason to accept them that does not destroy descent. A continuous collective variable cannot supply that reason: the Q4 gap between the two funnels is 0.023, under any usable deposition width.

This crate already keeps a history of visited basins and uses it to deposit bias. Minima hopping uses a history for something else: to scale the next escape. Revisiting a known minimum makes the next attempt more violent rather than the current one less attractive.

That difference is the whole mechanism and it is worth stating precisely, because the two are complementary and must stay separable:

escape feedback

basin bias

what a revisit raises

the escape scale

the potential on that basin

acceptance

adaptive threshold on the energy rise

Metropolis on F + V

transition regions

left crossable

filled if revisited

Goedecker argues against flooding transition regions for exactly this reason: the region between funnels has to stay crossable, so the response to a revisit should be a harder push rather than a higher potential where the chain needs to pass.

The escape scale grows geometrically while a chain revisits, which is the guarantee that no funnel is permanent: a chain that keeps returning keeps escalating until it leaves. Schoenborn, Goedecker, Roy and Oganov, J. Chem. Phys. 130, 144108 (2009), add feedback proportional to the visit count and report that this finds the LJ75 Marks decahedron where a cut-and-splice evolutionary algorithm does not.

Enums

enum Visit

What a quench was, relative to the history.

Same

The basin the chain was already in.

Known

A basin the history has seen before, but not the current one.

New

A basin not in the history.

Structs and Unions

struct EscapeFeedback

Escape scale and acceptance threshold, both driven by the history.

Defaults are Goedecker’s: the escape scale grows by 1.05 on a revisit and shrinks by the same factor on a discovery, and the acceptance threshold moves the other way, so roughly half of proposals are accepted without a temperature being chosen.

beta_same: f64

Growth on returning to the current basin.

beta_known: f64

Growth on reaching a basin already in the history.

beta_new: f64

Shrink on reaching a new basin.

visits_coeff: f64

Enhanced feedback: the known-basin growth is multiplied by 1 + visits_coeff * visits, so a repeatedly seen basin is escaped harder than one seen once (Schoenborn et al.).

alpha_accept: f64

Threshold multiplier on acceptance; below one.

alpha_reject: f64

Threshold multiplier on rejection; above one.

escape_ceiling: f64

Ceiling on the escape scale.

A streak of revisits grows the scale geometrically, and an unbounded scale eventually proposes a structure so scattered that its relaxation costs far more than an ordinary one. The ceiling keeps the charged cost of a proposal bounded without removing the escalation.

The default of four is set by that cost, not by taste. At a ceiling of sixty-four an LJ38 run paid 138 charged evaluations per hop against 39 without the controller, so the same budget bought 2173 hops rather than 7775 and the run solved 1 seed in 8 where the plain chain solved 8. Escalation that prices itself out of the budget is not escalation.

escape_floor: f64

Floor on the escape scale, so a run of discoveries cannot drive it to zero and freeze the search.

n_same: usize

Counts of each outcome, for reporting.

n_known: usize

Quenches that landed in a known other basin.

n_new: usize

Quenches that opened a new basin.

Implementations

impl EscapeFeedback

Functions

fn accept(&mut self, delta: f64) -> bool

Whether to accept a move of energy rise delta, updating the threshold.

The rule is Goedecker’s: accept when the rise is under the threshold, then move the threshold so the acceptance rate sits near a half. No temperature appears, which is the point: a Metropolis temperature cold enough to polish cannot cross and one hot enough to cross cannot polish, while this threshold adapts to whichever the chain is currently failing at.

fn classify(&self, current: Option<usize>, reached: usize) -> Visit

Classifies a quench without recording it.

fn discovery_rate(&self) -> f64

Fraction of quenches that were discoveries.

A run whose fraction is near zero is revisiting and should be escalating; one near one is wandering and should be settling. Reported because it says whether the feedback is doing anything.

fn escape(&self) -> f64

Current escape scale.

fn known_basins(&self) -> usize

Basins in the history.

fn new(escape: f64, threshold: f64) -> Self

Controller starting at escape and threshold, with Goedecker’s rates.

fn observe(&mut self, current: Option<usize>, reached: usize) -> Visit

Records a quench and updates the escape scale.

Returns what the quench was. The scale rises on a revisit and falls on a discovery, which is the feedback: a chain that keeps returning escalates until it leaves, and one that keeps finding new structures settles down to explore them.

fn threshold(&self) -> f64

Current acceptance threshold.

fn visits(&self, basin: usize) -> u32

Times basin has been recorded.