mod funnel_bo¶
- module funnel_bo¶
First-derivative interface for HMC-style samplers. Choosing which morphology to search next, by expected improvement.
The searches here fail by funnel, not by local optimization. At 75 points every failing run reaches the icosahedral plateau and stays; the relaxation is fine, the descent is fine, and the answer is in a region the chain never visits. Eight mechanisms built to make the chain leave a funnel were measured and none helped, because leaving is not the problem: the chain has nowhere better to be told to go.
That is a decision problem over a small space, and it is the shape Bayesian optimization is for. Not over the coordinates, where three hundred dimensions puts a Gaussian process out of reach, but over a structural descriptor: the share of points in each local environment, five numbers from
crate::structure::ptm_fractions. A model of “how low does this morphology go” over those five numbers is cheap to fit and is exactly the surface the search is blind to.What is modelled
For each distinct morphology the search has visited, the lowest energy found there. A Gaussian process over that gives a mean and a variance everywhere, including at morphologies never visited, and expected improvement turns the pair into a single number: how much better than the incumbent this morphology is likely to be, integrated over the model’s own uncertainty.
The point is what expected improvement does with a region that has never been sampled. Its mean reverts to the prior and its variance is large, so it scores highly; a region sampled repeatedly and found mediocre scores low however close it is to the incumbent. That is the opposite of what a bias does, and it is the missing half: a bias says where not to go, this says where to go instead.
Why a Gaussian process and not something larger
The observation count is the number of distinct morphologies, which is hundreds rather than millions, and the dimension is five. Exact inference is a Cholesky factorisation of a few-hundred-square matrix, done once per refit. Nothing here needs approximating.
Structs and Unions
- struct FunnelModel¶
A Gaussian process over a structural descriptor.
Squared-exponential kernel with a single length scale, which is right when the inputs are fractions on a common scale, and a noise term that also keeps the factorisation well conditioned when two morphologies are nearly equal.
- length_scale: f64¶
Kernel length scale, in units of the descriptor.
- amplitude: f64¶
Signal standard deviation.
- noise: f64¶
Observation noise standard deviation.
Implementations
- impl FunnelModel¶
Functions
- fn expected_improvement(&mut self, x: ArrayView1<f64>) -> f64¶
Expected improvement at a morphology, for a minimisation.
Zero where the model is confident nothing better lives, and large where the mean is low or the uncertainty is high. The second half is what makes this different from following the model’s mean: a morphology never visited scores on its variance alone, which is how a search reaches a funnel it has no evidence about.
- fn incumbent(&self) -> Option<f64>¶
The best value seen, which is what improvement is measured against.
- fn is_empty(&self) -> bool¶
Whether nothing has been observed.
- fn len(&self) -> usize¶
Morphologies observed.
- fn new(length_scale: f64, amplitude: f64, noise: f64) -> Self¶
A model over descriptors, with a length scale in descriptor units.
- fn observe(&mut self, x: ArrayView1<f64>, y: f64)¶
Records the lowest energy found at a morphology.
A morphology already present is updated to the lower of the two rather than added again: the quantity modelled is how low a region goes, not how often it was sampled.
- fn predict(&mut self, x: ArrayView1<f64>) -> (f64, f64)¶
Posterior mean and standard deviation at a morphology.