1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
//! Provides the [`NeighbourMethod`](crate::NeighbourMethod) enum
use itertools::izip;
use num::Float;
use rand::prelude::*;
use rand_distr::{Normal, StandardNormal};
use std::fmt::Debug;
use crate::{Bounds, Point};
/// Method of getting a random neighbour
pub enum Method<F, R, const N: usize>
where
F: Float,
StandardNormal: Distribution<F>,
R: Rng,
{
/// Get a neighbour in the vicinity of the current point
/// by sampling a random normal distribution with the mean
/// in that point and with the provided standard deviation
Normal {
/// Standard deviation
sd: F,
},
/// Custom: choose your own!
Custom {
/// Custom function
f: fn(p: &Point<F, N>, bounds: &Bounds<F, N>, rng: &mut R) -> Point<F, N>,
},
}
impl<F, R, const N: usize> Method<F, R, N>
where
F: Float + Debug,
StandardNormal: Distribution<F>,
R: Rng,
{
/// Get a neighbour of the current point
///
/// Arguments:
/// * `p` --- Current point;
/// * `bounds` --- Bounds of the parameter space;
/// * `distribution` --- Distribution to sample from;
/// * `rng` --- Random number generator.
pub fn neighbour(&self, p: &Point<F, N>, bounds: &Bounds<F, N>, rng: &mut R) -> Point<F, N> {
match self {
Method::Normal { sd } => {
let mut new_p = [F::zero(); N];
// Generate a new point
izip!(&mut new_p, p, bounds).for_each(|(np, &p, r)| {
// Create a normal distribution around the current coordinate
let d = Normal::new(p, *sd).unwrap();
// Sample from this distribution
let mut p = d.sample(rng);
// If the result is not in the range, repeat until it is
while !r.contains(&p) {
p = d.sample(rng);
}
// Save the new coordinate
*np = F::from(p).unwrap();
});
new_p
}
Method::Custom { f } => f(p, bounds, rng),
}
}
}