Skip to main content

radiate_core/
alter.rs

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