Skip to main content

radiate_core/
alter.rs

1use crate::{Chromosome, Gene, Genotype, Population, math::indexes, random_provider};
2use crate::{Lineage, LineageUpdate, MetricSet, MetricUpdate, Rate, metric};
3use radiate_utils::{ToSnakeCase, intern};
4use std::sync::Arc;
5
6#[macro_export]
7macro_rules! alters {
8    ($($struct_instance:expr),* $(,)?) => {
9        {
10            let mut vec: Vec<Alterer<_>> = Vec::new();
11            $(
12                vec.push($struct_instance.alterer());
13            )*
14            vec
15        }
16    };
17}
18
19/// The [AlterResult] struct is used to represent the result of an
20/// alteration operation. It contains the number of operations
21/// performed and a vector of metrics that were collected
22/// during the alteration process.
23pub struct AlterResult(pub usize);
24
25impl AlterResult {
26    pub fn empty() -> Self {
27        Default::default()
28    }
29
30    pub fn count(&self) -> usize {
31        self.0
32    }
33
34    pub fn merge(&mut self, other: AlterResult) {
35        let AlterResult(other_count) = other;
36        self.0 += other_count;
37    }
38}
39
40impl Default for AlterResult {
41    fn default() -> Self {
42        AlterResult(0)
43    }
44}
45
46impl From<usize> for AlterResult {
47    fn from(value: usize) -> Self {
48        AlterResult(value)
49    }
50}
51
52pub struct AlterContext<'a> {
53    metrics: &'a mut MetricSet,
54    lineage: &'a mut Lineage,
55    operation: &'static str,
56    generation: usize,
57    rate: f32,
58}
59
60impl<'a> AlterContext<'a> {
61    pub fn new(
62        operation: &'static str,
63        metrics: &'a mut MetricSet,
64        lineage: &'a mut Lineage,
65        generation: usize,
66        rate: f32,
67    ) -> Self {
68        Self {
69            metrics,
70            lineage,
71            operation,
72            generation,
73            rate,
74        }
75    }
76
77    pub fn rate(&self) -> f32 {
78        self.rate
79    }
80
81    pub fn generation(&self) -> usize {
82        self.generation
83    }
84
85    pub fn metric(&mut self, name: &'static str, value: impl Into<MetricUpdate<'a>>) {
86        self.metrics.upsert((name, value.into()));
87    }
88
89    pub fn update_lineage(&mut self, update: impl Into<LineageUpdate>) {
90        self.lineage.push(self.operation, update.into());
91    }
92}
93
94/// The [Alterer] enum is used to represent the different
95/// types of alterations that can be performed on a
96/// population - It can be either a mutation or a crossover operation.
97#[derive(Clone)]
98pub enum Alterer<C: Chromosome> {
99    Mutate(&'static str, Rate, Arc<dyn Mutate<C>>),
100    Crossover(&'static str, Rate, Arc<dyn Crossover<C>>),
101}
102
103impl<C: Chromosome> Alterer<C> {
104    pub fn name(&self) -> &'static str {
105        match &self {
106            Alterer::Mutate(name, _, _) => name,
107            Alterer::Crossover(name, _, _) => name,
108        }
109    }
110
111    pub fn rate(&mut self) -> &mut Rate {
112        match self {
113            Alterer::Mutate(_, rate, _) => rate,
114            Alterer::Crossover(_, rate, _) => rate,
115        }
116    }
117
118    #[inline]
119    pub fn alter(
120        &mut self,
121        population: &mut Population<C>,
122        lineage: &mut Lineage,
123        metrics: &mut MetricSet,
124        generation: usize,
125    ) {
126        let rate = self.rate().get(generation, metrics);
127        let operation = self.name();
128
129        metrics.upsert(metric!(
130            radiate_utils::intern!(format!("{}.rate", operation)),
131            rate
132        ));
133
134        let mut ctx = AlterContext {
135            metrics,
136            lineage,
137            operation,
138            generation,
139            rate,
140        };
141
142        match &self {
143            Alterer::Mutate(name, _, m) => {
144                let timer = std::time::Instant::now();
145                let AlterResult(count) = m.mutate(population, &mut ctx);
146                metrics.upsert((*name, count));
147                metrics.upsert((
148                    radiate_utils::intern!(format!("{}.time", name)),
149                    timer.elapsed(),
150                ));
151            }
152            Alterer::Crossover(name, _, c) => {
153                let timer = std::time::Instant::now();
154                let AlterResult(count) = c.crossover(population, &mut ctx);
155                metrics.upsert((*name, count));
156                metrics.upsert((
157                    radiate_utils::intern!(format!("{}.time", name)),
158                    timer.elapsed(),
159                ));
160            }
161        }
162    }
163}
164
165/// Minimum population size required to perform crossover - this ensures that there
166/// are enough individuals to select parents from. If the population size is
167/// less than this value, we will not be able to select two distinct parents.
168const MIN_POPULATION_SIZE: usize = 3;
169/// Minimum number of parents required for crossover operation. This is typically
170/// two, as crossover usually involves two parents to produce offspring.
171const MIN_NUM_PARENTS: usize = 2;
172
173/// The [Crossover] trait is used to define the crossover operation for a genetic algorithm.
174///
175/// In a genetic algorithm, crossover is a genetic operator used to vary the
176/// programming of a chromosome or chromosomes from one generation to the next.
177/// It is analogous to reproduction and biological crossover.
178///
179/// A [Crossover] typically takes two parent [Chromosome]s and produces two or more offspring [Chromosome]s.
180/// This trait allows you to define your own crossover operation on either the entire population
181/// or a subset of the population. If a struct implements the [Crossover] trait but does not override
182/// any of the methods, the default implementation will perform a simple crossover operation on the
183/// entire population.
184pub trait Crossover<C: Chromosome>: Send + Sync {
185    fn name(&self) -> String {
186        let name = std::any::type_name::<Self>()
187            .split("::")
188            .last()
189            .map(|s| s.to_snake_case())
190            .unwrap();
191
192        let path = name.split('_').collect::<Vec<&str>>();
193        let mut new_name = vec!["crossover"];
194        for part in path {
195            if !part.contains("crossov") {
196                new_name.push(part);
197            }
198        }
199
200        new_name.join(".")
201    }
202
203    fn rate(&self) -> Rate {
204        Rate::default()
205    }
206
207    fn alterer(self) -> Alterer<C>
208    where
209        Self: Sized + 'static,
210    {
211        Alterer::Crossover(intern!(self.name()), self.rate(), Arc::new(self))
212    }
213
214    #[inline]
215    fn crossover(&self, population: &mut Population<C>, ctx: &mut AlterContext) -> AlterResult {
216        let mut result = AlterResult::default();
217        let mut parents = [0usize; MIN_NUM_PARENTS];
218
219        for i in 0..population.len() {
220            if random_provider::bool(ctx.rate()) && population.len() > MIN_POPULATION_SIZE {
221                indexes::individual_indexes(i, population.len(), MIN_NUM_PARENTS, &mut parents);
222                let cross_result = self.cross(population, &parents, ctx);
223                result.merge(cross_result);
224            }
225        }
226
227        result
228    }
229
230    #[inline]
231    fn cross(
232        &self,
233        population: &mut Population<C>,
234        parent_indexes: &[usize],
235        ctx: &mut AlterContext,
236    ) -> AlterResult {
237        let mut result = AlterResult::default();
238
239        if let Some((one, two)) = population.get_pair_mut(parent_indexes[0], parent_indexes[1]) {
240            let cross_result = {
241                let geno_one = one.genotype_mut();
242                let geno_two = two.genotype_mut();
243
244                let min_len = std::cmp::min(geno_one.len(), geno_two.len());
245                let chromosome_index = random_provider::range(0..min_len);
246
247                let chrom_one = &mut geno_one[chromosome_index];
248                let chrom_two = &mut geno_two[chromosome_index];
249
250                self.cross_chromosomes(chrom_one, chrom_two, ctx)
251            };
252
253            if cross_result.count() > 0 {
254                let parent_lineage = (one.family(), two.family());
255                let parent_ids = (one.id(), two.id());
256                one.invalidate(ctx.generation());
257                two.invalidate(ctx.generation());
258
259                ctx.update_lineage((parent_lineage, parent_ids, one.id()));
260                ctx.update_lineage((parent_lineage, parent_ids, two.id()));
261                result.merge(cross_result);
262            }
263        }
264
265        result
266    }
267
268    #[inline]
269    fn cross_chromosomes(
270        &self,
271        chrom_one: &mut C,
272        chrom_two: &mut C,
273        ctx: &mut AlterContext,
274    ) -> AlterResult {
275        let mut cross_count = 0;
276
277        for i in 0..std::cmp::min(chrom_one.len(), chrom_two.len()) {
278            if random_provider::bool(ctx.rate()) {
279                let gene_one = chrom_one.get(i);
280                let gene_two = chrom_two.get(i);
281
282                let new_gene_one = gene_one.with_allele(gene_two.allele());
283                let new_gene_two = gene_two.with_allele(gene_one.allele());
284
285                chrom_one.set(i, new_gene_one);
286                chrom_two.set(i, new_gene_two);
287
288                cross_count += 1;
289            }
290        }
291
292        AlterResult::from(cross_count)
293    }
294}
295
296pub trait Mutate<C: Chromosome>: Send + Sync {
297    fn name(&self) -> String {
298        let name = std::any::type_name::<Self>()
299            .split("::")
300            .last()
301            .map(|s| s.to_snake_case())
302            .unwrap();
303
304        let path = name.split('_').collect::<Vec<&str>>();
305        let mut new_name = vec!["mutate"];
306        for part in path {
307            if !part.contains("mutat") {
308                new_name.push(part);
309            }
310        }
311
312        new_name.join(".")
313    }
314
315    fn rate(&self) -> Rate {
316        Rate::default()
317    }
318
319    fn alterer(self) -> Alterer<C>
320    where
321        Self: Sized + 'static,
322    {
323        Alterer::Mutate(intern!(self.name()), self.rate(), Arc::new(self))
324    }
325
326    #[inline]
327    fn mutate(&self, population: &mut Population<C>, ctx: &mut AlterContext) -> AlterResult {
328        let mut result = AlterResult::default();
329
330        for phenotype in population.iter_mut() {
331            let mutate_result = self.mutate_genotype(phenotype.genotype_mut(), ctx);
332
333            if mutate_result.count() > 0 {
334                let parent = (phenotype.family(), phenotype.id());
335                phenotype.invalidate(ctx.generation());
336                ctx.update_lineage((parent, phenotype.id()));
337            }
338
339            result.merge(mutate_result);
340        }
341
342        result
343    }
344
345    #[inline]
346    fn mutate_genotype(&self, genotype: &mut Genotype<C>, ctx: &mut AlterContext) -> AlterResult {
347        let mut result = AlterResult::default();
348
349        for chromosome in genotype.iter_mut() {
350            let mutate_result = self.mutate_chromosome(chromosome, ctx);
351            result.merge(mutate_result);
352        }
353
354        result
355    }
356
357    #[inline]
358    fn mutate_chromosome(&self, chromosome: &mut C, ctx: &mut AlterContext) -> AlterResult {
359        let mut count = 0;
360        for gene in chromosome.iter_mut() {
361            if random_provider::bool(ctx.rate()) {
362                count += self.mutate_gene(gene);
363            }
364        }
365
366        count.into()
367    }
368
369    #[inline]
370    fn mutate_gene(&self, gene: &mut C::Gene) -> usize {
371        *gene = gene.new_instance();
372        1
373    }
374}