radiate_core/
selector.rs

1use crate::Chromosome;
2use crate::genome::population::Population;
3use crate::objectives::Objective;
4
5use super::random_provider;
6
7/// A trait for selection algorithms. Selection algorithms are used to select
8/// individuals from a population to be used in the next generation. The
9/// selection process is (most of the time) based on the fitness of the individuals in the
10/// population. The selection process can be based on the fitness of the individuals
11/// in the population, or it can be based on the individuals themselves.
12///
13pub trait Select<C: Chromosome>: Send + Sync {
14    fn name(&self) -> &'static str {
15        std::any::type_name::<Self>()
16            .split("<")
17            .next()
18            .unwrap_or(std::any::type_name::<Self>())
19            .split("::")
20            .last()
21            .unwrap_or("Unknown Selector")
22    }
23
24    fn select(
25        &self,
26        population: &Population<C>,
27        optimize: &Objective,
28        count: usize,
29    ) -> Population<C>;
30}
31
32/// An iterator that generates random indices based on probabilities.
33/// This iterator is used in the RouletteWheel selection algorithm, and
34/// Boltzmann selection algorithm. This is essentially the 'roulette wheel'
35/// that is spun to select individuals from the population. The probability
36/// of selecting an individual is based on the fitness (probability) of the individual.
37/// The higher the fitness, the higher the probability of the individual being selected.
38pub struct ProbabilityWheelIterator<'a> {
39    probabilities: &'a [f32],
40    total: f32,
41    max_index: usize,
42    current: usize,
43}
44
45impl<'a> ProbabilityWheelIterator<'a> {
46    pub fn new(probabilities: &'a [f32], max_index: usize) -> Self {
47        let total = probabilities.iter().sum();
48        Self {
49            probabilities,
50            total,
51            max_index,
52            current: 0,
53        }
54    }
55}
56
57impl Iterator for ProbabilityWheelIterator<'_> {
58    type Item = usize;
59
60    fn next(&mut self) -> Option<Self::Item> {
61        // In `Radiate` there is a selector for surviving individuals (members who will be selected
62        // to be passed on to the next generation without any changes)
63        // and a selector for selecting individuals to be used in the
64        // next generation. Because of this, we don't select all the individuals
65        // in the population, we only select a certain number of individuals.
66        // If we have selected all the individuals that this selector is supposed to select, we return None.
67        if self.current >= self.max_index {
68            return None;
69        }
70
71        let mut value = random_provider::random::<f32>() * self.total;
72        let mut index = 0;
73
74        // We iterate over the probabilities of the individuals in the population - the 'wheel'
75        for (i, &prob) in self.probabilities.iter().enumerate() {
76            value -= prob;
77            if value <= 0.0 {
78                index = i;
79                break;
80            }
81        }
82
83        self.current += 1;
84        Some(index)
85    }
86}