genetic_rs_common/builtin/
repopulator.rs1use rand::Rng as RandRng;
2
3use crate::{Repopulator, Rng};
4
5#[cfg(feature = "tracing")]
6use tracing::*;
7
8pub trait RandomlyMutable {
10 fn mutate(&mut self, rate: f32, rng: &mut impl Rng);
12}
13
14#[cfg(not(feature = "tracing"))]
17pub trait FeatureBoundedRandomlyMutable: RandomlyMutable {}
18#[cfg(not(feature = "tracing"))]
19impl<T: RandomlyMutable> FeatureBoundedRandomlyMutable for T {}
20
21#[cfg(feature = "tracing")]
24pub trait FeatureBoundedRandomlyMutable: RandomlyMutable + std::fmt::Debug {}
25#[cfg(feature = "tracing")]
26impl<T: RandomlyMutable + std::fmt::Debug> FeatureBoundedRandomlyMutable for T {}
27
28pub trait Mitosis: Clone + FeatureBoundedRandomlyMutable {
30 fn divide(&self, rate: f32, rng: &mut impl Rng) -> Self {
32 let mut child = self.clone();
33 child.mutate(rate, rng);
34 child
35 }
36}
37
38#[cfg(all(feature = "crossover", not(feature = "tracing")))]
40#[cfg_attr(docsrs, doc(cfg(feature = "crossover")))]
41pub trait Crossover: Clone + PartialEq {
42 fn crossover(&self, other: &Self, rate: f32, rng: &mut impl Rng) -> Self;
44}
45
46#[cfg(all(feature = "crossover", feature = "tracing"))]
48#[cfg_attr(docsrs, doc(cfg(feature = "crossover")))]
49pub trait Crossover: Clone + std::fmt::Debug {
50 fn crossover(&self, other: &Self, rate: f32, rng: &mut impl Rng) -> Self;
52}
53
54#[cfg(feature = "speciation")]
56#[cfg_attr(docsrs, doc(cfg(feature = "speciation")))]
57pub trait Speciated: Sized {
58 fn is_same_species(&self, other: &Self) -> bool;
60
61 fn filter_same_species<'a>(&'a self, genomes: &'a [Self]) -> Vec<&'a Self> {
63 genomes.iter().filter(|g| self.is_same_species(g)).collect()
64 }
65}
66
67pub struct MitosisRepopulator<G: Mitosis> {
69 pub mutation_rate: f32,
71 _marker: std::marker::PhantomData<G>,
72}
73
74impl<G: Mitosis> MitosisRepopulator<G> {
75 pub fn new(mutation_rate: f32) -> Self {
77 Self {
78 mutation_rate,
79 _marker: std::marker::PhantomData,
80 }
81 }
82}
83
84impl<G> Repopulator<G> for MitosisRepopulator<G>
85where
86 G: Mitosis,
87{
88 fn repopulate(&self, genomes: &mut Vec<G>, target_size: usize) {
89 let mut rng = rand::rng();
90 let champions = genomes.clone();
91 let mut champs_cycle = champions.iter().cycle();
92
93 while genomes.len() < target_size {
95 let parent = champs_cycle.next().unwrap();
96 let child = parent.divide(self.mutation_rate, &mut rng);
97 genomes.push(child);
98 }
99 }
100}
101
102pub struct CrossoverRepopulator<G: Crossover> {
104 pub mutation_rate: f32,
106 _marker: std::marker::PhantomData<G>,
107}
108
109impl<G: Crossover> CrossoverRepopulator<G> {
110 pub fn new(mutation_rate: f32) -> Self {
112 Self {
113 mutation_rate,
114 _marker: std::marker::PhantomData,
115 }
116 }
117}
118
119impl<G> Repopulator<G> for CrossoverRepopulator<G>
120where
121 G: Crossover,
122{
123 fn repopulate(&self, genomes: &mut Vec<G>, target_size: usize) {
124 let mut rng = rand::rng();
125 let champions = genomes.clone();
126 let mut champs_cycle = champions.iter().enumerate().cycle();
127
128 while genomes.len() < target_size {
130 let (i, parent1) = champs_cycle.next().unwrap();
131 let mut j = rng.random_range(1..champions.len());
132 if i == j {
133 j = 0;
134 }
135 let parent2 = &genomes[j];
136
137 #[cfg(feature = "tracing")]
138 let span = span!(
139 Level::DEBUG,
140 "crossover",
141 a = tracing::field::debug(parent1),
142 b = tracing::field::debug(parent2)
143 );
144 #[cfg(feature = "tracing")]
145 let enter = span.enter();
146
147 let child = parent1.crossover(parent2, self.mutation_rate, &mut rng);
148
149 #[cfg(feature = "tracing")]
150 drop(enter);
151
152 genomes.push(child);
153 }
154 }
155}
156
157#[cfg(feature = "speciation")]
159pub struct SpeciatedCrossoverRepopulator<G: Crossover + Speciated + PartialEq> {
160 pub mutation_rate: f32,
162 _marker: std::marker::PhantomData<G>,
163}
164
165#[cfg(feature = "speciation")]
166impl<G: Crossover + Speciated + PartialEq> SpeciatedCrossoverRepopulator<G> {
167 pub fn new(mutation_rate: f32) -> Self {
169 Self {
170 mutation_rate,
171 _marker: std::marker::PhantomData,
172 }
173 }
174}
175
176#[cfg(feature = "speciation")]
177impl<G> Repopulator<G> for SpeciatedCrossoverRepopulator<G>
178where
179 G: Crossover + Speciated + PartialEq,
180{
181 fn repopulate(&self, genomes: &mut Vec<G>, target_size: usize) {
182 let mut rng = rand::rng();
183 let champions = genomes.clone();
184 let mut champs_cycle = champions.iter().cycle();
185
186 while genomes.len() < target_size {
188 let parent1 = champs_cycle.next().unwrap();
189 let mut parent2 = &champions[rng.random_range(0..champions.len() - 1)];
190
191 while parent1 == parent2 || !parent1.is_same_species(parent2) {
192 parent2 = &champions[rng.random_range(0..champions.len() - 1)];
194 }
195
196 #[cfg(feature = "tracing")]
197 let span = span!(
198 Level::DEBUG,
199 "crossover",
200 a = tracing::field::debug(parent1),
201 b = tracing::field::debug(parent2)
202 );
203 #[cfg(feature = "tracing")]
204 let enter = span.enter();
205
206 let child = parent1.crossover(parent2, self.mutation_rate, &mut rng);
207
208 #[cfg(feature = "tracing")]
209 drop(enter);
210
211 genomes.push(child);
212 }
213 }
214}