scirs2_optimize/multi_objective/selection/
mod.rs

1//! Selection operators for multi-objective optimization
2//!
3//! Methods for selecting individuals from populations.
4
5use crate::multi_objective::solutions::Solution;
6use rand::seq::SliceRandom;
7use rand::Rng;
8
9/// Trait for selection operators
10pub trait SelectionOperator {
11    /// Select individuals from population
12    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution>;
13}
14
15/// Tournament selection
16#[derive(Debug, Clone)]
17pub struct TournamentSelection {
18    tournament_size: usize,
19}
20
21impl TournamentSelection {
22    pub fn new(tournament_size: usize) -> Self {
23        Self { tournament_size }
24    }
25
26    fn binary_tournament(&self, population: &[Solution]) -> Solution {
27        let mut rng = rand::rng();
28        let idx1 = rng.gen_range(0..population.len());
29        let idx2 = rng.gen_range(0..population.len());
30
31        let sol1 = &population[idx1];
32        let sol2 = &population[idx2];
33
34        // Prefer lower rank (better solutions)
35        if sol1.rank < sol2.rank {
36            sol1.clone()
37        } else if sol2.rank < sol1.rank {
38            sol2.clone()
39        } else {
40            // Same rank, prefer higher crowding distance
41            if sol1.crowding_distance > sol2.crowding_distance {
42                sol1.clone()
43            } else {
44                sol2.clone()
45            }
46        }
47    }
48}
49
50impl SelectionOperator for TournamentSelection {
51    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
52        let mut selected = Vec::with_capacity(n_select);
53
54        for _ in 0..n_select {
55            selected.push(self.binary_tournament(population));
56        }
57
58        selected
59    }
60}
61
62/// Random selection
63#[derive(Debug, Clone)]
64pub struct RandomSelection;
65
66impl RandomSelection {
67    pub fn new() -> Self {
68        Self
69    }
70}
71
72impl SelectionOperator for RandomSelection {
73    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
74        let mut rng = rand::rng();
75        let mut selected = Vec::with_capacity(n_select);
76        for _ in 0..n_select.min(population.len()) {
77            let idx = rng.gen_range(0..population.len());
78            selected.push(population[idx].clone());
79        }
80        selected
81    }
82}
83
84/// Roulette wheel selection based on fitness
85#[derive(Debug, Clone)]
86pub struct RouletteWheelSelection;
87
88impl RouletteWheelSelection {
89    pub fn new() -> Self {
90        Self
91    }
92}
93
94impl SelectionOperator for RouletteWheelSelection {
95    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
96        let mut rng = rand::rng();
97        let mut selected = Vec::with_capacity(n_select);
98
99        // Calculate fitness scores (inverse of rank for maximization)
100        let max_rank = population.iter().map(|s| s.rank).max().unwrap_or(1);
101        let fitness_scores: Vec<f64> = population
102            .iter()
103            .map(|s| (max_rank + 1 - s.rank) as f64)
104            .collect();
105
106        let total_fitness: f64 = fitness_scores.iter().sum();
107
108        for _ in 0..n_select {
109            let mut accumulator = 0.0;
110            let random_value = rng.random::<f64>() * total_fitness;
111
112            for (i, &fitness) in fitness_scores.iter().enumerate() {
113                accumulator += fitness;
114                if accumulator >= random_value {
115                    selected.push(population[i].clone());
116                    break;
117                }
118            }
119        }
120
121        selected
122    }
123}
124
125/// Truncation selection - select best individuals
126#[derive(Debug, Clone)]
127pub struct TruncationSelection;
128
129impl TruncationSelection {
130    pub fn new() -> Self {
131        Self
132    }
133}
134
135impl SelectionOperator for TruncationSelection {
136    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
137        let mut sorted = population.to_vec();
138        sorted.sort_by(|a, b| {
139            a.rank.cmp(&b.rank).then(
140                b.crowding_distance
141                    .partial_cmp(&a.crowding_distance)
142                    .unwrap(),
143            )
144        });
145
146        sorted.into_iter().take(n_select).collect()
147    }
148}