Skip to main content

wafrift_evolution/evolution/crossover/
selection.rs

1use crate::evolution::Chromosome;
2use rand::Rng;
3
4/// Selection strategy for tournament selection.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum SelectionStrategy {
7    Standard,
8    Adaptive,
9    Roulette,
10}
11
12/// Tournament selection with configurable tournament size.
13#[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/// Tournament selection with explicit tournament size.
20#[must_use]
21pub fn tournament_select_with_size<'a>(
22    population: &'a [Chromosome],
23    tournament_size: usize,
24    rng: &mut impl Rng,
25) -> &'a Chromosome {
26    let size = tournament_size.min(population.len());
27    let mut best_idx = rng.gen_range(0..population.len());
28    for _ in 1..size {
29        let candidate_idx = rng.gen_range(0..population.len());
30        if population[candidate_idx].fitness > population[best_idx].fitness {
31            best_idx = candidate_idx;
32        }
33    }
34    &population[best_idx]
35}
36
37/// Roulette wheel selection (fitness proportionate selection).
38#[must_use]
39pub fn roulette_select<'a>(population: &'a [Chromosome], rng: &mut impl Rng) -> &'a Chromosome {
40    if population.len() <= 1 {
41        return &population[0];
42    }
43    let total_fitness: f64 = population.iter().map(|c| c.fitness.max(0.0)).sum();
44    if total_fitness <= 0.0 {
45        return &population[rng.gen_range(0..population.len())];
46    }
47    let mut spin = rng.gen_range(0.0..total_fitness);
48    for chromosome in population {
49        spin -= chromosome.fitness.max(0.0);
50        if spin <= 0.0 {
51            return chromosome;
52        }
53    }
54    &population[population.len() - 1]
55}
56
57/// Adaptive selection that adjusts tournament size based on population diversity.
58#[must_use]
59pub fn adaptive_select<'a>(
60    population: &'a [Chromosome],
61    diversity: f64,
62    rng: &mut impl Rng,
63) -> &'a Chromosome {
64    let base_size = 3_usize;
65    let max_size = (population.len() / 3).max(base_size);
66    let adjusted_size = base_size + ((max_size - base_size) as f64 * (1.0 - diversity)) as usize;
67    tournament_select_with_size(population, adjusted_size, rng)
68}