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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use crate::{DiffusionMap, ReductionError};
use linfa::{param_guard::TransformGuard, ParamGuard};

/// Diffusion map hyperparameters
///
/// The diffusion map algorithms has only two explicit hyperparameter. The first is the stepsize.
/// As the algorithm calculates the closeness of points after a number of steps taken in the
/// diffusion graph, a larger step size introduces a more global behaviour of the projection while
/// a smaller one (especially one) just projects close obserations closely together.
/// The second parameter is the embedding size and defines the target dimensionality.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffusionMapValidParams {
    steps: usize,
    embedding_size: usize,
}

impl DiffusionMapValidParams {
    pub fn steps(&self) -> usize {
        self.steps
    }

    pub fn embedding_size(&self) -> usize {
        self.embedding_size
    }
}

/// Diffusion map hyperparameters
///
/// The diffusion map algorithms has only two explicit hyperparameter. The first is the stepsize.
/// As the algorithm calculates the closeness of points after a number of steps taken in the
/// diffusion graph, a larger step size introduces a more global behaviour of the projection while
/// a smaller one (especially one) just projects close obserations closely together.
/// The second parameter is the embedding size and defines the target dimensionality.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DiffusionMapParams(DiffusionMapValidParams);

impl DiffusionMapParams {
    /// Set the number of steps in the diffusion operator
    ///
    /// The diffusion map algorithm expresses the transition probability with a kernel matrix and
    /// then takes multiple steps along the diffusion operator. In practice scales the
    /// eigenvalues of the decomposition exponentially with the number of steps.
    pub fn steps(mut self, steps: usize) -> Self {
        self.0.steps = steps;

        self
    }

    pub fn embedding_size(mut self, embedding_size: usize) -> Self {
        self.0.embedding_size = embedding_size;

        self
    }

    /// Creates the set of default parameters
    ///
    /// # Parameters
    ///
    /// * `embedding_size`: the number of dimensions in the projection
    ///
    /// # Returns
    ///
    /// Parameter set with number of steps = 1
    pub fn new(embedding_size: usize) -> DiffusionMapParams {
        Self(DiffusionMapValidParams {
            steps: 1,
            embedding_size,
        })
    }
}

impl Default for DiffusionMapParams {
    fn default() -> Self {
        Self::new(2)
    }
}

impl<F> DiffusionMap<F> {
    pub fn params(embedding_size: usize) -> DiffusionMapParams {
        DiffusionMapParams::new(embedding_size)
    }
}

impl ParamGuard for DiffusionMapParams {
    type Checked = DiffusionMapValidParams;
    type Error = ReductionError;

    fn check_ref(&self) -> Result<&Self::Checked, Self::Error> {
        if self.0.steps == 0 {
            Err(ReductionError::StepsZero)
        } else if self.0.embedding_size == 0 {
            Err(ReductionError::EmbeddingTooSmall(self.0.embedding_size))
        } else {
            Ok(&self.0)
        }
    }

    fn check(self) -> Result<Self::Checked, Self::Error> {
        self.check_ref()?;
        Ok(self.0)
    }
}
impl TransformGuard for DiffusionMapParams {}