~ruther/ctu-fee-eoa

ref: e366ce2ed235017732d88aac26694736eea23cdb ctu-fee-eoa/codes/eoa_lib/src/selection.rs -rw-r--r-- 2.5 KiB
e366ce2e — Rutherther chore: make library out of eoa_lib a month ago
                                                                                
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
use rand::{seq::IteratorRandom, Rng, RngCore};

use crate::{comparison::BetterThanOperator, replacement::EvaluatedPopulation};

pub trait Selection<TChromosome, TResult> {
    fn select(&mut self,
              count: usize,
              evaluations: &EvaluatedPopulation<TChromosome, TResult>,
              better_than: &dyn BetterThanOperator<TResult>
    ) -> impl Iterator<Item = usize>;
}

pub struct TournamentSelection {
    rng: Box<dyn RngCore>,
    p: f64,
    k: usize
}

impl TournamentSelection {
    pub fn new(k: usize, p: f64) -> Self {
        assert!(0.0 <= p && p <= 1.0);
        assert!(k > 0);

        Self {
            rng: Box::new(rand::rng()),
            p,
            k
        }
    }

    fn tournament<TChromosome, TResult>(
        &mut self,
        idxs: &mut Vec<usize>,
        evaluations: &EvaluatedPopulation<TChromosome, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
    ) -> usize {
        idxs.sort_by(|&i, &j| better_than.ordering(
            &evaluations.population[i].evaluation,
            &evaluations.population[j].evaluation)
        );

        let mut p_selector = self.rng.random_range(0.0..=1.0f64);
        let p = self.p;
        let k = self.k;

        let mut selected = idxs[k - 1];
        // let's say p = 0.7
        // the best has probability 0.7 of being selected
        // if the best is not selected, the second has 0.7 probability of being selected... (that's 0.7 * 0.3 without conditions)
        // and so on. The last element is selected if none of the previous were.
        for i in 0..k-1 {
            if p_selector <= p {
                selected = i;
                break;
            }

            p_selector -= p;
            // 'Expand' the rest to '100%' again
            p_selector /= 1.0 - p;
        }

        selected
    }
}

impl<TChromosome, TResult> Selection<TChromosome, TResult> for TournamentSelection {
    fn select(
        &mut self,
        count: usize,
        evaluations: &EvaluatedPopulation<TChromosome, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
    ) -> impl Iterator<Item = usize> {
        // Let's reuse a single vector for the indices
        let mut k_selected_idxs = vec![0; self.k];
        (0..count).map(move |_| {
            // Choose k. Do not care if already selected previously.
            (0..evaluations.population.len()).choose_multiple_fill(&mut self.rng, &mut k_selected_idxs);
            // Tournament between the k
            self.tournament(&mut k_selected_idxs, evaluations, better_than)
        })
    }
}