mod cluster_hopping¶
- module cluster_hopping¶
Basin hopping over quenched minima with a basin-keyed bias. Basin hopping over quenched minima with a bias keyed on basin identity.
Cluster global optimisation happens on the quenched landscape,
E_q(x) = E(local_min(x)), not on the raw surface: the funnel structure is only visible after relaxation, and a search on the raw surface finds nothing on a 38-atom Lennard-Jones cluster.Three things then decide whether a funnel can be left.
The relaxation is where the budget goes. A full one costs a few hundred charged evaluations and most trials land nowhere near the incumbent, so trials are screened by a short relaxation first and only promoted when they land within
Config::screen_marginof the incumbent. Measured on LJ38, screening took basin discovery from 27 to 327 at a fixed charge.The bias is keyed on basin identity rather than on a collective variable. A variable has to be chosen correctly or it cannot see the competition: on LJ75 the Marks decahedron and the structures a search settles into differ by 0.023 in the fourth Steinhardt parameter, narrower than any usable deposition width, so biasing on it fills both competitors at once.
The moves have to change the packing rather than perturb it, which is what
crate::movekernel::SurfaceRelocate,crate::movekernel::ShellRotateandcrate::movekernel::Symmetrisedo, and which of them pays at a given stage is decided online rather than fixed.Types
- type Grad<'a>¶
A borrow of one, for a caller that has a gradient to lend.
The two lifetimes are separate on purpose. Tying the trait object’s lifetime to the borrow makes the pair invariant, so a caller that holds a gradient and wants to lend it to a sequence of inner runs cannot reborrow it: it has one gradient and can hand it over once.
- type GradFn<'g>¶
Gradient of the objective, charged to the ledger by the caller.
Optional because only the soft-mode escape needs it: everything else in this driver works from relaxations alone.
- type Relax<'a>¶
Relaxes
x, charging every evaluation, and stopping when the budget ends.The relaxation is supplied by the caller because the objective, its gradient and the minimiser are the caller’s: this module owns the search, not the numerics under it.
- type Settle<'a>¶
Partial relaxation of the listed atoms in the frozen environment.
Arguments: ledger, structure, moved atom indices, descent steps. Returns the settled structure. The callee owns the objective and therefore the honest fractional price; it charges through
Ledger::charge_frac.
Functions
- fn optimize(cfg: &Config, ledger: &mut Ledger, relax: Relax<'_>, seed: u64) -> Outcome¶
Convenience entry point seeding its own start.
- fn optimize_with_gradient<'g>(cfg: &Config, ledger: &mut Ledger, relax: Relax<'_>, grad: Option<&mut GradFn<'g>>, seed: u64) -> Outcome¶
As
optimize, with a gradient for the soft-mode escape.
- fn optimize_with_settle<'g>(cfg: &Config, ledger: &mut Ledger, relax: Relax<'_>, grad: Option<&mut GradFn<'g>>, settle: Option<Settle<'_>>, seed: u64) -> Outcome¶
As
optimize_with_gradient, with a settle stage for the staged quench.
- fn random_cluster<R: Rng + ?Sized>(n: usize, density: f64, min_sep: f64, rng: &mut R) -> Array1<f64>¶
Seeds a non-overlapping configuration at liquid-like density.
Uniform draws over a container overlap almost surely at the sizes of interest, and a relaxation cannot recover from that.
- fn random_cluster_in_radius<R: Rng + ?Sized>(n: usize, radius: f64, min_sep: f64, rng: &mut R) -> Array1<f64>¶
- fn repack_rigid_groups<R: Rng + ?Sized>(template: ArrayView1<f64>, groups: &[Vec<usize>], length_scale: f64, rng: &mut R) -> Array1<f64>¶
Seeds a non-overlapping configuration inside a declared sphere radius. Re-place rigid groups on a new sphere, keeping each group’s internals.
The shell dimensions are dimensionless molecular-preset coefficients multiplied by the caller’s declared
length_scale.
- fn run<R: Rng + ?Sized>(cfg: &Config, start: ArrayView1<f64>, ledger: &mut Ledger, relax: Relax<'_>, rng: &mut R) -> Outcome¶
Runs the driver until the ledger is spent.
startis a starting configuration andrelaxperforms a relaxation of the requested number of steps, charging the ledger.
- fn run_with_bias<'g, R: Rng + ?Sized>(cfg: &Config, start: ArrayView1<f64>, ledger: &mut Ledger, relax: Relax<'_>, grad: Option<&mut GradFn<'g>>, bias: &mut BasinBias<ClusterFingerprint>, rng: &mut R) -> Outcome¶
As
run_with_gradient, with a bias supplied by the caller and left behind when the run ends.For a caller running several chains under one budget. The well-tempered bias is a memory of the landscape rather than of the chain that walked it, and a funnel one chain has filled is filled for the next one. Rebuilding it per chain throws that away, which is not a small effect: at 75 points the crossing takes on the order of a hundred thousand hops of accumulation, and a bank of sixteen chains each starting from an empty bias solved 2 seeds in 8 where one long chain solved 9 in 16.
Only for a single-rung run. Replica exchange gives each rung its own bias by construction and there is nothing for one external bias to be.
- fn run_with_gradient<'g, R: Rng + ?Sized>(cfg: &Config, start: ArrayView1<f64>, ledger: &mut Ledger, relax: Relax<'_>, grad: Option<&mut GradFn<'g>>, rng: &mut R) -> Outcome¶
Runs from
startwith an optional charged gradient.
- fn run_with_gradient_settle<'g, R: Rng + ?Sized>(cfg: &Config, start: ArrayView1<f64>, ledger: &mut Ledger, relax: Relax<'_>, grad: Option<&mut GradFn<'g>>, settle: Option<Settle<'_>>, rng: &mut R) -> Outcome¶
As
run, with a gradient for the soft-mode escape.Without one,
Config::minima_hoppingfalls back to scaling the ordinary displacement, which carries the same feedback law and is what Goedecker reports as strictly weaker.
Enums
- enum ClusterFingerprint¶
Descriptor for basin keying, matched to the metric that will compare it.
A sorted distance spectrum is permutation and rotation invariant already, so Euclidean distance on it is a usable if scale-broken notion of sameness. A shape metric quotients out those symmetries itself and needs the coordinates, so the two cannot be mixed.
- Spectrum(SortedPairs)¶
Sorted pairwise distances, compared by Euclidean distance.
- Coordinates¶
Coordinates, for a metric that does its own matching.
- Sites(SiteEnergies)¶
Sorted per-point pair energies, keying on how well each point is bound rather than on how far apart the points are.
- Canonical(Box<crate::shape::CanonicalOrder>)¶
Coordinates put in a canonical order against a fixed reference, so Euclidean distance between two of them is a shape distance.
Implementations
- impl ClusterFingerprint¶
Functions
- fn for_keying(n_points: usize, shape_keyed: bool) -> Self¶
The descriptor a given keying requires.
- enum Keying¶
Which descriptor a run keys basins on.
Named rather than a boolean because there are now three and the choice is the lever: at 75 points the merge radius on a distance spectrum is sharply sensitive, 13 seeds in 24 at 0.7 against 0 in 8 at 0.95, and a descriptor that separates distinct structures more cleanly is what would widen that.
- Distances¶
Sorted pairwise distances.
- Shape¶
Coordinates, matched by a shape metric.
- Sites¶
Sorted per-point pair energies.
- Canonical¶
Coordinates canonically ordered against a fixed reference.
The only keying here that does not throw correspondence away. Sorting buys invariance by discarding which point holds which value; a canonical order keeps it, so two structures with the same multiset of distances and a different arrangement separate.
It is also what makes shape keying affordable. A shape distance costs an IRA call, so keying on it directly pays one call per basin comparison and a bias holding thousands of basins cannot be queried at hop rate. Matching each structure once against a reference costs one call per hop and leaves every comparison Euclidean.
Structs and Unions
- struct Config¶
Driver settings.
- n_points: usize¶
Points in a state; the state length must be
3 * n_points.
- length_scale: f64¶
Declared coordinate length scale.
- energy_scale: f64¶
Declared objective-energy scale.
- move_library: MoveLibrary¶
Proposal library hosted by this configuration.
- neighbour_cutoff: f64¶
Separation below which two points count as neighbours.
- symmetrise_cutoff: f64¶
Pair cutoff used by the symmetry proposal.
- temperature: f64¶
Metropolis temperature on the quenched chain.
- bias_height: f64¶
Height of a fresh bias deposit.
- bias_gamma: f64¶
Well-tempered bias factor; must exceed one.
- merge_radius: f64¶
Distance below which two states are the same basin.
Its units are those of whichever metric keys the bias. Against a sorted distance spectrum compared by Euclidean distance it is a number in descriptor space with no physical meaning; against a shape distance it is a length.
- theta: f64¶
Design point for the budget-window temperature, as a fraction of the sphere-model descent boundary. Must lie strictly below two.
- budget_window: bool¶
Set the temperature by the budget-window law rather than holding it.
- allocate_moves: bool¶
Choose the move kernel by discounted Thompson allocation.
- adaptive_height: bool¶
Set the deposit height from the escape gaps the chain observes.
- max_hops: Option<usize>¶
Hops a single
runmay take before returning, when set.Used by the replica ladder to advance one chain by a slice; a plain run leaves it unset and stops only when the ledger does.
- replicas: usize¶
Replicas run on a temperature ladder, with periodic swaps.
One is the plain chain. Above one, the driver runs a ladder and offers swaps through
crate::exchange::Exchange, which is the crate’s own operator and satisfies detailed balance by construction.This is the standard non-local mechanism for a multi-funnel landscape and the measurements here say why it is the right one to reach for: no single move from the plateau reaches anything lower, so a cold chain cannot leave it, while a hot chain crosses freely and finds nothing precise. A swap moves a hot chain’s crossing down to a cold chain that can polish it, which neither temperature achieves alone.
- swap_period: usize¶
Hops between swap attempts.
- minima_hopping: bool¶
Drive the escape scale and the acceptance threshold from the history, after Goedecker’s minima hopping, instead of a Metropolis temperature.
Revisiting a known minimum makes the next escape harder rather than the current basin less attractive, which is a different use of the same history the bias keeps. The transition region between funnels is left crossable, which Goedecker argues is why flooding it is the wrong response to a revisit.
This is the scaled-move form: the escape scale multiplies the move amplitude and the acceptance threshold replaces Metropolis. Soft-mode climbs are not taken every hop under this flag; they are the separate
Config::escape_on_stallpath. Measured: activating every hop under a gradient cost ~687 charged evaluations per hop on LJ38 and bought 291 hops from 200k, which is not a search. The controller and the climb are complementary and must stay separable.
- escape_lanczos_steps: usize¶
Lanczos steps for the soft-mode escape.
Each costs two gradient evaluations, charged. Eight resolves the softest mode of a cluster well enough to displace along, against about forty charged evaluations for the relaxation that follows.
- escape_epsilon: f64¶
Finite-difference step for the Hessian-vector product.
- escape_amplitude: f64¶
Distance moved along the softest mode per climbing step.
- escape_overshoot: f64¶
Push past the saddle, in units of the climbing step, before the feedback scale multiplies it.
- escape_max_climb: usize¶
Climbing steps before a climb is abandoned.
- escape_on_stall: bool¶
Climb out of the basin when the chain stops improving.
The escape and the plain chain have opposite economics and this is how they are combined. A climb is a guaranteed way out of a funnel and costs 637 charged evaluations against 30 for an ordinary hop, so running one every hop buys 471 hops where the plain chain buys a hundred thousand and loses LJ38 outright. Running one only when the chain has stopped improving costs a few per cent and supplies the one thing a biased random walk has no mechanism for: leaving a funnel on purpose.
- escape_stall_patience: usize¶
Smallest number of hops without improvement before a climb is triggered.
A floor, not the trigger. The trigger is
escape_stall_factortimes the longest quiet stretch this chain has already survived, so a climb fires only when the chain is stuck longer than it has ever been stuck before.A fixed patience cannot be set. Traced on 75 points, the runs that succeed cross at 42 and 55 per cent of the way in, after 1500 to 1900 basins, and go tens of thousands of hops between improvements on the way. A patience of 400 fires about 180 climbs into that and the chain never accumulates: the arm scored 2 seeds in 8 against 9 in 16 without it.
- escape_stall_factor: f64¶
Multiple of the longest quiet stretch so far that counts as stuck.
- track_funnels: bool¶
Track the funnel partition the search’s own transitions imply.
A stall is currently detected from energy: so many hops without a new best. That conflates two situations a search should treat differently, a chain polishing inside a region it can leave, and a chain that cannot leave at all. The transition graph tells them apart: when the accepted hops split into two parts with few edges between them and the chain sits in one, that is a funnel and not slow progress.
Steer with spectral (Fiedler) well-tempered bias on the hop graph.
When true, accepted hops build a transition graph on basin identity (the same fingerprint as the per-basin bias; with
iraandKeying::Canonicalthat identity is IRA, not SortedPairs). The second eigenvector of the normalised Laplacian is the continuous CV for an extra well-tempered bias (crate::spectral::SpectralBias). That is the algorithm: identity supplies resolution, the spectrum supplies the funnel coordinate — no hand-chosen Q4. Refits cost an eigendecomposition of a matrix the size of the basin count, on a schedule (Config::funnel_period). Seecrate::funnel_spectralandcrate::spectral.
- funnel_period: usize¶
Accepted hops between Laplacian refits / Fiedler updates.
- symmetrise_on_stall: bool¶
Symmetrise onto the symmetry the structure nearly has, on a stall.
Oakley, Johnston and Wales report the mean first encounter time for the 98-point cluster, whose global minimum is tetrahedral, improving by more than seventyfold under a scheme of this kind. That is the case this driver is weakest on: 3 seeds in 8 at twelve million evaluations against 8 in 8 at 75 points.
Applied when the chain is stuck rather than as an allocator arm, because it is not a perturbation competing with the others. It either finds an approximate symmetry and lands the structure on it, or finds none and leaves the chain alone. See
crate::symmetrise.
- symmetry_tolerance: f64¶
Largest deviation at which an approximate symmetry is worth using.
- symmetry_merge_radius: f64¶
Coordinate-space radius used to merge points after symmetrisation.
- symmetrise_patience: usize¶
Hops without improvement before a symmetrisation is considered.
Separate from the escape patience because the two answer different questions: an escape is for a chain that cannot leave, this is for a chain that has stopped finding anything and may be near a symmetric answer without being on it.
- angular_moves: bool¶
Wales and Doye’s angular move, applied when a point is loose.
“If the highest pair energy rose above a fraction R of the lowest pair energy then an angular move was employed for the atom in question with all other atoms fixed” (J. Phys. Chem. A 101, 5111). This is the move their unbiased search used to reach the decahedral minima at 75 and 102 points, and it is not in this crate’s library: the nearest thing, surface relocation, picks the least-coordinated point rather than the worst-bound one and places it near the surface rather than at the far edge.
It replaces the allocator’s choice on the steps where it fires, rather than being one arm among many, because the criterion decides when it is the right move.
- angular_target: f64¶
Acceptance rate the pair-energy ratio is tuned to.
“R was adjusted to give an acceptance ratio for angular moves of 0.5 and generally converged to between 0.40 and 0.44.”
- keying: Keying¶
Which descriptor basins are keyed on.
Takes precedence over
shape_keyed, which stays for callers that only need the two-way choice.
- contextual_moves: bool¶
Choose the move from where the chain is standing.
The allocator learns one success rate per move, which is the right model when a move has a rate and the wrong one when it has a precondition. The angular move is the clear case: it is not applied at a frequency, it is applied when a point crosses a pair-energy criterion, and a rate learned across the times it was and was not appropriate describes no situation the chain is ever in. See
crate::contextual.
- contextual_floor: f64¶
Rate at which the contextual allocator picks uniformly regardless.
- bayes_screen: bool¶
Decide whether to finish a relaxation from a posterior rather than from a fixed margin.
The margin screen is the one mechanism here measured to be worth having, at 13 seeds in 24 against 2 in 8 without it, and what it does is spend numerical effort where it is likely to pay. That is a decision under uncertainty about a quantity not yet computed, and a constant is a poor way to make it. See
crate::screen.
- flat_histogram: bool¶
Accept against the density of minima rather than against the energy.
The Metropolis rule targets
g(E~) exp(-E~ / T)in quenched energy, and on a multi-funnel landscapegdecides the outcome: the chain sits in whichever funnel holds the most minima. Weighting by1 / gmakes the sampled energy histogram flat, so the deep and rare energies get the same share of the run as the shallow and abundant ones. Seecrate::dos.
- flat_sweep: usize¶
Trials between weight refreshes. The weight is frozen across a sweep so each sweep is an exact chain for its own target rather than an adaptive one whose invariance has to be argued.
- flat_quantile: f64¶
Quantile of a sweep’s visited energies that sets the cut below which the target is flat. Lower is greedier: the flat region shrinks toward the deepest energies the chain has reached.
- statistical_temperature: bool¶
Take the temperature from the entropy’s own slope rather than from a constant.
The Metropolis rule and the basin bias both measure well and both stand; what this replaces is the one hand-set number they sit on. See
crate::dos::DensityOfStates::temperature.
- energy_bias: bool¶
Deposit a well-tempered bias in quenched energy.
The per-basin bias fills the basin the chain stands in, and the trap is a funnel holding exponentially many basins. Energy separates the funnel where a coordinate length cannot. All scales come from the run’s own quenched-energy distribution. See
crate::dos::EnergyBias.
- depth_reward: bool¶
Reward move arms by the depth they reach, not by acceptance.
See
crate::allocate::DepthAllocator.
- soft_perturb: bool¶
Perturb in the soft subspace of the incumbent’s own curvature.
An isotropic step in
3ndimensions puts nearly all of its norm on stiff directions, and the quench relaxes those components straight back into the basin they came from: only the projection onto the low-curvature subspace survives. Confining the draw to that subspace with per-mode thermal amplitudes is the local GaussianN(0, T H^{-1})truncated to the modes that carry displacement atT, computed matrix-free by shifted Lanczos and charged to the ledger like any other work. Nothing here mentions a morphology: the subspace is the structure’s own.
- soft_modes: usize¶
Soft modes kept in the subspace.
- soft_steps: usize¶
Lanczos steps per subspace computation.
- cov_perturb: bool¶
Perturb from a covariance learned from this run’s accepted moves.
The soft-subspace arm computes the directions that matter from the Hessian and pays charged evaluations for them. This arm learns the same object free: accepted minimum-to-minimum displacements concentrate in the directions basins actually connect along, so their shrunk empirical covariance,
(1 - gamma) sigma0^2 I + gamma S, is a proposal fitted to the run’s own successes. Shrinkage toward isotropy covers the cold start, and the weight ramps with evidence, which is the Ledoit-Wolf compromise rather than a schedule. Sampling needs no factorisation:sqrt(1-gamma) sigma0 z0 + sqrt(gamma/m) sum z_i d_ihas exactly the mixture covariance. Nothing morphological enters; the buffer is this run’s history.
- staged_quench: bool¶
Stage the quench: settle the moved atoms in the frozen environment before the screening relaxation.
The measured-productive moves displace one to three atoms while every trial pays a full-system screen. A k-atom settle costs k of the n(n-1)/2 pair rows per evaluation, charged fractionally through
Ledger::charge_frac, so the cheap stage absorbs the descent the full screen would otherwise spend whole evaluations on.
- settle_iters: usize¶
Descent steps in the settle stage.
- group_cutoff: f64¶
Inter-group contact cutoff for the molecular library.
- covalent_cutoff: f64¶
Bonding cutoff below which two atoms are one molecule, for deriving the groups from the structure’s own connectivity each hop. Used only when no species are declared; with species the bond-matrix rule over covalent radii replaces it.
- species: Option<Vec<u32>>¶
Atomic numbers, one per point. With these set the connectivity uses the species-aware bond matrix, which a single length cannot replace on a system holding more than one element.
- bond_tolerance: f64¶
Bond-matrix tolerance on the covalent radii sum.
- frozen: Option<Vec<bool>>¶
Frozen mask, one flag per point. A frozen point is environment: groups made entirely of frozen points never enter the move library, and the structure is not recentred or contained, since the frozen frame IS the frame. The caller’s objective is expected to return zero force on frozen points so the quench leaves them where they stand.
- active_region: Option<(Vec<usize>, usize)>¶
Dynamic active region: seed atoms and the number of bond-matrix neighbour shells around them that stay mobile, recomputed from the current structure each hop. Everything outside the region is treated as frozen for that hop, so the mobile patch follows the seeds. Requires
species.
- bayes_exploration: f64¶
Trials relaxed regardless of the posterior, to keep the model’s training set from being censored by the rule it trains.
- bayes_threshold: f64¶
Posterior probability of improvement above which a trial is relaxed.
- bayes_warmup: usize¶
Observations before the posterior is consulted at all.
- tabu_on_stall: bool¶
Forbid the funnel the chain is stuck in, rather than making it expensive.
Wales and Doye record the lockout directly: once the lowest icosahedral minimum is reached at 75 points, the decahedron is never found later in that run. Two responses were measured here and both failed. A well-tempered bias raises the potential where the chain has been, and runs that fail register as many basins as runs that succeed, so the filling is not what decides it. Restarting the walker from a random configuration failed too, nineteen times per run: a random start descends into the icosahedral funnel again because that funnel’s basin of attraction is far wider. The lockout is entropic and a soft penalty cannot outrun it.
This rejects outright. Structures within the merge radius of a quarantined one are refused whatever their energy, so the chain cannot return to a funnel it has been declared stuck in. The ledger still records them, so a quarantine that turns out to cover the answer costs the search nothing it had already found.
- tabu_capacity: usize¶
Quarantined structures held at once, oldest dropped first.
- restart_on_stall: bool¶
Restart the walker from a fresh configuration on a stall, keeping the bias.
What is stuck is the walker, not the landscape memory. Traced at 75 points, a run that fails stops improving at 2 to 26 per cent of the way in and spends the rest inside the icosahedral funnel, while the runs that succeed cross at 42 to 91 per cent; so a chain that has not crossed early is unlikely to, and the thing worth keeping from its remaining budget is what it has already filled in.
Different from the climb, which moves the walker a short way and leaves it in the same funnel, and from a bank, which splits the budget. This spends nothing and discards nothing: the bias the old chain built is what steers the new one away from where the old one was.
- calibrate_radius: bool¶
Set the merge radius from how far an accepted hop actually reaches.
A radius chosen by hand does not transfer: one calibrated at 38 points is wrong at 75, and one calibrated in a sorted-distance spectrum is wrong in a shape metric. Two structures are the same basin when a single accepted hop can carry the chain between them, and the search reports that step length for free. See
crate::calibrate.
- calibrate_quantile: f64¶
Quantile of the accepted-hop step length the radius tracks.
- calibrate_warmup: u64¶
Accepted hops required before the calibrated radius is used.
- bias_by_rung: bool¶
Scale the deposit height with rung temperature.
A bias pushes a chain out of where it sits and a low temperature keeps it in, so a cold rung carrying a full bias is evicted from good basins and cannot return. Measured on LJ75, that inverts the ladder: the coldest rung held -391.3 while the hottest held -396.0, where a working ladder has the deepest structure at the cold end.
Scaling the height by the rung’s temperature ratio leaves the coldest rung nearly a plain hopping chain, which polishes, and the hottest carrying the full bias, which crosses. The swap then moves a crossing down to a chain that can refine it, which is the division of labour the ladder exists for.
- ladder_top: f64¶
Hottest temperature on the ladder, as a multiple of
temperature.
- return_screen: bool¶
Abandon a trial whose short relaxation is heading back to the current basin, before paying for the full one.
The energy screen passes a returning trial, because a perturbation that falls straight back carries the incumbent’s energy and looks like a success. Near a deep minimum roughly nineteen proposals in twenty return, so most of the budget buys relaxations into the basin the chain already occupies. Measured on the shape distance after a partial relaxation, returns and escapes separate cleanly: 0.160 against 1.846 with 97 per cent of pairs ordered correctly at thirty iterations.
- soap_class_residual: bool¶
SOAP hop uses the 555→421 / fcc-prototype oracle. Off (recommended) is the observed-cloud residual
2p − μ.
- soap_hop: bool¶
Offer the SOAP pullback arm. Recommended leaves this on. Off is the control that asks whether the arm does any work.
- return_polish: usize¶
Extra relaxation steps on a returning trial when
return_screenis on.Zero (the recommended default) skips the full quench entirely, which is how a hop opts out of return polish. A positive value finishes every returning trial in that hop at a fraction of
relax_stepsso a near-incumbent that is actually a new isomer can still settle.
- return_polish_after: usize¶
Ledger spend that must be reached before
return_polishfires.Zero polishes every returning trial. A positive value keeps the first part of the hop as skip-return and only finishes returns after that many charged evaluations, so one chain can cover both the ico GM and the later Marks funnel.
- path_on_stall: bool¶
Attempt a multi-step path between funnels when hopping stalls.
Basin hopping searches to depth one, and from the structure a 75-point search settles into none of 1800 single moves reaches anything lower. A path relaxes images between the current structure and a structurally different archive member, so the corridor between two funnels is examined rather than jumped.
- stall_patience: usize¶
Hops without improvement before a path is attempted.
- path_images: usize¶
Images relaxed along a path.
- anneal_diversity: bool¶
Anneal the merge radius from wide to narrow across the budget.
The threshold that decides when two structures are one basin is a temperature rather than a setting, and the only published method that solves the hard cluster sizes reliably anneals it. Held fixed, it is the quantity three separate calibrations here failed to pin down.
- diversity_floor: f64¶
Fraction of the starting radius the annealed threshold falls to.
Bounded below by what basin identity needs, which is not what a population diversity threshold needs. A merge radius under the distance a single hop covers, 0.4766 on 75-point minima, stops recognising a structure already visited: annealing 0.7 down to 0.07 took a run from 250 basins at 25 revisits to 4423 at 2.6, and the best found from -396.282 to -394.629.
- height_revisits: f64¶
Revisits a basin should take before the accumulated bias clears the escape gap, when the height is adaptive.
- shape_keyed: bool¶
Key basins on IRA shape distance rather than on the descriptor.
Measured on LJ38 at 400 thousand charged evaluations: keying on the descriptor solves 1 seed in 8. The threshold there has to absorb relabelling and rotation, which is what makes it untransferable between sizes and what three separate calibrations failed to pin down.
- screen_margin: f64¶
How far above the incumbent a screened trial may land and still be promoted to a full relaxation.
- screen_steps: usize¶
Relaxation steps in the screening pass. Calibrated by sweep on the corrected relaxer, LJ38 at 4e5 charged evaluations, four seeds each:
steps
solved
charged per hop
hops
6
0/4
11
149392
10
0/4
16
94412
15
1/4
21
66437
25
4/4
33
49728
40
4/4
47
33396
Three times the hops buys nothing when the quench is short. The chain moves on the transformed landscape, and a screened energy that has not reached its basin is not a point on it, so a proposal is compared against the incumbent on a quantity that is not the one being minimised. 25 is the knee: 40 solves as often and costs 1.4 times as much per hop, 15 costs less and solves once in four.
This is the same wall the adaptive screening quench hit from the other side. There the extrapolated energy was wrong by 1e4 at the step where its rule fired; here a genuinely shorter quench is simply not enough quench. Both say the screening pass is the quench rather than overhead around it.
- adaptive_screen: bool¶
Whether the screening pass stops on a decision instead of
screen_steps.The fixed length is where the budget goes: measured on 38 points, 89 to 92 per cent of charged evaluations were spent screening, against 8 per cent on the relaxations that screening exists to avoid. Every mechanism in this crate that tried to change where the chain goes was measured and failed; this one changes what a hop costs, which is the axis the only successful mechanism so far, the return screen, also moved.
- record_gradient: f64¶
Gradient below which a structure may be recorded as the run’s best.
Loose enough that a genuine minimum passes, since a quenched cluster comes back near 1e-6, and tight enough to bar a partial quench, which comes back near 1e-1 or worse.
- surrogate_tolerance: f64¶
Predictive spread, in units of the temperature, above which the first stage abstains rather than deciding.
See
crate::delayed::Surrogate::predict_at.
- delayed_acceptance: bool¶
Whether acceptance is delayed behind a learned surrogate.
A first stage decides on a surrogate for the quenched energy, costing one evaluation and no gradient, and only survivors are quenched; a second stage subtracts the surrogate difference back out. The composite step is reversible with respect to the true target for any surrogate, so a poor one costs acceptance rate rather than correctness. This is what the screen was reaching for and does not have. See
crate::delayed.
- construct_width: usize¶
Candidates built and scored per growth proposal.
Costs no charged evaluations, since scoring is structural.
- probe_screen: bool¶
Whether to score the quench extrapolation without acting on it.
Runs the screening pass to its full length and records what an adaptive stop would have claimed, which is the only way to separate “the model is wrong” from “the model is right and the search needs the precision”.
- quench_warmup: usize¶
Descent steps before the quench predictor may speak.
The first steps of a quench from a perturbed cluster are nowhere near the quadratic region: atoms sit close enough that energies run to 1e5, and a log-linear fit through three such decrements extrapolates a tail that has nothing to do with the basin. Measured, a stop at step 4 missed the full pass by 1.0e4 on a landscape whose minima are 0.5 apart.
- quench_confidence: f64¶
Standard deviations of separation a verdict needs.
- relax_steps: usize¶
Relaxation steps in the full pass.
- container: f64¶
Container half-width, applied when a move is generated.
- min_separation: f64¶
Closest approach enforced before a trial is relaxed.
Implementations
- impl Config¶
Functions
- fn derived(n_points: usize) -> Self¶
Recommended flags, with the two hand-set scalars replaced by derived ones: budget-window temperature (
θ = 1/2inside the descent window) and the cost-asymmetric Bayes screenτ = (R-S)/(2R-S)fromcrate::screen::cost_asymmetric_threshold.This is not the measured
recommendedconfiguration. Hit rates for this stack are not claimed until a campaign records them.
- fn for_cluster(n_points: usize) -> Self¶
- fn for_molecular(species: Vec<u32>, groups: Vec<Vec<usize>>, energy_scale: f64) -> Self¶
Species-aware molecular preset with rigid groups.
- fn packing_cna_applies(&self) -> bool¶
CNA 555 / Ih packing diagnostics apply only to a monoatomic cluster. A molecule or a slab has species or a frozen frame; those are not Honeycutt-Andersen environments.
- fn proposal_kernel(&self) -> ClusterProposal¶
Proposal mixture for use by the general
crate::sampler::Sampler.
- fn recommended(n_points: usize) -> Self¶
Settings for
n_pointsat the campaign’s measured defaults. The measured configuration: the stack every layer of which beat or matched its paired control across four cluster morphologies.Composed surface relocations paying one acceptance test (LJ75 49/144 against 17/144, Bayes factor 3104 with the arm allocator), Normal-Gamma Thompson allocation rewarded by depth, and tabu on stall (LJ98 40/72 against 20/72, Bayes factor 43.8). Neutral where its mechanisms are not needed: 55/72 against 55/72 on the 38-point double funnel and 47-48 of 48 on the 55-point single funnel. Reference GMIN at matched potential-call budgets: 37/48, 0/48, 0/48.
Config::for_clusterremains the plain Wales-Doye protocol, kept as the comparison baseline; this is what a caller who wants answers should start from.LeanBurst includes the SOAP pullback (analytic (J^{+}) of stacked local power spectra). The hop target is the observed-cloud residual
2p − μ, the same map used on molecules and slabs: partitioned by observed species, never by a CNA class or an fcc prototype. Thompson allocates SOAP with surface, single, burst and sym. The return screen and stall symmetrisation are on; Ih-dominated stalls withhold symmetrise rather than invent a missing packing.
- fn recommended_molecular(species: Vec<u32>, groups: Vec<Vec<usize>>, energy_scale: f64) -> Self¶
Measured allocation and stall controls over a molecular move library.
SOAP is the same observed-cloud residual as the cluster hop:
2p − μwithin each observed atomic number. No CNA, no fcc prototype. A slab setsactive_region; frozen atoms stay as SOAP neighbours and do not move.
- fn start_radius(&self) -> f64¶
Radius of the preset’s initial cluster sphere.
- fn with_scales(n_points: usize, length_scale: f64, energy_scale: f64) -> Self¶
Lennard-Jones preset expressed against declared physical scales.
- struct Ledger¶
Work ledger: every objective or gradient evaluation is charged.
A relaxation inside a move spends the same budget as a proposal does, which is the accounting that makes methods with different internal structure comparable. Published cluster success rates are quoted per hopping step, with the relaxation inside each step uncounted.
- best: f64¶
Lowest objective value seen.
- best_state: Option<Array1<f64>>¶
State attaining
Ledger::best.
Implementations
- impl Ledger¶
Functions
- fn budget(&self) -> usize¶
Charged evaluations the ledger was created with.
- fn charge(&mut self) -> bool¶
Charges one unit, returning
falsewhen the budget is gone.
- fn charge_frac(&mut self, frac: f64) -> bool¶
Charges a fraction of one evaluation, for work that touches a subset of the system.
A k-atom partial evaluation computes k of the n(n-1)/2 pair rows, so its honest price is a fraction of a full evaluation. The fraction accumulates as exact debt and is settled into whole charged units as it crosses one: deterministic, auditable, and never cheaper than the work done because the residue is still owed when the run ends.
- fn charge_many(&mut self, n: usize) -> bool¶
Charges
nunits at once, returningfalsewhen the budget ran out partway.For a caller that ran work against a sub-ledger and is settling up. The alternative, handing the real ledger to the inner run, makes any budget arithmetic inside it see the whole campaign’s budget rather than the slice it was given.
- fn new(budget: usize) -> Self¶
Creates a ledger with
budgetcharged evaluations.
- fn record(&mut self, value: f64, state: ArrayView1<f64>)¶
Records a value and its state when it improves the incumbent.
- fn remaining(&self) -> usize¶
Charged evaluations remaining.
- fn spent(&self) -> usize¶
Charged evaluations spent.
- struct Outcome¶
What a run produced.
- best: f64¶
Lowest quenched value found.
- best_state: Option<Array1<f64>>¶
State attaining it.
- final_state: Option<Array1<f64>>¶
Live chain at the end of the run, which a later hop can continue.
- hops: usize¶
Hops taken.
- screened_out: usize¶
Trials rejected by screening before a full relaxation.
- basins: usize¶
Distinct basins registered.
- charged: usize¶
Charged evaluations spent.
- returned: usize¶
Trials abandoned because their partial relaxation was going home.
- escape_scale: f64¶
Escape scale at the end of the run, when the controller is used.
- escape_threshold: f64¶
Acceptance threshold at the end of the run.
- visit_counts: (usize, usize, usize)¶
Quenches classified as a return, a known basin and a new one.
- soft_perturbs: usize¶
Soft-subspace perturbations proposed.
- soft_subspaces: usize¶
Soft-subspace recomputations paid for.
- soft_escapes: usize¶
Proposals made along the softest mode.
- soft_crossed: usize¶
Of those, the ones whose climb reached a saddle.
- improvements: Vec<(usize, usize, usize, f64)>¶
Hop, charged evaluations spent, basin count and value at each new global best.
This is what a first-encounter time is computed from, and it is the statistic worth reporting. A success rate at a fixed budget is the same quantity through an arbitrary threshold: above the budget it saturates and says nothing about the margin, below it censors and says nothing about how near the failures came. The work to first reach a target is a property of the method rather than of a budget someone chose, which is why the literature quotes mean first encounter times.
The charged count is the part that makes it comparable. Hops are not: two arms with different screening spend different amounts per hop, and this campaign has arms ranging from 26 to 637 charged evaluations per hop.
Capped on the number of records rather than on the hops: a run that improves ten thousand times is descending, and the tail of that is not what anyone is asking about.
- merge_radius: f64¶
Merge radius at the end of the run, calibrated or as configured.
- mean_step: f64¶
Mean accepted-hop step length, which the radius is a quantile of.
- angular: (usize, usize, f64)¶
Angular moves attempted, and the ratio they settled at.
- contextual: (Vec<usize>, usize)¶
Picks per move under the contextual allocator, and choices it forced.
- screen: (usize, usize, usize, usize)¶
Screen decisions: made, relaxed, forced by the exploration floor, and observations the model was fitted on.
- tabu: (usize, usize)¶
Funnels quarantined, and proposals refused for landing in one.
- funnel: Option<(usize, usize, f64)>¶
The funnel partition at the end of the run: parts, and how separated.
A connectivity near zero means the search’s transitions split into two nearly disconnected sets, which is what a funnel boundary looks like from the inside.
- symmetrised: (usize, f64)¶
Symmetrisations attempted, and the energy they gained.
- restarts: usize¶
Restarts triggered by a stall.
- stall_escapes: usize¶
Climbs triggered by a stall.
- stall_escape_gain: f64¶
Energy gained by those that landed lower than where they left.
- soft_lambda: f64¶
Mean softest eigenvalue over those proposals.
- rungs: Vec<(f64, usize, f64)>¶
Per-rung temperature, basin count and best energy.
What says whether a ladder is doing its job rather than merely swapping: a hot rung should register many basins and a poor energy, a cold rung few basins and a deep one. A ladder where every rung looks alike is a ladder whose spread is too narrow to be worth its cost.
- swaps_tried: usize¶
Swap attempts between adjacent replicas.
- accepted: usize¶
Hops the acceptance rule took, before any veto.
- unconverged_records: usize¶
Structures barred from the ledger for not being minima.
- arms: Vec<(String, usize, usize, f64)>¶
Per-arm draws, accepts and best quenched value, in library order.
- delayed: Option<(usize, usize, usize, usize)>¶
Delayed acceptance: first stages run, first-stage rejections (each a quench not paid), second stages run, and second-stage rejections (the surrogate’s mistakes).
- swaps_accepted: usize¶
Swaps accepted.
- paths: usize¶
Paths attempted after a stall.
- path_escapes: usize¶
Paths that produced a structure outside the starting basin.
Nearly always all of them, and so not worth much on its own: an image interpolated towards a different structure differs from the start by construction. The useful count is
path_improvements.
- path_improvements: usize¶
Paths that produced a structure lower than the chain was standing on.
- path_gain: f64¶
Total depth gained from paths, in energy units.