genevo/selection/
truncation.rs

1//! The `truncation` module provides selection methods that are merely based
2//! on the fitness values of the individuals but don't have any stochastic
3//! component.
4//!
5//! The provided `SelectionOp` implementations are:
6//! * `MaximizeSelector`
7
8use crate::{
9    algorithm::EvaluatedPopulation,
10    genetic::{Fitness, Genotype, Parents},
11    operator::{GeneticOperator, MultiObjective, SelectionOp, SingleObjective},
12    random::Rng,
13};
14
15/// The `MaximizeSelector` selects the best performing `genetic::Genotype`s
16/// from the population.
17///
18/// This `MaximizeSelector` can be used for single-objective fitness values
19/// as well as multi-objective fitness values.
20#[allow(missing_copy_implementations)]
21#[derive(Clone, Debug, PartialEq)]
22pub struct MaximizeSelector {
23    /// The truncation threshold is the ratio between the number of parents
24    /// to be selected and the size of the population:
25    /// threshold = number of parents / size of population
26    selection_ratio: f64,
27    /// The number of individuals per parents.
28    num_individuals_per_parents: usize,
29    // phantom types
30}
31
32impl MaximizeSelector {
33    /// Constructs a new instance of the `MaximizeSelector`.
34    pub fn new(selection_ratio: f64, num_individuals_per_parents: usize) -> Self {
35        MaximizeSelector {
36            selection_ratio,
37            num_individuals_per_parents,
38        }
39    }
40
41    /// Returns the selection ratio.
42    ///
43    /// The selection ratio is the fraction of number of parents that are
44    /// selected on every call of the `selection` function and the number
45    /// of individuals in the population.
46    pub fn selection_ratio(&self) -> f64 {
47        self.selection_ratio
48    }
49
50    /// Sets the selection ratio to a new value.
51    ///
52    /// The selection ratio is the fraction of number of parents that are
53    /// selected on every call of the `selection` function and the number
54    /// of individuals in the population.
55    pub fn set_selection_ratio(&mut self, value: f64) {
56        self.selection_ratio = value;
57    }
58
59    /// Returns the number of individuals per parents use by this selector.
60    pub fn num_individuals_per_parents(&self) -> usize {
61        self.num_individuals_per_parents
62    }
63
64    /// Sets the number of individuals per parents to the given value.
65    pub fn set_num_individuals_per_parents(&mut self, value: usize) {
66        self.num_individuals_per_parents = value;
67    }
68}
69
70/// Can be used for single-objective optimization
71impl SingleObjective for MaximizeSelector {}
72/// Can be used for multi-objective optimization
73impl MultiObjective for MaximizeSelector {}
74
75impl GeneticOperator for MaximizeSelector {
76    fn name() -> String {
77        "Maximizing-Truncation-Selection".to_string()
78    }
79}
80
81impl<G, F> SelectionOp<G, F> for MaximizeSelector
82where
83    G: Genotype,
84    F: Fitness,
85{
86    fn select_from<R>(&self, evaluated: &EvaluatedPopulation<G, F>, _: &mut R) -> Vec<Parents<G>>
87    where
88        R: Rng + Sized,
89    {
90        let individuals = evaluated.individuals();
91        let fitness_values = evaluated.fitness_values();
92
93        // mating pool holds indices to the individuals and fitness_values slices
94        let mut mating_pool: Vec<usize> = (0..fitness_values.len()).collect();
95        // sort mating pool from best performing to worst performing index
96        mating_pool.sort_by(|x, y| fitness_values[*y].cmp(&fitness_values[*x]));
97        let mating_pool = mating_pool;
98
99        let num_parents_to_select =
100            (individuals.len() as f64 * self.selection_ratio + 0.5).floor() as usize;
101        let pool_size = mating_pool.len();
102        let mut selected: Vec<Parents<G>> = Vec::with_capacity(num_parents_to_select);
103
104        let mut index_m = 0;
105        for _ in 0..num_parents_to_select {
106            let mut tuple = Vec::with_capacity(self.num_individuals_per_parents);
107            for _ in 0..self.num_individuals_per_parents {
108                // index into mating pool
109                index_m %= pool_size;
110                // index into individuals slice
111                let index_i = mating_pool[index_m];
112                tuple.push(individuals[index_i].clone());
113                index_m += 1;
114            }
115            selected.push(tuple);
116        }
117        selected
118    }
119}