genetic_rs_common/builtin/
repopulator.rs1use crate::Repopulator;
2
3pub trait RandomlyMutable {
5 type Context;
7
8 fn mutate(&mut self, ctx: &Self::Context, rate: f32, rng: &mut impl rand::Rng);
10}
11
12impl<'a, T: RandomlyMutable + 'a, I: Iterator<Item = &'a mut T>> RandomlyMutable for I {
14 type Context = T::Context;
15
16 fn mutate(&mut self, ctx: &Self::Context, rate: f32, rng: &mut impl rand::Rng) {
17 self.for_each(|x| x.mutate(ctx, rate, rng));
18 }
19}
20
21pub trait Mitosis: Clone {
23 type Context;
25
26 fn divide(&self, ctx: &<Self as Mitosis>::Context, rate: f32, rng: &mut impl rand::Rng)
28 -> Self;
29}
30
31impl<T: Mitosis> Mitosis for Vec<T> {
32 type Context = T::Context;
33
34 fn divide(
35 &self,
36 ctx: &<Self as Mitosis>::Context,
37 rate: f32,
38 rng: &mut impl rand::Rng,
39 ) -> Self {
40 let mut child = Vec::with_capacity(self.len());
41 for gene in self {
42 child.push(gene.divide(ctx, rate, rng));
43 }
44 child
45 }
46}
47
48pub struct MitosisRepopulator<G: Mitosis> {
50 pub mutation_rate: f32,
52
53 pub ctx: G::Context,
55 _marker: std::marker::PhantomData<G>,
56}
57
58impl<G: Mitosis> MitosisRepopulator<G> {
59 pub fn new(mutation_rate: f32, ctx: G::Context) -> Self {
61 Self {
62 mutation_rate,
63 ctx,
64 _marker: std::marker::PhantomData,
65 }
66 }
67}
68
69impl<G> Repopulator<G> for MitosisRepopulator<G>
70where
71 G: Mitosis,
72{
73 fn repopulate(&mut self, genomes: &mut Vec<G>, target_size: usize) {
74 let mut rng = rand::rng();
75 let champions = genomes.clone();
76 let mut champs_cycle = champions.iter().cycle();
77
78 while genomes.len() < target_size {
80 let parent = champs_cycle.next().unwrap();
81 let child = parent.divide(&self.ctx, self.mutation_rate, &mut rng);
82 genomes.push(child);
83 }
84 }
85}
86
87#[cfg(feature = "crossover")]
88mod crossover {
89 use rand::RngExt;
90
91 use super::*;
92
93 pub trait Crossover: Clone {
95 type Context;
97
98 fn crossover(
100 &self,
101 other: &Self,
102 ctx: &Self::Context,
103 rate: f32,
104 rng: &mut impl rand::Rng,
105 ) -> Self;
106 }
107
108 pub struct CrossoverRepopulator<G: Crossover> {
110 pub mutation_rate: f32,
112
113 pub ctx: G::Context,
115 _marker: std::marker::PhantomData<G>,
116 }
117
118 impl<G: Crossover> CrossoverRepopulator<G> {
119 pub fn new(mutation_rate: f32, ctx: G::Context) -> Self {
121 Self {
122 mutation_rate,
123 ctx,
124 _marker: std::marker::PhantomData,
125 }
126 }
127 }
128
129 impl<G> Repopulator<G> for CrossoverRepopulator<G>
130 where
131 G: Crossover,
132 {
133 fn repopulate(&mut self, genomes: &mut Vec<G>, target_size: usize) {
134 let mut rng = rand::rng();
135 let champions = genomes.clone();
136 let mut champs_cycle = champions.iter().enumerate().cycle();
137
138 while genomes.len() < target_size {
140 let (i, parent1) = champs_cycle.next().unwrap();
141 let mut j = rng.random_range(1..champions.len());
142 if i == j {
143 j = 0;
144 }
145 let parent2 = &genomes[j];
146
147 let child = parent1.crossover(parent2, &self.ctx, self.mutation_rate, &mut rng);
148
149 genomes.push(child);
150 }
151 }
152 }
153}
154
155#[cfg(feature = "crossover")]
156pub use crossover::*;
157
158#[cfg(feature = "speciation")]
159mod speciation {
160 use std::collections::HashMap;
161
162 use rand::RngExt;
163
164 use super::*;
165
166 pub trait Speciated {
168 type Species: Eq + std::hash::Hash; fn species(&self) -> Self::Species;
174 }
175
176 pub struct SpeciatedCrossoverRepopulator<G: Crossover + Speciated> {
178 pub crossover: CrossoverRepopulator<G>,
181
182 pub allow_emergency_repr: bool,
187
188 _marker: std::marker::PhantomData<G>,
189 }
190
191 impl<G: Crossover + Speciated> SpeciatedCrossoverRepopulator<G> {
192 pub fn new(mutation_rate: f32, allow_emergency_repr: bool, ctx: G::Context) -> Self {
194 Self {
195 crossover: CrossoverRepopulator::new(mutation_rate, ctx),
196 allow_emergency_repr,
197 _marker: std::marker::PhantomData,
198 }
199 }
200 }
201
202 impl<G> Repopulator<G> for SpeciatedCrossoverRepopulator<G>
203 where
204 G: Crossover + Speciated,
205 {
206 fn repopulate(&mut self, genomes: &mut Vec<G>, target_size: usize) {
209 let initial_size = genomes.len();
210 let mut rng = rand::rng();
211 let mut species: HashMap<<G as Speciated>::Species, Vec<&G>> = HashMap::new();
212
213 for genome in genomes.iter() {
214 let spec = genome.species();
215 species.entry(spec).or_insert_with(Vec::new).push(genome);
216 }
217
218 let mut species_iter = species.values();
219 let to_create = target_size - initial_size;
220 let mut new_genomes = Vec::with_capacity(to_create);
221
222 while new_genomes.len() < to_create {
223 if let Some(spec) = species_iter.next() {
224 if spec.len() < 2 {
225 continue;
226 }
227
228 for (i, &parent1) in spec.iter().enumerate() {
229 let mut j = rng.random_range(1..spec.len());
230 if j == i {
231 j = 0;
232 }
233 let parent2 = spec[j];
234
235 new_genomes.push(parent1.crossover(
236 parent2,
237 &self.crossover.ctx,
238 self.crossover.mutation_rate,
239 &mut rng,
240 ));
241 }
242 } else {
243 if new_genomes.is_empty() {
246 if self.allow_emergency_repr {
248 self.crossover.repopulate(genomes, target_size);
249 return;
250 } else {
251 panic!("no genomes with common species");
252 }
253 }
254
255 species_iter = species.values();
256 }
257 }
258
259 genomes.extend(new_genomes);
260 }
261 }
262}
263
264#[cfg(feature = "speciation")]
265pub use speciation::*;