mod funnel_spectral¶
- module funnel_spectral¶
Learning the funnel partition from the search’s own transitions.
Every descriptor tried in this crate was chosen: a bond-order parameter, a sorted distance spectrum, per-point pair energies, template-matching fractions, common-neighbour fractions. Each is a guess about what separates the regions the search must tell apart, and the guesses have been poor. The fourth-order bond-order parameter separates the two funnels at 75 points by 0.023, under any usable deposition width; sorted distances need a threshold that is a knife edge, 9 seeds in 16 at 0.7 and 0 in 18 at anything coarser.
The search generates a better object for free. Its accepted hops are edges between basins, and the graph they form has the funnel structure in it whether or not any descriptor does. Cameron treats the 38-point landscape this way, as a network of some seventy thousand minima, and reads the metastability off the spectrum rather than off a coordinate.
What that gives is a partition rather than a number: the sign of the second eigenvector of the normalised Laplacian, the Fiedler vector, splits the basins into the two sets between which transitions are rarest. The corresponding eigenvalue says how well separated they are, so the method reports its own confidence.
Why this is not another descriptor
A descriptor is a function of one structure. This is a function of the search: two structures land in the same part because the chain moves between them, not because they look alike. A funnel is defined by what is reachable, which is what makes it the right object, and it is exactly what a chosen coordinate cannot see.
What it costs
An eigendecomposition of a matrix the size of the basin count, so it is refitted on a schedule rather than per hop. The basin count runs to a few thousand in a long run, which is past what a dense solver should be asked for every step and comfortable every few thousand.
Structs and Unions
- struct FunnelSpectrum¶
Accumulates transitions and splits them when asked.
- fits: usize¶
Splits computed.
- refusals: usize¶
Splits refused because the graph was too small or disconnected.
Implementations
- impl FunnelSpectrum¶
Functions
- fn is_empty(&self) -> bool¶
Whether nothing has been recorded.
- fn len(&self) -> usize¶
Basins seen.
- fn new() -> Self¶
A fresh accumulator.
- fn pending(&self) -> usize¶
Hops since the last split was computed.
- fn record(&mut self, from: usize, to: usize)¶
Records an accepted hop between basins.
Only accepted hops: a rejected proposal says the chain declined to move, which is a statement about the acceptance rule rather than about reachability, and including them makes every basin look adjacent to whatever the chain happened to try.
- fn split(&mut self) -> Result<Partition, SpectralError>¶
Splits the visited basins in two, or reports why it could not.
- struct Partition¶
A two-way split of the visited basins, with the confidence in it.
- basins: Vec<usize>¶
Basin identifiers, in the order the parts refer to.
- side: Vec<bool>¶
Which side of the split each basin fell on.
- connectivity: f64¶
Algebraic connectivity: the second eigenvalue of the normalised Laplacian.
Near zero means the two parts are nearly disconnected, which is what a funnel boundary looks like. Large means the graph is well mixed and the split is arbitrary, and a caller should ignore it rather than act on it.
Implementations
- impl Partition¶
Functions
- fn separated(&self, threshold: f64) -> bool¶
Whether the split is worth acting on.
A partition of a well-mixed graph is a cut through the middle of one region, and steering by it would push the chain away from where it already is for no reason. The threshold is on the connectivity, not on the sizes: a small part is fine when it is genuinely separated.
- fn side_of(&self, basin: usize) -> Option<bool>¶
Which side a basin is on, if it was visited.
- fn sizes(&self) -> (usize, usize)¶
Basins on each side.