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}