scirs2_optimize/multi_objective/selection/
mod.rs1use crate::multi_objective::solutions::Solution;
6use scirs2_core::random::seq::SliceRandom;
7use scirs2_core::random::Rng;
8
9pub trait SelectionOperator {
11 fn select(&self, population: &[Solution], n_select: usize) -> Vec<Solution>;
13}
14
15#[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 if sol1.rank < sol2.rank {
36 sol1.clone()
37 } else if sol2.rank < sol1.rank {
38 sol2.clone()
39 } else {
40 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#[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#[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 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#[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}