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 {
155            alter_counts: &mut self.alter_counts,
156            generation,
157            control_rate: rates[0],
158            internal_rates: &rates[1..],
159        };
160
161        match &mut self.inner {
162            AlterInner::Mutate(m) => {
163                let mutator = Arc::get_mut(&mut (*m)).unwrap();
164
165                let timer = std::time::Instant::now();
166                let result = mutator.mutate(population, &mut ctx);
167                metrics.upsert(&self.time_name, timer.elapsed());
168                metrics.upsert(&self.name, result);
169
170                for (name, count) in ctx.alter_counts.iter() {
171                    metrics.upsert(name, *count);
172                }
173            }
174            AlterInner::Crossover(c) => {
175                let timer = std::time::Instant::now();
176                let result = c.crossover(population, &mut ctx);
177                metrics.upsert(&self.time_name, timer.elapsed());
178                metrics.upsert(&self.name, result);
179
180                for (name, count) in ctx.alter_counts.iter() {
181                    metrics.upsert(name, *count);
182                }
183            }
184        }
185
186        Ok(())
187    }
188}
189
190/// Minimum population size required to perform crossover - this ensures that there
191/// are enough individuals to select parents from. If the population size is
192/// less than this value, we will not be able to select two distinct parents.
193const MIN_POPULATION_SIZE: usize = 3;
194/// Minimum number of parents required for crossover operation. This is typically
195/// two, as crossover usually involves two parents to produce offspring.
196const MIN_NUM_PARENTS: usize = 2;
197
198/// The [Crossover] trait is used to define the crossover operation for a genetic algorithm.
199///
200/// In a genetic algorithm, crossover is a genetic operator used to vary the
201/// programming of a chromosome or chromosomes from one generation to the next.
202/// It is analogous to reproduction and biological crossover.
203///
204/// A [Crossover] typically takes two parent [Chromosome]s and produces two or more offspring [Chromosome]s.
205/// This trait allows you to define your own crossover operation on either the entire population
206/// or a subset of the population. If a struct implements the [Crossover] trait but does not override
207/// any of the methods, the default implementation will perform a simple crossover operation on the
208/// entire population.
209pub trait Crossover<C: Chromosome>: Send + Sync {
210    fn name(&self) -> String {
211        generate_metric_key::<Self>(metric_tags::CROSSOVER)
212    }
213
214    fn into_alterer(self) -> Alterer<C>
215    where
216        Self: Sized + 'static,
217    {
218        Alterer::crossover(self.name(), Arc::new(self))
219    }
220
221    fn rates(&self) -> RateSet {
222        RateSet::default()
223    }
224
225    #[inline]
226    fn crossover(&self, mut population: &mut [Phenotype<C>], ctx: &mut AlterContext) -> usize {
227        let mut result = 0;
228        let mut parents = [0; MIN_NUM_PARENTS];
229        let pop_size = population.len();
230
231        for i in 0..pop_size {
232            if random_provider::bool(ctx.rate()) && pop_size > MIN_POPULATION_SIZE {
233                indexes::fill_subset_inclusive(i, pop_size, &mut parents);
234
235                result += population
236                    .get_pair_mut(parents[0], parents[1])
237                    .map(|(one, two)| self.cross(one, two, ctx))
238                    .unwrap_or(0);
239            }
240        }
241
242        result
243    }
244
245    #[inline]
246    fn cross(
247        &self,
248        parent_one: &mut Phenotype<C>,
249        parent_two: &mut Phenotype<C>,
250        ctx: &mut AlterContext,
251    ) -> usize {
252        let geno_one = parent_one.genotype_mut();
253        let geno_two = parent_two.genotype_mut();
254
255        let min_len = std::cmp::min(geno_one.len(), geno_two.len());
256        let chromosome_index = random_provider::range(0..min_len);
257
258        let chrom_one = &mut geno_one[chromosome_index];
259        let chrom_two = &mut geno_two[chromosome_index];
260
261        let cross_result = self.cross_chromosomes(chrom_one, chrom_two, ctx);
262
263        if cross_result > 0 {
264            parent_one.invalidate(ctx.generation());
265            parent_two.invalidate(ctx.generation());
266        }
267
268        cross_result
269    }
270
271    #[inline]
272    fn cross_chromosomes(
273        &self,
274        chrom_one: &mut C,
275        chrom_two: &mut C,
276        ctx: &mut AlterContext,
277    ) -> usize {
278        let mut cross_count = 0;
279
280        for i in 0..std::cmp::min(chrom_one.len(), chrom_two.len()) {
281            if random_provider::bool(ctx.rate()) {
282                let gene_one = chrom_one.get_mut(i);
283                let gene_two = chrom_two.get_mut(i);
284
285                if let Some((gene_one, gene_two)) = gene_one.zip(gene_two) {
286                    std::mem::swap(gene_one, gene_two);
287                    cross_count += 1;
288                }
289            }
290        }
291
292        cross_count
293    }
294}
295
296pub trait Mutate<C: Chromosome>: Send + Sync {
297    fn name(&self) -> String {
298        generate_metric_key::<Self>(metric_tags::MUTATOR)
299    }
300
301    fn into_alterer(self) -> Alterer<C>
302    where
303        Self: Sized + 'static,
304    {
305        Alterer::mutation(self.name(), Arc::new(self))
306    }
307
308    fn rates(&self) -> RateSet {
309        RateSet::default()
310    }
311
312    #[inline]
313    fn mutate(&mut self, population: &mut [Phenotype<C>], ctx: &mut AlterContext) -> usize {
314        population
315            .iter_mut()
316            .map(|phenotype| {
317                let mutate_result = phenotype
318                    .genotype_mut()
319                    .iter_mut()
320                    .fold(0, |acc, chromosome| {
321                        acc + self.mutate_chromosome(chromosome, ctx)
322                    });
323
324                if mutate_result > 0 {
325                    phenotype.invalidate(ctx.generation());
326                }
327
328                mutate_result
329            })
330            .sum()
331    }
332
333    #[inline]
334    fn mutate_chromosome(&mut self, chromosome: &mut C, ctx: &mut AlterContext) -> usize {
335        chromosome
336            .iter_mut()
337            .filter(|_| random_provider::bool(ctx.rate()))
338            .fold(0, |acc, gene| {
339                *gene = gene.new_instance();
340                acc + 1
341            })
342    }
343}