Skip to main content

radiate_core/
alter.rs

1use crate::{Chromosome, Gene, MetricSet, math::indexes, random_provider, stats::metric_tags};
2use crate::{GetPairMut, Phenotype};
3use crate::{RateSet, error::RadiateResult};
4pub use radiate_expr::*;
5use radiate_utils::{SmallStr, generate_metric_key};
6use std::collections::HashMap;
7use std::sync::Arc;
8
9#[macro_export]
10macro_rules! alters {
11    ($($struct_instance:expr),* $(,)?) => {
12        {
13            let mut vec: Vec<Alterer<_>> = Vec::new();
14            $(
15                vec.push($struct_instance.into_alterer());
16            )*
17            vec
18        }
19    };
20}
21
22#[derive(Clone, Default)]
23pub struct AlterUpdates(pub HashMap<SmallStr, usize>);
24
25impl AlterUpdates {
26    pub fn new() -> Self {
27        AlterUpdates(HashMap::new())
28    }
29
30    pub fn clear(&mut self) {
31        for value in self.0.values_mut() {
32            *value = 0;
33        }
34    }
35
36    pub fn iter(&self) -> impl Iterator<Item = (&SmallStr, &usize)> {
37        self.0.iter().filter(|(_, count)| **count > 0)
38    }
39
40    pub fn upsert(&mut self, name: impl AsRef<str>, value: usize) {
41        if let Some(existing) = self.0.get_mut(name.as_ref()) {
42            *existing += value;
43        } else {
44            self.0
45                .insert(SmallStr::from_string(name.as_ref().into()), value);
46        }
47    }
48}
49
50pub struct AlterContext<'a> {
51    alter_counts: &'a mut AlterUpdates,
52    generation: usize,
53    control_rate: f32,
54    internal_rates: &'a [f32],
55}
56
57impl<'a> AlterContext<'a> {
58    pub fn new(
59        alter_counts: &'a mut AlterUpdates,
60        generation: usize,
61        control_rate: f32,
62        internal_rates: &'a [f32],
63    ) -> Self {
64        AlterContext {
65            alter_counts,
66            generation,
67            control_rate,
68            internal_rates,
69        }
70    }
71
72    pub fn rate(&self) -> f32 {
73        self.control_rate
74    }
75
76    pub fn internal_rate(&self, index: usize) -> f32 {
77        self.internal_rates.get(index).copied().unwrap_or(0.0)
78    }
79
80    pub fn generation(&self) -> usize {
81        self.generation
82    }
83
84    pub fn upsert(&mut self, name: impl AsRef<str>, value: usize) {
85        self.alter_counts.upsert(name, value);
86    }
87}
88
89#[derive(Clone)]
90pub enum AlterInner<C: Chromosome> {
91    Mutate(Arc<dyn Mutate<C>>),
92    Crossover(Arc<dyn Crossover<C>>),
93}
94
95/// The [Alterer] struct is used to represent the different
96/// types of alterations that can be performed on a
97/// population - It can be either a mutation or a crossover operation.
98#[derive(Clone)]
99pub struct Alterer<C: Chromosome> {
100    time_name: SmallStr,
101    name: SmallStr,
102    inner: AlterInner<C>,
103    alter_counts: AlterUpdates,
104    rate_set: RateSet,
105}
106
107impl<C: Chromosome> Alterer<C> {
108    pub fn mutation(name: impl Into<SmallStr>, m: Arc<dyn Mutate<C>>) -> Self {
109        Self::build_internal(name, AlterInner::Mutate(m))
110    }
111
112    pub fn crossover(name: impl Into<SmallStr>, c: Arc<dyn Crossover<C>>) -> Self {
113        Self::build_internal(name, AlterInner::Crossover(c))
114    }
115
116    fn build_internal(name: impl Into<SmallStr>, inner: AlterInner<C>) -> Self {
117        let name = name.into();
118
119        let time_name = SmallStr::from_string(format!("{}.time", name));
120        let control_rate_name = SmallStr::from_string(format!("{}.rate", name));
121
122        let rate_set = match &inner {
123            AlterInner::Mutate(m) => m.rates().alias(control_rate_name.clone()),
124            AlterInner::Crossover(c) => c.rates().alias(control_rate_name.clone()),
125        };
126
127        Self {
128            time_name,
129            name,
130            inner,
131            alter_counts: AlterUpdates::new(),
132            rate_set,
133        }
134    }
135
136    pub fn rates(&self) -> &RateSet {
137        &self.rate_set
138    }
139
140    pub fn name(&self) -> &str {
141        &self.name
142    }
143
144    pub fn alter(
145        &mut self,
146        population: &mut [Phenotype<C>],
147        metrics: &mut MetricSet,
148        generation: usize,
149    ) -> RadiateResult<()> {
150        let rates = self.rate_set.calculate_rates(generation, metrics)?;
151
152        self.alter_counts.clear();
153
154        let mut ctx = AlterContext::new(&mut self.alter_counts, generation, rates[0], &rates[1..]);
155
156        match &mut self.inner {
157            AlterInner::Mutate(m) => {
158                let mutator = Arc::get_mut(&mut (*m)).unwrap();
159
160                let timer = std::time::Instant::now();
161                let result = mutator.mutate(population, &mut ctx);
162                metrics.upsert(&self.time_name, timer.elapsed());
163                metrics.upsert(&self.name, result);
164
165                for (name, count) in ctx.alter_counts.iter() {
166                    metrics.upsert(name, *count);
167                }
168            }
169            AlterInner::Crossover(c) => {
170                let timer = std::time::Instant::now();
171                let result = c.crossover(population, &mut ctx);
172                metrics.upsert(&self.time_name, timer.elapsed());
173                metrics.upsert(&self.name, result);
174
175                for (name, count) in ctx.alter_counts.iter() {
176                    metrics.upsert(name, *count);
177                }
178            }
179        }
180
181        Ok(())
182    }
183}
184
185/// Minimum population size required to perform crossover - this ensures that there
186/// are enough individuals to select parents from. If the population size is
187/// less than this value, we will not be able to select two distinct parents.
188const MIN_POPULATION_SIZE: usize = 3;
189/// Minimum number of parents required for crossover operation. This is typically
190/// two, as crossover usually involves two parents to produce offspring.
191const MIN_NUM_PARENTS: usize = 2;
192
193/// The [Crossover] trait is used to define the crossover operation for a genetic algorithm.
194///
195/// In a genetic algorithm, crossover is a genetic operator used to vary the
196/// programming of a chromosome or chromosomes from one generation to the next.
197/// It is analogous to reproduction and biological crossover.
198///
199/// A [Crossover] typically takes two parent [Chromosome]s and produces two or more offspring [Chromosome]s.
200/// This trait allows you to define your own crossover operation on either the entire population
201/// or a subset of the population. If a struct implements the [Crossover] trait but does not override
202/// any of the methods, the default implementation will perform a simple crossover operation on the
203/// entire population.
204pub trait Crossover<C: Chromosome>: Send + Sync {
205    fn name(&self) -> String {
206        generate_metric_key::<Self>(metric_tags::CROSSOVER)
207    }
208
209    fn into_alterer(self) -> Alterer<C>
210    where
211        Self: Sized + 'static,
212    {
213        Alterer::crossover(self.name(), Arc::new(self))
214    }
215
216    fn rates(&self) -> RateSet {
217        RateSet::default()
218    }
219
220    #[inline]
221    fn crossover(&self, mut population: &mut [Phenotype<C>], ctx: &mut AlterContext) -> usize {
222        let mut result = 0;
223        let mut parents = [0; MIN_NUM_PARENTS];
224        let pop_size = population.len();
225
226        for i in 0..pop_size {
227            if random_provider::bool(ctx.rate()) && pop_size > MIN_POPULATION_SIZE {
228                indexes::fill_subset_inclusive(i, pop_size, &mut parents);
229
230                result += population
231                    .get_pair_mut(parents[0], parents[1])
232                    .map(|(one, two)| self.cross(one, two, ctx))
233                    .unwrap_or(0);
234            }
235        }
236
237        result
238    }
239
240    #[inline]
241    fn cross(
242        &self,
243        parent_one: &mut Phenotype<C>,
244        parent_two: &mut Phenotype<C>,
245        ctx: &mut AlterContext,
246    ) -> usize {
247        let geno_one = parent_one.genotype_mut();
248        let geno_two = parent_two.genotype_mut();
249
250        let min_len = std::cmp::min(geno_one.len(), geno_two.len());
251        let chromosome_index = random_provider::range(0..min_len);
252
253        let chrom_one = &mut geno_one[chromosome_index];
254        let chrom_two = &mut geno_two[chromosome_index];
255
256        let cross_result = self.cross_chromosomes(chrom_one, chrom_two, ctx);
257
258        if cross_result > 0 {
259            parent_one.invalidate(ctx.generation());
260            parent_two.invalidate(ctx.generation());
261        }
262
263        cross_result
264    }
265
266    #[inline]
267    fn cross_chromosomes(
268        &self,
269        chrom_one: &mut C,
270        chrom_two: &mut C,
271        ctx: &mut AlterContext,
272    ) -> usize {
273        let mut cross_count = 0;
274
275        for i in 0..std::cmp::min(chrom_one.len(), chrom_two.len()) {
276            if random_provider::bool(ctx.rate()) {
277                let gene_one = chrom_one.get_mut(i);
278                let gene_two = chrom_two.get_mut(i);
279
280                if let Some((gene_one, gene_two)) = gene_one.zip(gene_two) {
281                    std::mem::swap(gene_one, gene_two);
282                    cross_count += 1;
283                }
284            }
285        }
286
287        cross_count
288    }
289}
290
291pub trait Mutate<C: Chromosome>: Send + Sync {
292    fn name(&self) -> String {
293        generate_metric_key::<Self>(metric_tags::MUTATOR)
294    }
295
296    fn into_alterer(self) -> Alterer<C>
297    where
298        Self: Sized + 'static,
299    {
300        Alterer::mutation(self.name(), Arc::new(self))
301    }
302
303    fn rates(&self) -> RateSet {
304        RateSet::default()
305    }
306
307    #[inline]
308    fn mutate(&mut self, population: &mut [Phenotype<C>], ctx: &mut AlterContext) -> usize {
309        population
310            .iter_mut()
311            .map(|phenotype| {
312                let mutate_result = phenotype
313                    .genotype_mut()
314                    .iter_mut()
315                    .fold(0, |acc, chromosome| {
316                        acc + self.mutate_chromosome(chromosome, ctx)
317                    });
318
319                if mutate_result > 0 {
320                    phenotype.invalidate(ctx.generation());
321                }
322
323                mutate_result
324            })
325            .sum()
326    }
327
328    #[inline]
329    fn mutate_chromosome(&mut self, chromosome: &mut C, ctx: &mut AlterContext) -> usize {
330        chromosome
331            .iter_mut()
332            .filter(|_| random_provider::bool(ctx.rate()))
333            .fold(0, |acc, gene| {
334                *gene = gene.new_instance();
335                acc + 1
336            })
337    }
338}