rust_monster/ga/
ga_selectors.rs1use super::ga_core::GASolution;
29use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
30use super::ga_random::{GARandomCtx};
31use std::cmp;
32
33pub trait GASelector<'a, T: GASolution>
38{
39 fn update(&mut self, population: &mut GAPopulation<T>) {}
43
44 fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T;
49}
50
51pub trait GAScoreTypeBasedSelection<T: GASolution>
62{
63 fn score(&self, individual: &T) -> f32;
64
65 fn population_sort_basis(&self) -> GAPopulationSortBasis;
66
67 fn max_score(&self, population: &GAPopulation<T>) -> f32;
68
69 fn min_score(&self, population: &GAPopulation<T>) -> f32;
70}
71
72pub struct GARawScoreBasedSelection;
74
75impl<T: GASolution> GAScoreTypeBasedSelection<T> for GARawScoreBasedSelection
76{
77 fn score(&self, individual: &T) -> f32
78 {
79 individual.score()
80 }
81
82 fn population_sort_basis(&self) -> GAPopulationSortBasis
83 {
84 GAPopulationSortBasis::Raw
85 }
86
87 fn max_score(&self, population: &GAPopulation<T>) -> f32
88 {
89 self.score(population.best_by_raw_score())
90 }
91
92 fn min_score(&self, population: &GAPopulation<T>) -> f32
93 {
94 self.score(population.worst_by_raw_score())
95 }
96}
97
98pub struct GAScaledScoreBasedSelection;
100
101impl<T: GASolution> GAScoreTypeBasedSelection<T> for GAScaledScoreBasedSelection
102{
103 fn score(&self, individual: &T) -> f32
104 {
105 individual.fitness()
106 }
107
108 fn population_sort_basis(&self) -> GAPopulationSortBasis
109 {
110 GAPopulationSortBasis::Scaled
111 }
112
113 fn max_score(&self, population: &GAPopulation<T>) -> f32
114 {
115 self.score(population.best_by_scaled_score())
116 }
117
118 fn min_score(&self, population: &GAPopulation<T>) -> f32
119 {
120 self.score(population.worst_by_scaled_score())
121 }
122}
123
124pub struct GARankSelector<'a, T: 'a + GASolution>
129{
130 score_selection: &'a GAScoreTypeBasedSelection<T>
131}
132
133impl<'a, T: GASolution> GARankSelector<'a, T>
134{
135 pub fn new(s: &'a GAScoreTypeBasedSelection<T>) -> GARankSelector<'a, T>
136 {
137 GARankSelector
138 {
139 score_selection: s
140 }
141 }
142}
143
144impl<'a, T: GASolution> GASelector<'a, T> for GARankSelector<'a, T>
145{
146 fn update(&mut self, population: &mut GAPopulation<T>)
147 {
148 population.sort();
149 }
150
151 fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
152 {
153 let mut best_count = 1;
156
157 let population_sort_basis = self.score_selection.population_sort_basis();
159
160 let best_score: f32 = self.score_selection.max_score(population);
162
163 for i in 1..population.size()
165 {
166 if self.score_selection.score(population.individual(i, population_sort_basis)) != best_score
167 {
168 break;
169 }
170
171 best_count = best_count + 1;
172 }
173
174 population.individual(rng_ctx.gen_range(0, best_count), population_sort_basis)
176 }
177}
178
179pub struct GAUniformSelector;
180
181impl GAUniformSelector
185{
186 pub fn new() -> GAUniformSelector
187 {
188 GAUniformSelector
189 }
190}
191
192impl<'a, T: GASolution> GASelector<'a, T> for GAUniformSelector
193{
194 fn update(&mut self, population: &mut GAPopulation<T>)
195 {
196 population.sort();
199 }
200
201 fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
203 {
204 population.individual(
207 rng_ctx.gen_range(0, population.size()),
208 GAPopulationSortBasis::Raw)
209 }
210}
211
212pub struct GARouletteWheelSelector<'a, T: 'a + GASolution>
214{
215 score_selection: &'a GAScoreTypeBasedSelection<T>,
216
217 wheel_proportions: Vec<f32>,
218}
219
220impl<'a, T: GASolution> GARouletteWheelSelector<'a, T>
221{
222 pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GARouletteWheelSelector<'a, T>
223 {
224 let wheel_size = p_size;
229
230 GARouletteWheelSelector
231 {
232 score_selection: s,
233 wheel_proportions: vec![0.0; wheel_size],
234 }
235 }
236}
237
238impl<'a, T: GASolution> GASelector<'a, T> for GARouletteWheelSelector<'a, T>
239{
240 fn update(&mut self, population: &mut GAPopulation<T>)
241 {
242 if population.size() != self.wheel_proportions.len()
243 {
244 self.wheel_proportions.resize(population.size(), 0.0);
245 }
246
247 population.sort();
248
249 let wheel_slots = self.wheel_proportions.len();
250 let max_score = self.score_selection.max_score(population);
251 let min_score = self.score_selection.min_score(population);
252
253 if max_score == min_score
254 {
255 for i in 0 .. wheel_slots
257 {
258 self.wheel_proportions[i] = ((i+1) as f32)/(wheel_slots as f32);
259 }
260 }
261 else if (max_score > 0.0 && min_score >= 0.0)
262 || (max_score <= 0.0 && min_score < 0.0)
263 {
264 let population_sort_basis = self.score_selection.population_sort_basis();
266
267 match population.order()
268 {
269 GAPopulationSortOrder::HighIsBest
270 => {
271 self.wheel_proportions[0]
272 = self.score_selection.score(
273 population.individual(0, population_sort_basis));
274
275 for i in 1 .. wheel_slots
276 {
277 self.wheel_proportions[i]
278 = self.score_selection.score(
279 population.individual(i, population_sort_basis))
280 + self.wheel_proportions[i-1];
281 }
282
283 for i in 0 .. wheel_slots
284 {
285 self.wheel_proportions[i]
286 /= self.wheel_proportions[wheel_slots-1];
287 }
288 },
289 GAPopulationSortOrder::LowIsBest
290 => {
291 self.wheel_proportions[0]
292 = -self.score_selection.score(
293 population.individual(0, population_sort_basis))
294 + max_score + min_score;
295
296 for i in 1 .. wheel_slots
297 {
298 self.wheel_proportions[i]
299 = -self.score_selection.score(
300 population.individual(i, population_sort_basis))
301 + max_score + min_score
302 + self.wheel_proportions[i-1];
303 }
304
305 for i in 0 .. wheel_slots
306 {
307 self.wheel_proportions[i]
308 /= self.wheel_proportions[wheel_slots-1];
309 }
310 }
311 }
312 }
313 else
314 {
315 }
317 }
318
319 fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
320 {
321 let wheel_slots = self.wheel_proportions.len();
322 let cutoff = rng_ctx.gen::<f32>();
323 let mut lower = 0;
324 let mut upper = wheel_slots-1;
325 let mut i;
326
327 while upper > lower
328 {
329 i = lower + (upper-lower)/2;
330
331 assert!(i < wheel_slots);
332
333 if self.wheel_proportions[i] > cutoff
334 {
335 if i > 0
336 {
337 upper = i-1;
338 }
339 else
340 {
341 upper = 0;
342 }
343 }
344 else
345 {
346 lower = i+1;
347 }
348 }
349
350 lower = cmp::min(wheel_slots-1, lower);
351
352 population.individual(lower, self.score_selection.population_sort_basis())
353 }
354}
355
356pub struct GATournamentSelector<'a, T: 'a + GASolution>
360{
361 score_selection: &'a GAScoreTypeBasedSelection<T>,
362 roulette_wheel_selector: GARouletteWheelSelector<'a, T>,
363}
364
365impl<'a, T: GASolution> GATournamentSelector<'a, T>
366{
367 pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GATournamentSelector<'a, T>
368 {
369 GATournamentSelector
370 {
371 score_selection: s,
372 roulette_wheel_selector: GARouletteWheelSelector::new(s, p_size)
373 }
374 }
375}
376
377impl<'a, T: GASolution> GASelector<'a, T> for GATournamentSelector<'a, T>
378{
379 fn update(&mut self, population: &mut GAPopulation<T>)
380 {
381 self.roulette_wheel_selector.update(population);
382 }
383
384 fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
385 {
386 let low_score_individual;
387 let high_score_individual;
388 let individual1;
389 let individual2;
390
391 individual1 = self.roulette_wheel_selector.select(population, rng_ctx);
392 individual2 = self.roulette_wheel_selector.select(population, rng_ctx);
393
394 if self.score_selection.score(individual1)
395 >= self.score_selection.score(individual2)
396 {
397 low_score_individual = individual2;
398 high_score_individual = individual1;
399 }
400 else
401 {
402 low_score_individual = individual1;
403 high_score_individual = individual2;
404 }
405
406 match population.order()
407 {
408 GAPopulationSortOrder::HighIsBest => high_score_individual,
409 GAPopulationSortOrder::LowIsBest => low_score_individual
410 }
411 }
412}