oxigen/
lib.rs

1//! This crate provides functions for parallel genetic algorithm execution.
2
3// #![feature(test)]
4
5extern crate historian;
6extern crate rand;
7extern crate rayon;
8
9pub mod age;
10// mod benchmarks;
11pub 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/// Struct that defines the fitness of each individual and the related information.
73#[derive(Copy, Clone, Debug)]
74pub struct Fitness {
75    /// Age of the individual.
76    age: u64,
77    /// Actual fitness.
78    fitness: f64,
79    /// Original fitness of the individual before being unfitnessed by age.
80    original_fitness: f64,
81    /// Age effect over the original fitness (usually negative).
82    age_effect: f64,
83    /// Refitness effect over the original fitness (usually negative).
84    refitness_effect: f64,
85}
86
87/// Struct that defines a pair of individual-fitness
88#[derive(Debug)]
89pub struct IndWithFitness<T: PartialEq + Send + Sync, Ind: Genotype<T>> {
90    /// Individual
91    pub ind: Ind,
92    /// Fitness (can be not computed yet)
93    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
113/// Struct that defines a genetic algorithm execution.
114pub struct GeneticExecution<T: PartialEq + Send + Sync, Ind: Genotype<T>> {
115    /// The number of individuals in the population.
116    population_size: usize,
117    /// Population with all individuals and their respective fitnesses.
118    population: Vec<IndWithFitness<T, Ind>>,
119    /// Size of the genotype problem.
120    genotype_size: Ind::ProblemSize,
121    /// The mutation rate variation along iterations and progress.
122    mutation_rate: Box<dyn MutationRate>,
123    /// The number of stages in the cup whose individuals are selected to crossover.
124    selection_rate: Box<dyn SelectionRate>,
125    /// The selection function.
126    selection: Box<dyn Selection>,
127    /// The age fitness decrease function.
128    age: Box<dyn Age>,
129    /// The crossover function.
130    crossover: Box<dyn Crossover<T, Ind>>,
131    /// The function used to recompute the fitness using the full population just
132    /// before survival pressure.
133    population_refitness: Box<dyn PopulationRefitness<T, Ind>>,
134    /// The function used to replace individuals in the population.
135    survival_pressure: Box<dyn SurvivalPressure<T, Ind>>,
136    /// The stop criterion to finish execution.
137    stop_criterion: Box<dyn StopCriterion>,
138    /// Cache fitness value of individuals or compute it in each iteration.
139    cache_fitness: bool,
140    /// Global cache for all evaluated individuals, i.e., if a new individual is
141    /// equal to a previous individual its fitness is not recomputed. Only valid
142    /// if cache_fitness is true.
143    global_cache: bool,
144    /// HashMap used for the global cache
145    #[cfg(feature = "global_cache")]
146    cache_map: HashMap<Ind::GenotypeHash, f64>,
147    /// Progress log, writes statistics of the population every certain number of generations.
148    progress_log: (u64, Option<File>),
149    /// Population log, writes the population and fitnesses every certain number of generations.
150    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    /// Creates a new default genetic algorithm execution.
179    pub fn new() -> Self {
180        Self::default()
181    }
182
183    /// Sets the population size.
184    pub fn population_size(mut self, new_pop_size: usize) -> Self {
185        self.population_size = new_pop_size;
186        self
187    }
188
189    /// Sets the initial population individuals. If lower individuals
190    /// than population_size are received, the rest of population will be
191    /// generated randomly.
192    pub fn population(mut self, new_pop: Vec<IndWithFitness<T, Ind>>) -> Self {
193        self.population = new_pop;
194        self
195    }
196
197    /// Sets the genotype size.
198    pub fn genotype_size(mut self, new_size: Ind::ProblemSize) -> Self {
199        self.genotype_size = new_size;
200        self
201    }
202
203    /// Sets the mutation rate.
204    pub fn mutation_rate(mut self, new_mut: Box<dyn MutationRate>) -> Self {
205        self.mutation_rate = new_mut;
206        self
207    }
208
209    /// Sets the number of tournament stages whose individuals are selected for crossover.
210    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    /// Sets the selection function of the genetic algorithm.
216    pub fn select_function(mut self, new_sel: Box<dyn Selection>) -> Self {
217        self.selection = new_sel;
218        self
219    }
220
221    /// Sets the age function of the genetic algorithm.
222    pub fn age_function(mut self, new_age: Box<dyn Age>) -> Self {
223        self.age = new_age;
224        self
225    }
226
227    /// Sets the crossover function of the genetic algorithm.
228    pub fn crossover_function(mut self, new_cross: Box<dyn Crossover<T, Ind>>) -> Self {
229        self.crossover = new_cross;
230        self
231    }
232
233    /// Sets the population refitness function of the genetic algorithm.
234    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    /// Sets the survival pressure function of the genetic algorithm.
243    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    /// Sets the stop criterion of the genetic algorithm.
252    pub fn stop_criterion(mut self, new_crit: Box<dyn StopCriterion>) -> Self {
253        self.stop_criterion = new_crit;
254        self
255    }
256
257    /// Sets the cache fitness flag.
258    pub fn cache_fitness(mut self, new_cache: bool) -> Self {
259        self.cache_fitness = new_cache;
260        self
261    }
262
263    /// Sets the global cache flag.
264    /// Panics: when trying to put it true without `global_cache` feature enabled.
265    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    /// Sets the progress log.
277    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    /// Sets the progress log.
283    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    /// Run the genetic algorithm executiion until the `stop_criterion` is satisfied.
289    ///
290    /// # Returns
291    ///
292    /// - A vector with the individuals of the population that are solution of the problem.
293    /// - The number of generations run.
294    /// - The average progress in the last generations.
295    /// - The entire population in the last generation (useful for resuming the execution).
296    pub fn run(mut self) -> (Vec<Ind>, u64, f64, Vec<IndWithFitness<T, Ind>>) {
297        // Initialize randomly the population
298        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        // A HashSet is not used to not force to implement Hash in Genotype
321        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(), &current_fitnesses)
331        {
332            generation += 1;
333
334            mutation_rate =
335                self.mutation_rate
336                    .rate(generation, progress, solutions.len(), &current_fitnesses);
337            selection_rate =
338                self.selection_rate
339                    .rate(generation, progress, solutions.len(), &current_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(&current_fitnesses, selection_rate);
345            let parents_children = self.cross(&selected);
346            // fitnesses must be computed for the new individuals to get solutions
347            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            // get solutions before kill
355            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 &current_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                // only none fitness individuals are sent to the receiver
526                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                // insert in cache if it is not already in it
534                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                // this match is only to keep the age
559                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 the number of selected individuals is odd, the last crossover is done
635            // using a random one among the selected individuals
636            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}