radiate_core/
alter.rs

1use crate::{Chromosome, Gene, Genotype, Metric, Population, indexes, random_provider};
2
3/// This is the main trait that is used to define the different types of alterations that can be
4/// performed on a population. The `Alter` trait is used to define the `alter` method that is used
5/// to perform the alteration on the population. The `alter` method takes a mutable reference to
6/// the population and a generation number as parameters. The `alter` method returns a vector of
7/// `Metric` objects that represent the metrics that were collected during the alteration process.
8///
9/// An 'Alter' in a traditional genetic algorithm is a process that modifies the population of
10/// individuals in some way. This can include operations such as mutation or crossover. The goal of
11/// an alter is to introduce new genetic material into the population, which can help to improve
12/// the overall fitness of the population. In a genetic algorithm, the alter is typically
13/// performed on a subset of the population, rather than the entire population. This allows for
14/// more targeted modifications to be made, which can help to improve the overall performance of
15/// the algorithm. The alter is an important part of the genetic algorithm process, as it helps
16/// to ensure that the population remains diverse and that new genetic material is introduced
17/// into the population. This can help to improve the overall performance of the algorithm and
18/// ensure that the population remains healthy and diverse.
19///
20/// In `radiate` the `alter` trait performs similar operations to a traditional genetic algorithm,
21/// but it is designed to be more flexible and extensible. Because an `Alter` can be of type `Mutate`
22/// or `Crossover`, it is abstracted out of those core traits into this trait.
23pub trait Alter<C: Chromosome>: Send + Sync {
24    fn alter(&self, population: &mut Population<C>, generation: usize) -> Vec<Metric>;
25}
26
27/// The `AlterResult` struct is used to represent the result of an
28/// alteration operation. It contains the number of operations
29/// performed and a vector of metrics that were collected
30/// during the alteration process.
31#[derive(Default)]
32pub struct AlterResult(pub usize, pub Option<Vec<Metric>>);
33
34impl AlterResult {
35    pub fn count(&self) -> usize {
36        self.0
37    }
38
39    pub fn metrics(&self) -> Option<&Vec<Metric>> {
40        self.1.as_ref()
41    }
42
43    pub fn merge(&mut self, other: AlterResult) {
44        let AlterResult(other_count, other_metrics) = other;
45
46        self.0 += other_count;
47        if let Some(metrics) = other_metrics {
48            if let Some(self_metrics) = &mut self.1 {
49                self_metrics.extend(metrics);
50            } else {
51                self.1 = Some(metrics);
52            }
53        }
54    }
55}
56
57impl Into<AlterResult> for usize {
58    fn into(self) -> AlterResult {
59        AlterResult(self, None)
60    }
61}
62
63impl Into<AlterResult> for (usize, Vec<Metric>) {
64    fn into(self) -> AlterResult {
65        AlterResult(self.0, Some(self.1))
66    }
67}
68
69impl Into<AlterResult> for (usize, Metric) {
70    fn into(self) -> AlterResult {
71        AlterResult(self.0, Some(vec![self.1]))
72    }
73}
74
75/// The `AlterAction` enum is used to represent the different
76/// types of alterations that can be performed on a
77/// population - It can be either a mutation or a crossover operation.
78pub enum AlterAction<C: Chromosome> {
79    Mutate(&'static str, f32, Box<dyn Mutate<C>>),
80    Crossover(&'static str, f32, Box<dyn Crossover<C>>),
81}
82
83impl<C: Chromosome> Alter<C> for AlterAction<C> {
84    fn alter(&self, population: &mut Population<C>, generation: usize) -> Vec<Metric> {
85        match &self {
86            AlterAction::Mutate(name, rate, m) => {
87                let timer = std::time::Instant::now();
88                let AlterResult(count, metrics) = m.mutate(population, generation, *rate);
89                let metric = Metric::new_operations(name, count, timer.elapsed());
90
91                match metrics {
92                    Some(metrics) => metrics.into_iter().chain(std::iter::once(metric)).collect(),
93                    None => vec![metric],
94                }
95            }
96            AlterAction::Crossover(name, rate, c) => {
97                let timer = std::time::Instant::now();
98                let AlterResult(count, metrics) = c.crossover(population, generation, *rate);
99                let metric = Metric::new_operations(name, count, timer.elapsed());
100
101                match metrics {
102                    Some(metrics) => metrics.into_iter().chain(std::iter::once(metric)).collect(),
103                    None => vec![metric],
104                }
105            }
106        }
107    }
108}
109
110/// The `Crossover` trait is used to define the crossover operation for a genetic algorithm.
111///
112/// In a genetic algorithm, crossover is a genetic operator used to vary the
113/// programming of a chromosome or chromosomes from one generation to the next.
114/// It is analogous to reproduction and biological crossover, upon which genetic algorithms are based.
115///
116/// A `Crossover` typically takes two parent chromosomes and produces two or more offspring chromosomes.
117/// This trait allows you to define your own crossover operation on either the entire population
118/// or a subset of the population. If a struct implements the `Crossover` trait but does not override
119/// any of the methods, the default implementation will perform a simple crossover operation on the
120/// entire population. This is the case with the `UniformCrossover` struct.
121pub trait Crossover<C: Chromosome>: Send + Sync {
122    fn name(&self) -> &'static str {
123        std::any::type_name::<Self>().split("::").last().unwrap()
124    }
125
126    fn rate(&self) -> f32 {
127        1.0
128    }
129
130    fn alterer(self) -> AlterAction<C>
131    where
132        Self: Sized + 'static,
133    {
134        AlterAction::Crossover(self.name(), self.rate(), Box::new(self))
135    }
136
137    #[inline]
138    fn crossover(
139        &self,
140        population: &mut Population<C>,
141        generation: usize,
142        rate: f32,
143    ) -> AlterResult {
144        let mut result = AlterResult::default();
145
146        for i in 0..population.len() {
147            if random_provider::random::<f32>() < rate && population.len() > 3 {
148                let parent_indexes = indexes::individual_indexes(i, population.len(), 2);
149                let cross_result = self.cross(population, &parent_indexes, generation, rate);
150                result.merge(cross_result);
151            }
152        }
153
154        result
155    }
156
157    #[inline]
158    fn cross(
159        &self,
160        population: &mut Population<C>,
161        parent_indexes: &[usize],
162        generation: usize,
163        rate: f32,
164    ) -> AlterResult {
165        let mut result = AlterResult::default();
166
167        if let Some((one, two)) = population.get_pair_mut(parent_indexes[0], parent_indexes[1]) {
168            let cross_result = {
169                let geno_one = one.genotype_mut();
170                let geno_two = two.genotype_mut();
171
172                let min_len = std::cmp::min(geno_one.len(), geno_two.len());
173                let chromosome_index = random_provider::range(0..min_len);
174
175                let chrom_one = &mut geno_one[chromosome_index];
176                let chrom_two = &mut geno_two[chromosome_index];
177
178                self.cross_chromosomes(chrom_one, chrom_two, rate)
179            };
180
181            if cross_result.count() > 0 {
182                one.invalidate(generation);
183                two.invalidate(generation);
184                result.merge(cross_result);
185            }
186        }
187
188        result
189    }
190
191    #[inline]
192    fn cross_chromosomes(&self, chrom_one: &mut C, chrom_two: &mut C, rate: f32) -> AlterResult {
193        let mut cross_count = 0;
194
195        for i in 0..std::cmp::min(chrom_one.len(), chrom_two.len()) {
196            if random_provider::random::<f32>() < rate {
197                let gene_one = chrom_one.get(i);
198                let gene_two = chrom_two.get(i);
199
200                let new_gene_one = gene_one.with_allele(gene_two.allele());
201                let new_gene_two = gene_two.with_allele(gene_one.allele());
202
203                chrom_one.set(i, new_gene_one);
204                chrom_two.set(i, new_gene_two);
205
206                cross_count += 1;
207            }
208        }
209
210        cross_count.into()
211    }
212}
213
214pub trait Mutate<C: Chromosome>: Send + Sync {
215    fn name(&self) -> &'static str {
216        std::any::type_name::<Self>().split("::").last().unwrap()
217    }
218
219    fn rate(&self) -> f32 {
220        1.0
221    }
222
223    fn alterer(self) -> AlterAction<C>
224    where
225        Self: Sized + 'static,
226    {
227        AlterAction::Mutate(self.name(), self.rate(), Box::new(self))
228    }
229
230    #[inline]
231    fn mutate(&self, population: &mut Population<C>, generation: usize, rate: f32) -> AlterResult {
232        let mut result = AlterResult::default();
233
234        for phenotype in population.iter_mut() {
235            let mutate_result = self.mutate_genotype(&mut phenotype.genotype_mut(), rate);
236
237            if mutate_result.count() > 0 {
238                phenotype.invalidate(generation);
239            }
240
241            result.merge(mutate_result);
242        }
243
244        result
245    }
246
247    #[inline]
248    fn mutate_genotype(&self, genotype: &mut Genotype<C>, rate: f32) -> AlterResult {
249        let mut result = AlterResult::default();
250
251        for chromosome in genotype.iter_mut() {
252            let mutate_result = self.mutate_chromosome(chromosome, rate);
253            result.merge(mutate_result);
254        }
255
256        result
257    }
258
259    #[inline]
260    fn mutate_chromosome(&self, chromosome: &mut C, rate: f32) -> AlterResult {
261        let mut count = 0;
262        for gene in chromosome.iter_mut() {
263            if random_provider::random::<f32>() < rate {
264                *gene = self.mutate_gene(gene);
265                count += 1;
266            }
267        }
268
269        count.into()
270    }
271
272    #[inline]
273    fn mutate_gene(&self, gene: &C::Gene) -> C::Gene {
274        gene.new_instance()
275    }
276}