radiate_core/
alter.rs

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