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),
        }
    }
}