1#![warn(missing_docs)]
2#![allow(clippy::needless_doctest_main)]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5use replace_with::replace_with_or_abort;
8
9#[cfg_attr(docsrs, doc(cfg(feature = "builtin")))]
11#[cfg(feature = "builtin")]
12pub mod builtin;
13
14pub mod prelude;
17
18#[cfg(feature = "rayon")]
19use rayon::prelude::*;
20
21#[cfg(feature = "tracing")]
22use tracing::*;
23
24#[cfg(feature = "tracing")]
25#[allow(missing_docs)]
26pub trait Rng: rand::Rng + std::fmt::Debug {}
27
28#[cfg(feature = "tracing")]
29impl<T: rand::Rng + std::fmt::Debug> Rng for T {}
30
31#[cfg(not(feature = "tracing"))]
32#[allow(missing_docs)]
33pub trait Rng: rand::Rng {}
34
35#[cfg(not(feature = "tracing"))]
36impl<T: rand::Rng> Rng for T {}
37
38pub trait FitnessFn<G> {
40 fn fitness(&self, genome: &G) -> f32;
42}
43
44impl<F: Fn(&G) -> f32, G> FitnessFn<G> for F {
45 fn fitness(&self, genome: &G) -> f32 {
46 (self)(genome)
47 }
48}
49
50pub trait NextgenFn<G> {
52 fn next_gen(&self, fitness: Vec<(G, f32)>) -> Vec<G>;
54}
55
56impl<F: Fn(Vec<(G, f32)>) -> Vec<G>, G> NextgenFn<G> for F {
57 fn next_gen(&self, fitness: Vec<(G, f32)>) -> Vec<G> {
58 (self)(fitness)
59 }
60}
61
62#[cfg(not(feature = "rayon"))]
120pub struct GeneticSim<F, NG, G>
121where
122 F: FitnessFn<G>,
123 NG: NextgenFn<G>,
124 G: Sized,
125{
126 pub genomes: Vec<G>,
128 fitness: F,
129 next_gen: NG,
130}
131
132#[cfg(feature = "rayon")]
134pub struct GeneticSim<F, NG, G>
135where
136 F: FitnessFn<G> + Send + Sync,
137 NG: NextgenFn<G> + Send + Sync,
138 G: Sized + Send,
139{
140 pub genomes: Vec<G>,
142 fitness: F,
143 next_gen: NG,
144}
145
146#[cfg(not(feature = "rayon"))]
147impl<F, NG, G> GeneticSim<F, NG, G>
148where
149 F: FitnessFn<G>,
150 NG: NextgenFn<G>,
151 G: Sized,
152{
153 pub fn new(starting_genomes: Vec<G>, fitness: F, next_gen: NG) -> Self {
156 Self {
157 genomes: starting_genomes,
158 fitness,
159 next_gen,
160 }
161 }
162
163 pub fn next_generation(&mut self) {
165 #[cfg(feature = "tracing")]
167 let span = span!(Level::TRACE, "next_generation");
168
169 #[cfg(feature = "tracing")]
170 let enter = span.enter();
171
172 replace_with_or_abort(&mut self.genomes, |genomes| {
173 let rewards = genomes
174 .into_iter()
175 .map(|g| {
176 let fitness: f32 = self.fitness.fitness(&g);
177 (g, fitness)
178 })
179 .collect();
180
181 self.next_gen.next_gen(rewards)
182 });
183
184 #[cfg(feature = "tracing")]
185 drop(enter);
186 }
187
188 pub fn perform_generations(&mut self, count: usize) {
190 for _ in 0..count {
191 self.next_generation();
192 }
193 }
194}
195
196#[cfg(feature = "rayon")]
197impl<F, NG, G> GeneticSim<F, NG, G>
198where
199 F: FitnessFn<G> + Send + Sync,
200 NG: NextgenFn<G> + Send + Sync,
201 G: Sized + Send,
202{
203 pub fn new(starting_genomes: Vec<G>, fitness: F, next_gen: NG) -> Self {
206 Self {
207 genomes: starting_genomes,
208 fitness,
209 next_gen,
210 }
211 }
212
213 pub fn next_generation(&mut self) {
215 replace_with_or_abort(&mut self.genomes, |genomes| {
216 let rewards = genomes
217 .into_par_iter()
218 .map(|e| {
219 let fitness: f32 = self.fitness.fitness(&e);
220 (e, fitness)
221 })
222 .collect();
223
224 self.next_gen.next_gen(rewards)
225 });
226 }
227
228 pub fn perform_generations(&mut self, count: usize) {
230 for _ in 0..count {
231 self.next_generation();
232 }
233 }
234}
235
236#[cfg(feature = "genrand")]
238#[cfg_attr(docsrs, doc(cfg(feature = "genrand")))]
239pub trait GenerateRandom {
240 fn gen_random(rng: &mut impl Rng) -> Self;
242}
243
244#[cfg(all(feature = "genrand", not(feature = "rayon")))]
246#[cfg_attr(docsrs, doc(cfg(feature = "genrand")))]
247pub trait GenerateRandomCollection<T>
248where
249 T: GenerateRandom,
250{
251 fn gen_random(rng: &mut impl Rng, amount: usize) -> Self;
253}
254
255#[cfg(all(feature = "genrand", feature = "rayon"))]
257pub trait GenerateRandomCollection<T>
258where
259 T: GenerateRandom + Send,
260{
261 fn gen_random(amount: usize) -> Self;
263}
264
265#[cfg(not(feature = "rayon"))]
266impl<C, T> GenerateRandomCollection<T> for C
267where
268 C: FromIterator<T>,
269 T: GenerateRandom,
270{
271 #[cfg_attr(feature = "tracing", instrument)]
272 fn gen_random(rng: &mut impl Rng, amount: usize) -> Self {
273 (0..amount).map(|_| T::gen_random(rng)).collect()
274 }
275}
276
277#[cfg(feature = "rayon")]
278impl<C, T> GenerateRandomCollection<T> for C
279where
280 C: FromParallelIterator<T>,
281 T: GenerateRandom + Send,
282{
283 #[cfg_attr(feature = "tracing", instrument)]
284 fn gen_random(amount: usize) -> Self {
285 (0..amount)
286 .into_par_iter()
287 .map(|_| T::gen_random(&mut rand::rng()))
288 .collect()
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::prelude::*;
295
296 #[test]
297 fn send_sim() {
298 let mut sim = GeneticSim::new(vec![()], |_: &()| 0., |_: Vec<((), f32)>| vec![()]);
299
300 let h = std::thread::spawn(move || {
301 sim.next_generation();
302 });
303
304 h.join().unwrap();
305 }
306}