1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::engines::genome::genes::gene::Gene;
use crate::engines::genome::population::Population;
use rand::Rng;

pub trait Select<G: Gene<G, A>, A> {
    fn select(&self, population: &Population<G, A>, count: usize) -> Population<G, A>;
}

#[allow(dead_code)]
pub enum Selector {
    Tournament(u8),
    Roulette,
    Rank,
    Elitism,
    Boltzmann(f32),
}

impl Selector {
    pub fn total_fitness<G: Gene<G, A>, A>(&self, population: &Population<G, A>) -> f32 {
        population
            .iter()
            .map(|i| match i.score() {
                Some(score) => score.as_float(),
                None => 0.0,
            })
            .sum::<f32>()
    }
}

impl<G: Gene<G, A>, A> Select<G, A> for Selector {
    fn select(&self, population: &Population<G, A>, count: usize) -> Population<G, A> {
        match self {
            Selector::Tournament(size) => {
                let mut rng = rand::thread_rng();
                let mut selected = Vec::with_capacity(count);

                for _ in 0..count {
                    let mut tournament = Vec::with_capacity(*size as usize);
                    for _ in 0..*size {
                        let idx = rng.gen_range(0..population.len());
                        tournament.push(idx);
                    }

                    tournament.sort();

                    selected.push(population.get(tournament[0]).clone());
                }

                Population::from_vec(selected)
            }
            Selector::Roulette => {
                let mut selected = Vec::with_capacity(count);
                let mut rng = rand::thread_rng();
                let total_fitness = self.total_fitness(population);

                for _ in 0..count {
                    let mut idx = rng.gen_range(0.0..total_fitness);

                    for individual in population.iter() {
                        idx -= match individual.score() {
                            Some(score) => score.as_float(),
                            None => 0.0,
                        };

                        if idx <= 0.0 {
                            selected.push(individual.clone());
                            break;
                        }
                    }
                }

                Population::from_vec(selected)
            }
            Selector::Rank => {
                let mut selected = Vec::with_capacity(count);
                let mut rng = rand::thread_rng();

                let total_rank = (population.len() * (population.len() + 1)) as f32 / 2.0;

                for _ in 0..count {
                    let mut idx = rng.gen_range(0.0..total_rank);
                    let mut selected_idx = 0;
                    for individual in population.iter() {
                        idx -= (population.len() - selected_idx) as f32;
                        if idx <= 0.0 {
                            selected.push(individual.clone());
                            break;
                        }
                        selected_idx += 1;
                    }
                }

                Population::from_vec(selected)
            }
            Selector::Elitism => population
                .iter()
                .take(count)
                .map(|individual| individual.clone())
                .collect::<Population<G, A>>(),
            Selector::Boltzmann(temperature) => {
                let mut selected = Vec::with_capacity(count);
                let mut rng = rand::thread_rng();

                let total_fitness = self.total_fitness(population);

                for _ in 0..count {
                    let mut idx = rng.gen_range(0.0..total_fitness);
                    for individual in population.iter() {
                        let fitness = match individual.score() {
                            Some(score) => score.as_float(),
                            None => 0.0,
                        };
                        let probability = (fitness / temperature).exp();
                        idx -= probability;
                        if idx <= 0.0 {
                            selected.push(individual.clone());
                            break;
                        }
                    }
                }

                Population::from_vec(selected)
            }
        }
    }
}