mod spectral¶
- module spectral¶
Collective variables from the spectrum of the visited landscape.
Every bias in this crate acts on a quantity computed from one configuration:
crate::bias::WellTemperedBiason a linear projection of it,crate::bias::BasinBiason a fingerprint of it. Both are functions of a single point, and on a multi-funnel landscape neither separates funnels reliably. Measured over forty quenched 75-point Lennard-Jones minima, a Steinhardt parameter spans four deposition widths and the best learned projection twenty-two, and a bias on either fills the target funnel and its competitor together.The object that carries the separation is not a configuration. It is the set of minima with the transitions observed between them, and a funnel is a poorly connected component of that graph. Component structure is spectral: the eigenvectors of the graph Laplacian belonging to the smallest non-zero eigenvalues vary slowly inside a component and quickly between them, so the second one, the Fiedler vector, is a coordinate on which two funnels lie apart by construction rather than by luck of projection.
Two measurements make this the right object rather than a nice idea.
The shape distance between such minima is bimodal with an empty middle: a hop lands at either 0.000, having returned, or at a median 1.910, and two independent minima never come closer than 1.663. A distance matrix with that structure is block structured, which is where a spectral partition is exact and where a merge radius or a kernel width has nothing to trade off. Three radius calibrations and a shape-space kernel failed here for that reason.
From the structure a search settles into, none of 1800 single moves reaches anything lower, so escape is a sequence of accepted uphill hops rather than a gap one move crosses. A quantity defined on the hop graph describes such a sequence; a quantity defined on one structure cannot.
The eigenproblem is solved by cyclic Jacobi rather than by pulling in a LAPACK binding. The matrix is the visited-basin graph, orders of magnitude smaller than the relaxations that produced it, and Jacobi is accurate for small symmetric matrices and has no dependency.
Functions
- fn laplacian_embedding(weights: ArrayView2<f64>, n_vectors: usize) -> Result<(Array1<f64>, Array2<f64>), SpectralError>¶
Eigenvectors of the normalised Laplacian, smallest non-trivial first.
Uses
L = I - D^-1/2 W D^-1/2and returns the random-walk eigenvectors, by applying theD^-1/2scaling, so the result is a function on nodes rather than on the symmetrised operator.The constant eigenvector belonging to eigenvalue zero is dropped: it says nothing about how the graph divides.
- fn symmetric_eigen(a: ArrayView2<f64>, max_sweeps: usize) -> (Array1<f64>, Array2<f64>)¶
Eigenvalues and eigenvectors of a symmetric matrix, ascending.
Cyclic Jacobi: repeatedly annihilate the largest off-diagonal entry by a plane rotation. Converges for any real symmetric matrix and is accurate for small ones, which is what a visited-basin graph is.
Returns
(values, vectors)with eigenvectorkin columnk.
Enums
- enum SpectralError¶
Reasons an embedding cannot be produced.
- TooFewNodes(usize)¶
Fewer nodes than a partition needs.
- IsolatedNodes(usize)¶
Nodes with no edges, for which the normalised Laplacian is undefined.
Traits implemented
- impl std::fmt::Display for SpectralError¶
- impl std::error::Error for SpectralError¶
Structs and Unions
- struct DiffusionDirection¶
The leading diffusion direction of an archive of minima, with a Nystrom extension for proposing from any structure.
The archive of visited minima is a point cloud on the landscape’s low-dimensional backbone. Its diffusion map (Coifman and Lafon, doi:10.1016/j.acha.2006.04.006) is the spectral embedding of the row-normalised kernel; the leading nontrivial eigenvector orders the archive along its principal connectivity direction, the same object the sketch-map literature draws for these landscapes, computed here by power iteration on the small dense kernel. The Nystrom extension evaluates that coordinate at a structure outside the archive, so a proposal can step along the backbone rather than isotropically. Rational spectral filtering over the sparse kernel replaces the dense pass when the archive outgrows it; at the archive sizes a charged run accumulates, dense is exact and cheaper.
Implementations
- impl DiffusionDirection¶
Functions
- fn coordinate(&self, x: &[f64]) -> f64¶
The diffusion coordinate of an arbitrary structure, by the Nystrom extension: the kernel-weighted average of the anchor coordinates.
- fn fit(structures: &[Vec<f64>]) -> Option<Self>¶
Fits the leading diffusion coordinate of
structures.Returns
Nonebelow four anchors, where a direction is not defined.
- struct SpectralBias<F: Fingerprint>¶
Well-tempered bias deposited on the Fiedler coordinate of the hop graph.
Keyed through a
Fingerprintlikecrate::bias::BasinBias, so it acts on the same exact basin identity that works on this landscape, and deposits on a continuous coordinate so that filling spreads over a funnel rather than over one basin at a time. That combination is the point: identity supplies the resolution, the spectrum supplies the generalisation, and neither a hand-picked order parameter nor a distance threshold is involved.A basin with no spectral position yet, because it is newly seen or not yet connected, reads as the origin. That is a real position rather than a missing value: an unconnected basin is not known to belong to either side of any partition.
- w0: f64¶
Deposited height per visit before well tempering.
- gamma: f64¶
Well-tempered factor; must exceed one.
- sigma: f64¶
Gaussian width on the spectral coordinate.
- refit_every: usize¶
Hops between recomputations of the embedding.
- min_nodes: usize¶
Connected nodes required before an embedding is attempted.
- refits: usize¶
Embeddings computed.
- last_error: Option<SpectralError>¶
Why the last attempt failed, if it did.
Implementations
- impl<F: Fingerprint> SpectralBias<F>¶
Functions
- fn coordinate(&self, basin: usize) -> Array1<f64>¶
Spectral position of a basin, or the origin when it has none.
- fn n_edges(&self) -> usize¶
Hops recorded.
- fn n_placed(&self) -> usize¶
Basins carrying a spectral position.
- fn new(fingerprint: F, merge_radius: f64, w0: f64, gamma: f64, sigma: f64) -> Self¶
Bias keyed by
fingerprint, merging basins withinmerge_radius.
- fn refit(&mut self) -> bool¶
Recomputes the embedding. Returns whether it succeeded.
A failure is recorded rather than leaving stale coordinates silently in place, because a coordinate that stopped updating is a bias acting on a graph the run has moved past.
- impl<F: Fingerprint> SpectralBias<F>¶
Functions
- fn visit(&mut self, x: ArrayView1<f64>, temp: f64)¶
Records that the chain moved to the basin holding
x, and deposits.The graph is built from what the chain actually does, so this is the entry point a driver calls rather than
Bias::depositalone: the coordinate does not exist until the transitions that define it do.
- struct TransitionGraph¶
Basins visited, and the transitions observed between them.
Implementations
- impl TransitionGraph¶
Functions
- fn adjacency(&self) -> (Vec<usize>, Array2<f64>)¶
Adjacency over the connected nodes, with the basin ids they belong to.
Nodes seen but never connected are dropped rather than kept with a zero degree, which would make the normalised Laplacian undefined.
- fn is_empty(&self) -> bool¶
True when no node has been seen.
- fn len(&self) -> usize¶
Nodes seen.
- fn n_edges(&self) -> usize¶
Edges recorded.
- fn record(&mut self, from: usize, to: usize, weight: f64)¶
Records a hop.
A hop that returns to the same basin marks the node as present but adds no edge: it says nothing about connectivity between components, and counting it would load the diagonal and flatten the spectrum. Given the measured return rate near a deep minimum, roughly 19 proposals in 20, including them would leave the graph describing little else.