wafrift_evolution/evolution/crossover/
selection.rs1use crate::evolution::Chromosome;
2use rand::Rng;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum SelectionStrategy {
7 Standard,
8 Adaptive,
9 Roulette,
10}
11
12#[must_use]
14pub fn tournament_select<'a>(population: &'a [Chromosome], rng: &mut impl Rng) -> &'a Chromosome {
15 let tournament_size = 3_usize.min(population.len());
16 tournament_select_with_size(population, tournament_size, rng)
17}
18
19#[must_use]
27pub fn tournament_select_with_size<'a>(
28 population: &'a [Chromosome],
29 tournament_size: usize,
30 rng: &mut impl Rng,
31) -> &'a Chromosome {
32 assert!(
33 !population.is_empty(),
34 "tournament_select_with_size called with empty population — caller bug"
35 );
36 let size = tournament_size.min(population.len());
37 let mut best_idx = rng.gen_range(0..population.len());
38 for _ in 1..size {
39 let candidate_idx = rng.gen_range(0..population.len());
40 if population[candidate_idx].fitness > population[best_idx].fitness {
41 best_idx = candidate_idx;
42 }
43 }
44 &population[best_idx]
45}
46
47#[must_use]
52pub fn roulette_select<'a>(population: &'a [Chromosome], rng: &mut impl Rng) -> &'a Chromosome {
53 assert!(
54 !population.is_empty(),
55 "roulette_select called with empty population — caller bug"
56 );
57 if population.len() == 1 {
58 return &population[0];
59 }
60 let total_fitness: f64 = population.iter().map(|c| c.fitness.max(0.0)).sum();
61 if total_fitness <= 0.0 {
62 return &population[rng.gen_range(0..population.len())];
63 }
64 let mut spin = rng.gen_range(0.0..total_fitness);
65 for chromosome in population {
66 spin -= chromosome.fitness.max(0.0);
67 if spin <= 0.0 {
68 return chromosome;
69 }
70 }
71 &population[population.len() - 1]
72}
73
74#[must_use]
76pub fn adaptive_select<'a>(
77 population: &'a [Chromosome],
78 diversity: f64,
79 rng: &mut impl Rng,
80) -> &'a Chromosome {
81 let base_size = 3_usize;
82 let max_size = (population.len() / 3).max(base_size);
83 let adjusted_size = base_size + ((max_size - base_size) as f64 * (1.0 - diversity)) as usize;
84 tournament_select_with_size(population, adjusted_size, rng)
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use rand::{SeedableRng, rngs::StdRng};
91
92 #[test]
93 fn tournament_select_single_chromosome_returns_it() {
94 let population = vec![Chromosome::new(vec![("encoding".into(), "None".into())])];
95 let mut rng = StdRng::seed_from_u64(29);
96 let selected = tournament_select(&population, &mut rng);
97 assert_eq!(selected.genes, population[0].genes);
98 }
99}