mod path¶
- module path¶
The pure-Rust SA driver loop. Multi-step paths between minima, where one hop cannot cross. Multi-step paths between minima, for landscapes where one hop cannot cross.
Basin hopping is a breadth-first search of depth one: propose, relax, accept. On a multi-funnel landscape that is the wrong depth, and the measurement says so rather than the intuition. From the structure a 75-point Lennard-Jones search settles into, none of 1800 single moves across the whole kernel set reaches anything lower; every move that leaves the basin lands higher, and the moves that land lowest do not leave. The target is 1.2 below and no single proposal goes there.
No bias, temperature law, collective variable or acquisition rule creates a path that is not in the move graph. What is needed is a search of depth greater than one: a sequence of intermediate structures between where the chain is and somewhere structurally different, each relaxed, so the corridor between two funnels is examined rather than jumped.
Fully relaxing an interpolated image does not do this, and the measurement is unambiguous. Between the structure a 75-point search settles into and each of four constructed morphologies, every image relaxed either back to the start or onto the endpoint: the deepest structure each path produced was that path’s own endpoint, to every printed digit. A fully relaxed image slides into whichever basin owns it and the corridor is never sampled.
transverse_pathremoves the component of the gradient along the path before descending, so an image falls to the valley floor while staying where it was placed. That projection is what a nudged elastic band uses and the reason it is used.Two things make it work rather than merely run.
The endpoints must be structurally different. Interpolating between two structures in one funnel lands back in it, which is consistent with the archive-based escape moves in this campaign scoring zero of eight on LJ75 while holding only structures the chain itself had reached.
The images must be relaxed individually and charged. An unrelaxed image is not a minimum and says nothing about which basin it belongs to, and a path whose cost is not charged is not comparable with the hopping it replaces.
Functions
-
fn interpolate_path<R, E>(a: ArrayView1<f64>, b: ArrayView1<f64>, n_images: usize, mut relax: R, mut left_start: E) -> PathOutcome¶
where
R: FnMut(ArrayView1<f64>) -> Option<(f64, Array1<f64>)>,
E: FnMut(ArrayView1<f64>) -> bool
¶ Relaxes images interpolated between two structures.
relaxreturns the value and relaxed structure, charging the caller’s ledger, and returnsNonewhen the budget is spent, which ends the path where it stands rather than silently returning a shorter one as complete.left_startdecides whether a relaxed image is outside the basin the path began in. It is the caller’s because basin identity is: on a cluster it is a shape distance againsta, and a threshold there is a length.Images are taken away from the endpoints. A fraction near zero relaxes back to
aand one near one tob, and neither is a crossing.
-
fn transverse_path<G>(a: ArrayView1<f64>, b: ArrayView1<f64>, n_images: usize, steps: usize, step_size: f64, mut grad: G) -> Vec<(f64, Array1<f64>)>¶
where
G: FnMut(ArrayView1<f64>) -> Option<(f64, Array1<f64>)>
¶ Relaxes images perpendicular to the path between two structures.
gradreturns the value and gradient at a point, charging the caller’s ledger, andNonewhen the budget is spent.Each image descends with the component of the gradient along the path removed, so it settles into the valley floor at its own position rather than sliding to an endpoint. The images that come out are candidate structures along the corridor; a caller relaxes the promising ones fully.
Structs and Unions
- struct PathOutcome¶
What a path attempt produced.
- best: Option<usize>¶
The deepest of them, if any.
- escapes: Vec<usize>¶
Images that left the starting basin, by the caller’s test.
Implementations
- impl PathOutcome¶
Functions
- struct PathPoint¶
A minimum found along a path, with where it came from.
- lambda: f64¶
Interpolation fraction the image started at, in
(0, 1).
- energy: f64¶
Relaxed value.
- state: Array1<f64>¶
Relaxed structure.
- struct StallDetector¶
Tracks whether a chain has stopped making progress, so a path is attempted when hopping has stalled rather than on a schedule.
A path costs many relaxations and is worth paying for only when the cheap mechanism has stopped working. The signal is the one this landscape gives plainly: near a deep minimum roughly nineteen proposals in twenty return to where they started, and the incumbent stops moving.
- patience: usize¶
Hops without an improvement before the chain counts as stalled.
- stalls: usize¶
Times a stall was reported.
Implementations
- impl StallDetector¶
Functions
- fn new(patience: usize) -> Self¶
Detector that reports a stall after
patiencehops without progress.
- fn observe(&mut self, energy: f64) -> bool¶
Records a hop’s value. Returns whether the chain is now stalled.
Reporting resets the counter, so a stall is reported once per stretch rather than on every hop after the threshold.
- fn since_improvement(&self) -> usize¶
Hops since the last improvement.
-
fn interpolate_path<R, E>(a: ArrayView1<f64>, b: ArrayView1<f64>, n_images: usize, mut relax: R, mut left_start: E) -> PathOutcome¶