1extern crate historian;
6extern crate rand;
7extern crate rayon;
8
9pub mod age;
10pub mod crossover;
12pub mod genotype;
13pub mod mutation_rate;
14pub mod niches_beta_rate;
15pub mod population_refitness;
16pub mod prelude;
17pub mod selection;
18pub mod selection_rate;
19pub mod slope_params;
20pub mod stop_criteria;
21pub mod survival_pressure;
22
23pub use age::*;
24pub use crossover::*;
25pub use genotype::Genotype;
26pub use mutation_rate::*;
27pub use niches_beta_rate::*;
28pub use population_refitness::*;
29pub use selection::*;
30pub use selection_rate::*;
31pub use slope_params::*;
32use std::fmt::Display;
33use std::marker::PhantomData;
34pub use stop_criteria::*;
35pub use survival_pressure::*;
36
37use historian::Histo;
38use rand::distributions::{Standard, Uniform};
39use rand::prelude::*;
40use rayon::prelude::*;
41#[cfg(feature = "global_cache")]
42use std::collections::HashMap;
43use std::fs::File;
44use std::io::prelude::*;
45use std::sync::mpsc::channel;
46
47const POPULATION_SEPARATOR: &[u8] = b"\n\n\n\n---------------------------------\n\n\n\n";
48const POPULATION_ERR_MSG: &str = "Error writing on population log file";
49const PROGRESS_ERR_MSG: &str = "Error writing in progress log file";
50const PROGRESS_HEADER: &[u8] = b"Generation\t\
51 Solutions\t\
52 Last progress\t\
53 Progress avg\t\
54 Progress std\t\
55 Progress max\t\
56 Progress min\t\
57 Progress p10\t\
58 Progress p25\t\
59 Progress median\t\
60 Progress p75\t\
61 Progress p90\t\
62 Fitness avg\t\
63 Fitness std\t\
64 Fitness max\t\
65 Fitness min\t\
66 Fitness p10\t\
67 Fitness p25\t\
68 Fitness median\t\
69 Fitness p75\t\
70 Fitness p90\n";
71
72#[derive(Copy, Clone, Debug)]
74pub struct Fitness {
75 age: u64,
77 fitness: f64,
79 original_fitness: f64,
81 age_effect: f64,
83 refitness_effect: f64,
85}
86
87#[derive(Debug)]
89pub struct IndWithFitness<T: PartialEq + Send + Sync, Ind: Genotype<T>> {
90 pub ind: Ind,
92 pub fitness: Option<Fitness>,
94 _phantom: PhantomData<T>,
95}
96
97impl<T: PartialEq + Send + Sync, Ind: Genotype<T>> IndWithFitness<T, Ind> {
98 pub fn new(ind: Ind, fitness: Option<Fitness>) -> IndWithFitness<T, Ind> {
99 IndWithFitness {
100 ind,
101 fitness,
102 _phantom: PhantomData,
103 }
104 }
105}
106
107impl<T: PartialEq + Send + Sync, Ind: Genotype<T>> Display for IndWithFitness<T, Ind> {
108 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
109 write!(f, "ind: {}, fitness: {:?}", self.ind, self.fitness)
110 }
111}
112
113pub struct GeneticExecution<T: PartialEq + Send + Sync, Ind: Genotype<T>> {
115 population_size: usize,
117 population: Vec<IndWithFitness<T, Ind>>,
119 genotype_size: Ind::ProblemSize,
121 mutation_rate: Box<dyn MutationRate>,
123 selection_rate: Box<dyn SelectionRate>,
125 selection: Box<dyn Selection>,
127 age: Box<dyn Age>,
129 crossover: Box<dyn Crossover<T, Ind>>,
131 population_refitness: Box<dyn PopulationRefitness<T, Ind>>,
134 survival_pressure: Box<dyn SurvivalPressure<T, Ind>>,
136 stop_criterion: Box<dyn StopCriterion>,
138 cache_fitness: bool,
140 global_cache: bool,
144 #[cfg(feature = "global_cache")]
146 cache_map: HashMap<Ind::GenotypeHash, f64>,
147 progress_log: (u64, Option<File>),
149 population_log: (u64, Option<File>),
151}
152
153impl<T: PartialEq + Send + Sync, Ind: Genotype<T>> Default for GeneticExecution<T, Ind> {
154 fn default() -> Self {
155 GeneticExecution {
156 population_size: 64,
157 population: Vec::new(),
158 genotype_size: Ind::ProblemSize::default(),
159 mutation_rate: Box::new(MutationRates::Constant(0.1)),
160 selection_rate: Box::new(SelectionRates::Constant(2)),
161 selection: Box::new(SelectionFunctions::Cup),
162 age: Box::new(AgeFunctions::None),
163 crossover: Box::new(CrossoverFunctions::SingleCrossPoint),
164 population_refitness: Box::new(PopulationRefitnessFunctions::None),
165 survival_pressure: Box::new(SurvivalPressureFunctions::Worst),
166 stop_criterion: Box::new(StopCriteria::SolutionFound),
167 cache_fitness: true,
168 global_cache: cfg!(feature = "global_cache"),
169 #[cfg(feature = "global_cache")]
170 cache_map: HashMap::new(),
171 progress_log: (0, None),
172 population_log: (0, None),
173 }
174 }
175}
176
177impl<T: PartialEq + Send + Sync, Ind: Genotype<T>> GeneticExecution<T, Ind> {
178 pub fn new() -> Self {
180 Self::default()
181 }
182
183 pub fn population_size(mut self, new_pop_size: usize) -> Self {
185 self.population_size = new_pop_size;
186 self
187 }
188
189 pub fn population(mut self, new_pop: Vec<IndWithFitness<T, Ind>>) -> Self {
193 self.population = new_pop;
194 self
195 }
196
197 pub fn genotype_size(mut self, new_size: Ind::ProblemSize) -> Self {
199 self.genotype_size = new_size;
200 self
201 }
202
203 pub fn mutation_rate(mut self, new_mut: Box<dyn MutationRate>) -> Self {
205 self.mutation_rate = new_mut;
206 self
207 }
208
209 pub fn selection_rate(mut self, new_sel_rate: Box<dyn SelectionRate>) -> Self {
211 self.selection_rate = new_sel_rate;
212 self
213 }
214
215 pub fn select_function(mut self, new_sel: Box<dyn Selection>) -> Self {
217 self.selection = new_sel;
218 self
219 }
220
221 pub fn age_function(mut self, new_age: Box<dyn Age>) -> Self {
223 self.age = new_age;
224 self
225 }
226
227 pub fn crossover_function(mut self, new_cross: Box<dyn Crossover<T, Ind>>) -> Self {
229 self.crossover = new_cross;
230 self
231 }
232
233 pub fn population_refitness_function(
235 mut self,
236 new_refit: Box<dyn PopulationRefitness<T, Ind>>,
237 ) -> Self {
238 self.population_refitness = new_refit;
239 self
240 }
241
242 pub fn survival_pressure_function(
244 mut self,
245 new_surv: Box<dyn SurvivalPressure<T, Ind>>,
246 ) -> Self {
247 self.survival_pressure = new_surv;
248 self
249 }
250
251 pub fn stop_criterion(mut self, new_crit: Box<dyn StopCriterion>) -> Self {
253 self.stop_criterion = new_crit;
254 self
255 }
256
257 pub fn cache_fitness(mut self, new_cache: bool) -> Self {
259 self.cache_fitness = new_cache;
260 self
261 }
262
263 pub fn global_cache(mut self, new_global_cache: bool) -> Self {
266 if cfg!(feature = "global_cache") {
267 self.global_cache = new_global_cache;
268 } else if new_global_cache {
269 panic!("global_cache feature must been enabled to enable global_cache");
270 } else {
271 self.global_cache = false;
272 }
273 self
274 }
275
276 pub fn progress_log(mut self, generations: u64, log_file: File) -> Self {
278 self.progress_log = (generations, Some(log_file));
279 self
280 }
281
282 pub fn population_log(mut self, generations: u64, log_file: File) -> Self {
284 self.population_log = (generations, Some(log_file));
285 self
286 }
287
288 pub fn run(mut self) -> (Vec<Ind>, u64, f64, Vec<IndWithFitness<T, Ind>>) {
297 while self.population.len() < self.population_size {
299 self.population.push(IndWithFitness::new(
300 Ind::generate(&self.genotype_size),
301 None,
302 ));
303 }
304 self.fix();
305 self.compute_fitnesses(true);
306
307 if self.progress_log.0 > 0 {
308 self.print_progress_header();
309 }
310
311 let (generation, progress, solutions) = self.run_loop();
312
313 (solutions, generation, progress, self.population)
314 }
315
316 fn run_loop(&mut self) -> (u64, f64, Vec<Ind>) {
317 let mut generation: u64 = 0;
318 let mut last_progresses: Vec<f64> = Vec::new();
319 let mut progress: f64 = std::f64::NAN;
320 let mut solutions: Vec<Ind> = Vec::new();
322 let mut mutation_rate;
323 let mut selection_rate;
324 let mut last_best = 0f64;
325
326 let mut current_fitnesses = self.get_fitnesses();
327 self.get_solutions(&mut solutions);
328 while !self
329 .stop_criterion
330 .stop(generation, progress, solutions.len(), ¤t_fitnesses)
331 {
332 generation += 1;
333
334 mutation_rate =
335 self.mutation_rate
336 .rate(generation, progress, solutions.len(), ¤t_fitnesses);
337 selection_rate =
338 self.selection_rate
339 .rate(generation, progress, solutions.len(), ¤t_fitnesses);
340
341 self.compute_fitnesses(true);
342 self.refitness(generation, progress, solutions.len());
343 current_fitnesses = self.get_fitnesses();
344 let selected = self.selection.select(¤t_fitnesses, selection_rate);
345 let parents_children = self.cross(&selected);
346 self.compute_fitnesses(false);
348 self.get_solutions(&mut solutions);
349 self.mutate(mutation_rate);
350 self.fix();
351 self.compute_fitnesses(false);
352 self.refitness(generation, progress, solutions.len());
353 self.age_unfitness();
354 self.get_solutions(&mut solutions);
356 self.survival_pressure_kill(&parents_children);
357
358 current_fitnesses = self.get_fitnesses();
359 let best = current_fitnesses[0];
360 progress = Self::update_progress(last_best, best, &mut last_progresses);
361 last_best = best;
362
363 if self.progress_log.0 > 0 && generation % self.progress_log.0 == 0 {
364 self.print_progress(generation, progress, &last_progresses, solutions.len());
365 }
366 if self.population_log.0 > 0 && generation % self.population_log.0 == 0 {
367 self.print_population(generation);
368 }
369
370 self.update_age();
371 }
372
373 (generation, progress, solutions)
374 }
375
376 fn print_population(&mut self, generation: u64) {
377 if let Some(ref mut f) = self.population_log.1 {
378 f.write_all(format!("Generation {}\n", generation).as_bytes())
379 .expect(POPULATION_ERR_MSG);
380 for (i, ind) in self.population.iter().enumerate() {
381 f.write_all(
382 format!(
383 "Individual: {}; fitness: {}, age: {}, original_fitness: {}\n",
384 i,
385 ind.fitness.unwrap().fitness,
386 ind.fitness.unwrap().age,
387 ind.fitness.unwrap().original_fitness
388 )
389 .as_bytes(),
390 )
391 .expect(POPULATION_ERR_MSG);
392 f.write_all(format!("{}\n\n", ind.ind).as_bytes())
393 .expect(POPULATION_ERR_MSG);
394 }
395 f.write_all(POPULATION_SEPARATOR).expect(POPULATION_ERR_MSG);
396 }
397 }
398
399 fn print_progress_header(&mut self) {
400 if let Some(ref mut f) = self.progress_log.1 {
401 f.write_all(PROGRESS_HEADER).expect(PROGRESS_ERR_MSG);
402 }
403 }
404
405 fn print_progress(
406 &mut self,
407 generation: u64,
408 progress: f64,
409 last_progresses: &[f64],
410 n_solutions: usize,
411 ) {
412 let current_fitnesses = self.get_fitnesses();
413 if let Some(ref mut f) = self.progress_log.1 {
414 let progress_hist = Histo::default();
415 for prog in last_progresses.iter() {
416 progress_hist.measure(*prog);
417 }
418 let fit_hist = Histo::default();
419 for fit in ¤t_fitnesses {
420 fit_hist.measure(*fit);
421 }
422 let fitness_avg =
423 current_fitnesses.iter().sum::<f64>() / current_fitnesses.len() as f64;
424 f.write_all(
425 format!(
426 "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\n",
427 generation,
428 n_solutions,
429 last_progresses[last_progresses.len() - 1],
430 progress,
431 (1_f64 / (last_progresses.len() - 1) as f64)
432 * last_progresses
433 .iter()
434 .fold(0_f64, |acc, x| acc + (x - progress).powi(2))
435 .sqrt(),
436 last_progresses
437 .iter()
438 .max_by(|x, y| x.partial_cmp(&y).unwrap())
439 .unwrap(),
440 last_progresses
441 .iter()
442 .min_by(|x, y| x.partial_cmp(&y).unwrap())
443 .unwrap(),
444 progress_hist.percentile(10_f64),
445 progress_hist.percentile(25_f64),
446 progress_hist.percentile(50_f64),
447 progress_hist.percentile(75_f64),
448 progress_hist.percentile(90_f64),
449 fitness_avg,
450 (1_f64 / (current_fitnesses.len() - 1) as f64)
451 * current_fitnesses
452 .iter()
453 .fold(0_f64, |acc, x| acc + (x - fitness_avg).powi(2))
454 .sqrt(),
455 current_fitnesses
456 .iter()
457 .max_by(|x, y| x.partial_cmp(&y).unwrap())
458 .unwrap(),
459 current_fitnesses
460 .iter()
461 .min_by(|x, y| x.partial_cmp(&y).unwrap())
462 .unwrap(),
463 fit_hist.percentile(10_f64),
464 fit_hist.percentile(25_f64),
465 fit_hist.percentile(50_f64),
466 fit_hist.percentile(75_f64),
467 fit_hist.percentile(90_f64),
468 ).as_bytes(),
469 ).expect(PROGRESS_ERR_MSG);
470 }
471 }
472
473 fn update_progress(last_best: f64, best: f64, last_progresses: &mut Vec<f64>) -> f64 {
474 last_progresses.push(best - last_best);
475 if last_progresses.len() == 16 {
476 last_progresses.remove(0);
477 }
478
479 last_progresses.par_iter().sum::<f64>() / last_progresses.len() as f64
480 }
481
482 fn fix(&mut self) {
483 self.population.par_iter_mut().for_each(|indwf| {
484 if indwf.ind.fix() {
485 indwf.fitness = None
486 }
487 });
488 }
489
490 fn update_age(&mut self) {
491 self.population.par_iter_mut().for_each(|indwf| {
492 if let Some(mut fit) = indwf.fitness {
493 fit.age += 1;
494 indwf.fitness = Some(fit);
495 }
496 });
497 }
498
499 fn get_fitnesses(&self) -> Vec<f64> {
500 self.population
501 .iter()
502 .map(|indwf| indwf.fitness.unwrap().fitness)
503 .collect::<Vec<f64>>()
504 }
505
506 #[cfg(feature = "global_cache")]
507 fn compute_fitnesses(&mut self, refresh_on_nocache: bool) {
508 if cfg!(feature = "global_cache") && self.cache_fitness && self.global_cache {
509 let (sender, receiver) = channel();
510 self.population
511 .par_iter()
512 .enumerate()
513 .filter(|(_i, indwf)| indwf.fitness.is_none())
514 .for_each_with(sender, |s, (i, indwf)| {
515 let hashed_ind = self.population[i].ind.hash();
516 let new_fit_value = {
517 match self.cache_map.get(&hashed_ind) {
518 Some(val) => *val,
519 None => indwf.ind.fitness(),
520 }
521 };
522 s.send((i, new_fit_value, hashed_ind)).unwrap();
523 });
524 for (i, new_fit_value, hashed_ind) in receiver {
525 self.population[i].fitness = Some(Fitness {
527 age: 0,
528 fitness: new_fit_value,
529 original_fitness: new_fit_value,
530 age_effect: 0.0,
531 refitness_effect: 0.0,
532 });
533 self.cache_map.entry(hashed_ind).or_insert(new_fit_value);
535 }
536 } else {
537 self.compute_fitnesses_without_global_cache(refresh_on_nocache);
538 }
539 }
540
541 #[cfg(not(feature = "global_cache"))]
542 fn compute_fitnesses(&mut self, refresh_on_nocache: bool) {
543 self.compute_fitnesses_without_global_cache(refresh_on_nocache);
544 }
545
546 fn compute_fitnesses_without_global_cache(&mut self, refresh_on_nocache: bool) {
547 self.population
548 .par_iter_mut()
549 .filter(|indwf| {
550 if refresh_on_nocache {
551 true
552 } else {
553 indwf.fitness.is_none()
554 }
555 })
556 .for_each(|indwf| {
557 let new_fit_value = indwf.ind.fitness();
558 match indwf.fitness {
560 Some(fit) => {
561 indwf.fitness = Some(Fitness {
562 fitness: new_fit_value,
563 ..fit
564 })
565 }
566 None => {
567 indwf.fitness = Some(Fitness {
568 age: 0,
569 fitness: new_fit_value,
570 original_fitness: new_fit_value,
571 age_effect: 0.0,
572 refitness_effect: 0.0,
573 })
574 }
575 }
576 });
577 }
578
579 fn age_unfitness(&mut self) {
580 let age_function = &self.age;
581 self.population.par_iter_mut().for_each(|indwf| {
582 if let Some(fit) = indwf.fitness {
583 let age_exceed: i64 = fit.age as i64 - age_function.age_threshold() as i64;
584 if age_exceed >= 0 {
585 let new_fit = age_function.age_unfitness(age_exceed as u64, fit.fitness);
586 indwf.fitness = Some(Fitness {
587 fitness: new_fit,
588 age: fit.age,
589 original_fitness: fit.original_fitness,
590 age_effect: fit.age_effect + (new_fit - fit.fitness),
591 refitness_effect: fit.refitness_effect,
592 });
593 }
594 }
595 });
596 }
597
598 fn get_solutions(&self, solutions: &mut Vec<Ind>) {
599 for indwf in &self.population {
600 if indwf
601 .ind
602 .is_solution(indwf.fitness.unwrap().original_fitness)
603 && Self::not_found_yet_solution(&solutions, &indwf.ind)
604 {
605 solutions.push(indwf.ind.clone());
606 }
607 }
608 }
609
610 #[allow(clippy::never_loop)]
611 fn not_found_yet_solution(solutions: &[Ind], other: &Ind) -> bool {
612 for ind in solutions {
613 if other.distance(ind) == 0.0 {
614 return false;
615 }
616 }
617
618 true
619 }
620
621 fn cross(&mut self, selected: &[usize]) -> Vec<Reproduction> {
622 let reprs_number = (selected.len() + 1) / 2;
623 let mut reprs = Vec::with_capacity(reprs_number);
624 let (sender, receiver) = channel();
625
626 std::ops::Range {
627 start: 0,
628 end: reprs_number,
629 }
630 .into_par_iter()
631 .for_each_with(sender, |s, i| {
632 let ind1 = i * 2;
633 let mut ind2 = ind1 + 1;
634 if ind2 >= selected.len() {
637 ind2 = SmallRng::from_entropy().sample(Uniform::from(0..selected.len()));
638 }
639 let (crossed1, crossed2) = self.crossover.cross(
640 &self.population[selected[ind1]].ind,
641 &self.population[selected[ind2]].ind,
642 );
643 s.send((selected[ind1], selected[ind2], crossed1, crossed2))
644 .unwrap();
645 });
646 for (parent1, parent2, child1, child2) in receiver {
647 self.population.push(IndWithFitness {
648 ind: child1,
649 fitness: None,
650 _phantom: PhantomData,
651 });
652 self.population.push(IndWithFitness {
653 ind: child2,
654 fitness: None,
655 _phantom: PhantomData,
656 });
657 reprs.push(Reproduction {
658 parents: (parent1, parent2),
659 children: (self.population.len() - 2, self.population.len() - 1),
660 });
661 }
662 reprs
663 }
664
665 fn mutate(&mut self, mutation_rate: f64) {
666 self.population.par_iter_mut().for_each(|indwf| {
667 let mut rgen = SmallRng::from_entropy();
668 for gen in 0..indwf.ind.iter().len() {
669 let random: f64 = rgen.sample(Standard);
670 if random < mutation_rate {
671 indwf.ind.mutate(&mut rgen, gen);
672 indwf.fitness = None;
673 }
674 }
675 });
676 }
677
678 fn refitness(&mut self, generation: u64, progress: f64, n_solutions: usize) {
679 let (sender, receiver) = channel();
680
681 self.population
682 .par_iter()
683 .enumerate()
684 .for_each_with(sender, |s, (i, indwf)| {
685 if let Some(fit) = indwf.fitness {
686 let new_fit = self.population_refitness.population_refitness(
687 i,
688 &self.population,
689 generation,
690 progress,
691 n_solutions,
692 );
693 if (new_fit - fit.fitness).abs() > 0.000_001 {
694 s.send((
695 i,
696 Some(Fitness {
697 fitness: new_fit,
698 age: fit.age,
699 original_fitness: fit.original_fitness,
700 age_effect: fit.age_effect,
701 refitness_effect: new_fit - fit.fitness,
702 }),
703 ))
704 .unwrap()
705 }
706 }
707 });
708 for (i, fit) in receiver {
709 self.population[i].fitness = fit;
710 }
711 }
712
713 fn survival_pressure_kill(&mut self, parents_children: &[Reproduction]) {
714 self.survival_pressure.kill(
715 self.population_size,
716 &mut self.population,
717 &parents_children,
718 );
719 }
720}