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 scirs2_core::random::seq::SliceRandom;
7use scirs2_core::random::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 = scirs2_core::random::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 Default for RandomSelection {
67    fn default() -> Self {
68        Self::new()
69    }
70}
71
72impl RandomSelection {
73    pub fn new() -> Self {
74        Self
75    }
76}
77
78impl SelectionOperator for RandomSelection {
79    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
80        let mut rng = scirs2_core::random::rng();
81        let mut selected = Vec::with_capacity(n_select);
82        for _ in 0..n_select.min(population.len()) {
83            let idx = rng.gen_range(0..population.len());
84            selected.push(population[idx].clone());
85        }
86        selected
87    }
88}
89
90/// Roulette wheel selection based on fitness
91#[derive(Debug, Clone)]
92pub struct RouletteWheelSelection;
93
94impl Default for RouletteWheelSelection {
95    fn default() -> Self {
96        Self::new()
97    }
98}
99
100impl RouletteWheelSelection {
101    pub fn new() -> Self {
102        Self
103    }
104}
105
106impl SelectionOperator for RouletteWheelSelection {
107    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
108        let mut rng = scirs2_core::random::rng();
109        let mut selected = Vec::with_capacity(n_select);
110
111        // Calculate fitness scores (inverse of rank for maximization)
112        let max_rank = population.iter().map(|s| s.rank).max().unwrap_or(1);
113        let fitness_scores: Vec<f64> = population
114            .iter()
115            .map(|s| (max_rank + 1 - s.rank) as f64)
116            .collect();
117
118        let total_fitness: f64 = fitness_scores.iter().sum();
119
120        for _ in 0..n_select {
121            let mut accumulator = 0.0;
122            let random_value = rng.random::<f64>() * total_fitness;
123
124            for (i, &fitness) in fitness_scores.iter().enumerate() {
125                accumulator += fitness;
126                if accumulator >= random_value {
127                    selected.push(population[i].clone());
128                    break;
129                }
130            }
131        }
132
133        selected
134    }
135}
136
137/// Truncation selection - select best individuals
138#[derive(Debug, Clone)]
139pub struct TruncationSelection;
140
141impl Default for TruncationSelection {
142    fn default() -> Self {
143        Self::new()
144    }
145}
146
147impl TruncationSelection {
148    pub fn new() -> Self {
149        Self
150    }
151}
152
153impl SelectionOperator for TruncationSelection {
154    fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution> {
155        let mut sorted = population.to_vec();
156        sorted.sort_by(|a, b| {
157            a.rank.cmp(&b.rank).then(
158                b.crowding_distance
159                    .partial_cmp(&a.crowding_distance)
160                    .unwrap(),
161            )
162        });
163
164        sorted.into_iter().take(n_select).collect()
165    }
166}