rust_monster/ga/
ga_selectors.rs

1// Copyright 2016 Revolution Solid & Contributors.
2// author(s): carlos-lopez-garces, sysnett
3// rust-monster is licensed under an MIT License.
4
5//! GA Selectors
6//!
7//! A selector represents and performs a method of selection.
8//!
9//! Selection is the action of choosing solutions (individuals) of the current
10//! generation that will create offspring for the next generation.
11//!
12//! Selectors represent and perform a different method of selection each. The
13//! expectation is that the offspring solutions be fitter than their selected
14//! parents. For this reason, many of the selectors tend to choose the fitter
15//! most of the time. However, many of them acknowledge the need for selecting
16//! less fit solutions, too: A genetic operator (crossover, mutation) used on
17//! suboptimal solutions may sometimes produce a solution that is fitter than
18//! those that could be produced by optimal ones.
19//!
20//! Available selectors:
21//!
22//! `GARankSelector`
23//! `GAUniformSelector`
24//! `GARouletteWheelSelector`
25//! `GATournamentSelector`
26//!
27//! # Examples
28use super::ga_core::GASolution;
29use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
30use super::ga_random::{GARandomCtx};
31use std::cmp;
32
33/// Selector trait.
34///
35/// Selector common interface. Each selector implements a different method
36/// of selection and keeps and manages its own internal state.
37pub trait GASelector<'a, T: GASolution>
38{
39    /// Update internal state. 
40    ///
41    /// NOOP default implementation for selectors that don't keep internal state.
42    fn update(&mut self, population: &mut GAPopulation<T>) {}
43
44    /// Select an individual from the population. 
45    ///
46    /// Each selector implements a different method of selection. Randomization 
47    /// is a key aspect of all methods.
48    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T;
49}
50
51/// Selection score type basis.
52///
53/// Selectors are configured, at the time of creation, with the type of score
54/// {RAW, SCALED} they will use to perform selections. The type of score
55/// ultimately determines the function that will be invoked on the `GASolution`
56/// to obtain the score value of the configured type. `GAScoreTypeBasedSelection`
57/// objects provide a unified interface to the different score functions of a
58/// `GASolution`. Selectors use these objects to obtain score values of the
59/// configured type, without explicitly choosing between them based on
60/// `GAPopulationSortBasis`.
61pub 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
72/// Selection based on RAW score.
73pub 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
98/// Selection based on SCALED score.
99pub 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
124/// Rank selector.
125///
126/// Select the best individual of the population. If more than 1 share the
127/// best score, choose 1 among them at random.
128pub 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        // TODO: Confirm assumption that population has 1 individual at least.
154        // Number of individuals that share best score.
155        let mut best_count = 1;
156
157        // This is not a move, but a copy.
158        let population_sort_basis = self.score_selection.population_sort_basis();
159
160        // All individuals that share the best score will be considered for selection.
161        let best_score: f32 = self.score_selection.max_score(population);
162
163        // Skip 0th best. It is known that it has the best score.
164        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        // Select any individual from those that share the best score.
175        population.individual(rng_ctx.gen_range(0, best_count), population_sort_basis)
176    }
177}
178
179pub struct GAUniformSelector;
180
181/// Uniform selector.
182///
183/// Select an individual at random, with equal probability.
184impl 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        // Need to sort first, because GAPopulation.individual() draws individuals
197        // from the sorted lists.
198        population.sort();
199    }
200
201    // Select any individual at random.
202    fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
203    {
204        // Since selection is at random, it doesn't matter where the individual
205        // is drawn from, the Raw/score-sorted or the Scaled/fitness-sorted list.
206        population.individual(
207            rng_ctx.gen_range(0, population.size()),
208            GAPopulationSortBasis::Raw)
209    }
210}
211
212/// Roulette Wheel selector.
213pub 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        // TODO: Comment doesn't look correct.
225        // vec![] borrows references (invocation of size() is through *p, or so the
226        // compiler says); since p has already been borrowed as a mutable reference
227        // (no data races allowed), p.size() can't be passed to vec![].
228        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            // Upper bound is excluded.
256            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            // This is not a move, but a copy.
265            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            // TODO: Raise error.
316        }
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
356/// Tournament selector.
357///
358/// Select 2 individuals using Roulette Wheel selection and select the best of the 2.
359pub 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}