radiate_core/
alter.rs

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