Skip to main content

radiate_alters/mutators/
gaussian.rs

1use radiate_core::{
2    AlterContext, AlterResult, BoundedGene, Chromosome, FloatGene, Gene, Mutate, Rate, Valid,
3    random_provider,
4};
5use radiate_utils::{Float, Primitive};
6
7/// The `GaussianMutator` is a simple mutator that adds a small amount of Gaussian noise to the gene.
8///
9/// This mutator is for use with any [Chromosome] which holds [FloatGene]s.
10#[derive(Debug, Clone)]
11pub struct GaussianMutator {
12    rate: Rate,
13}
14
15impl GaussianMutator {
16    /// Create a new instance of the `GaussianMutator` with the given rate.
17    /// The rate must be between 0.0 and 1.0.
18    pub fn new(rate: impl Into<Rate>) -> Self {
19        let rate = rate.into();
20
21        if !rate.is_valid() {
22            panic!("Rate is not valid: {:?}", rate);
23        }
24
25        GaussianMutator { rate }
26    }
27}
28
29impl<F, C> Mutate<C> for GaussianMutator
30where
31    F: Float + Primitive,
32    C: Chromosome<Gene = FloatGene<F>>,
33{
34    fn rate(&self) -> Rate {
35        self.rate.clone()
36    }
37
38    #[inline]
39    fn mutate_chromosome(&mut self, chromosome: &mut C, ctx: &mut AlterContext) -> AlterResult {
40        let mut count = 0;
41
42        random_provider::with_rng(|rand| {
43            for gene in chromosome.as_mut_slice() {
44                if rand.bool(ctx.rate()) {
45                    // The reason we use the sampling min/max from the gene here instead of it's
46                    // 'bounds' is because this operation is essentially a form of 'local search'
47                    // and we want to ensure that the mutated value is not too far from the original value.
48                    let min = gene.min().extract::<f64>().unwrap();
49                    let max = gene.max().extract::<f64>().unwrap();
50
51                    let std_dev = (max - min) * 0.25;
52                    let value = gene.allele().extract::<f64>().unwrap();
53
54                    let gaussian = rand.gaussian(value, std_dev);
55                    let allele = gaussian.clamp(min, max);
56
57                    *gene.allele_mut() = allele.extract::<F>().unwrap();
58
59                    count += 1;
60                }
61            }
62        });
63
64        AlterResult::from(count)
65    }
66}