use super::ga_core::GASolution;
use super::ga_population::{GAPopulation, GAPopulationSortBasis, GAPopulationSortOrder};
use super::ga_random::{GARandomCtx};
use std::cmp;
pub trait GASelector<'a, T: GASolution>
{
fn update(&mut self, population: &mut GAPopulation<T>) {}
fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T;
}
pub trait GAScoreTypeBasedSelection<T: GASolution>
{
fn score(&self, individual: &T) -> f32;
fn population_sort_basis(&self) -> GAPopulationSortBasis;
fn max_score(&self, population: &GAPopulation<T>) -> f32;
fn min_score(&self, population: &GAPopulation<T>) -> f32;
}
pub struct GARawScoreBasedSelection;
impl<T: GASolution> GAScoreTypeBasedSelection<T> for GARawScoreBasedSelection
{
fn score(&self, individual: &T) -> f32
{
individual.score()
}
fn population_sort_basis(&self) -> GAPopulationSortBasis
{
GAPopulationSortBasis::Raw
}
fn max_score(&self, population: &GAPopulation<T>) -> f32
{
self.score(population.best_by_raw_score())
}
fn min_score(&self, population: &GAPopulation<T>) -> f32
{
self.score(population.worst_by_raw_score())
}
}
pub struct GAScaledScoreBasedSelection;
impl<T: GASolution> GAScoreTypeBasedSelection<T> for GAScaledScoreBasedSelection
{
fn score(&self, individual: &T) -> f32
{
individual.fitness()
}
fn population_sort_basis(&self) -> GAPopulationSortBasis
{
GAPopulationSortBasis::Scaled
}
fn max_score(&self, population: &GAPopulation<T>) -> f32
{
self.score(population.best_by_scaled_score())
}
fn min_score(&self, population: &GAPopulation<T>) -> f32
{
self.score(population.worst_by_scaled_score())
}
}
pub struct GARankSelector<'a, T: 'a + GASolution>
{
score_selection: &'a GAScoreTypeBasedSelection<T>
}
impl<'a, T: GASolution> GARankSelector<'a, T>
{
pub fn new(s: &'a GAScoreTypeBasedSelection<T>) -> GARankSelector<'a, T>
{
GARankSelector
{
score_selection: s
}
}
}
impl<'a, T: GASolution> GASelector<'a, T> for GARankSelector<'a, T>
{
fn update(&mut self, population: &mut GAPopulation<T>)
{
population.sort();
}
fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
{
let mut best_count = 1;
let population_sort_basis = self.score_selection.population_sort_basis();
let best_score: f32 = self.score_selection.max_score(population);
for i in 1..population.size()
{
if self.score_selection.score(population.individual(i, population_sort_basis)) != best_score
{
break;
}
best_count = best_count + 1;
}
population.individual(rng_ctx.gen_range(0, best_count), population_sort_basis)
}
}
pub struct GAUniformSelector;
impl GAUniformSelector
{
pub fn new() -> GAUniformSelector
{
GAUniformSelector
}
}
impl<'a, T: GASolution> GASelector<'a, T> for GAUniformSelector
{
fn update(&mut self, population: &mut GAPopulation<T>)
{
population.sort();
}
fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
{
population.individual(
rng_ctx.gen_range(0, population.size()),
GAPopulationSortBasis::Raw)
}
}
pub struct GARouletteWheelSelector<'a, T: 'a + GASolution>
{
score_selection: &'a GAScoreTypeBasedSelection<T>,
wheel_proportions: Vec<f32>,
}
impl<'a, T: GASolution> GARouletteWheelSelector<'a, T>
{
pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GARouletteWheelSelector<'a, T>
{
let wheel_size = p_size;
GARouletteWheelSelector
{
score_selection: s,
wheel_proportions: vec![0.0; wheel_size],
}
}
}
impl<'a, T: GASolution> GASelector<'a, T> for GARouletteWheelSelector<'a, T>
{
fn update(&mut self, population: &mut GAPopulation<T>)
{
if population.size() != self.wheel_proportions.len()
{
self.wheel_proportions.resize(population.size(), 0.0);
}
population.sort();
let wheel_slots = self.wheel_proportions.len();
let max_score = self.score_selection.max_score(population);
let min_score = self.score_selection.min_score(population);
if max_score == min_score
{
for i in 0 .. wheel_slots
{
self.wheel_proportions[i] = ((i+1) as f32)/(wheel_slots as f32);
}
}
else if (max_score > 0.0 && min_score >= 0.0)
|| (max_score <= 0.0 && min_score < 0.0)
{
let population_sort_basis = self.score_selection.population_sort_basis();
match population.order()
{
GAPopulationSortOrder::HighIsBest
=> {
self.wheel_proportions[0]
= self.score_selection.score(
population.individual(0, population_sort_basis));
for i in 1 .. wheel_slots
{
self.wheel_proportions[i]
= self.score_selection.score(
population.individual(i, population_sort_basis))
+ self.wheel_proportions[i-1];
}
for i in 0 .. wheel_slots
{
self.wheel_proportions[i]
/= self.wheel_proportions[wheel_slots-1];
}
},
GAPopulationSortOrder::LowIsBest
=> {
self.wheel_proportions[0]
= -self.score_selection.score(
population.individual(0, population_sort_basis))
+ max_score + min_score;
for i in 1 .. wheel_slots
{
self.wheel_proportions[i]
= -self.score_selection.score(
population.individual(i, population_sort_basis))
+ max_score + min_score
+ self.wheel_proportions[i-1];
}
for i in 0 .. wheel_slots
{
self.wheel_proportions[i]
/= self.wheel_proportions[wheel_slots-1];
}
}
}
}
else
{
}
}
fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
{
let wheel_slots = self.wheel_proportions.len();
let cutoff = rng_ctx.gen::<f32>();
let mut lower = 0;
let mut upper = wheel_slots-1;
let mut i;
while upper > lower
{
i = lower + (upper-lower)/2;
assert!(i < wheel_slots);
if self.wheel_proportions[i] > cutoff
{
if i > 0
{
upper = i-1;
}
else
{
upper = 0;
}
}
else
{
lower = i+1;
}
}
lower = cmp::min(wheel_slots-1, lower);
population.individual(lower, self.score_selection.population_sort_basis())
}
}
pub struct GATournamentSelector<'a, T: 'a + GASolution>
{
score_selection: &'a GAScoreTypeBasedSelection<T>,
roulette_wheel_selector: GARouletteWheelSelector<'a, T>,
}
impl<'a, T: GASolution> GATournamentSelector<'a, T>
{
pub fn new(s: &'a GAScoreTypeBasedSelection<T>, p_size: usize) -> GATournamentSelector<'a, T>
{
GATournamentSelector
{
score_selection: s,
roulette_wheel_selector: GARouletteWheelSelector::new(s, p_size)
}
}
}
impl<'a, T: GASolution> GASelector<'a, T> for GATournamentSelector<'a, T>
{
fn update(&mut self, population: &mut GAPopulation<T>)
{
self.roulette_wheel_selector.update(population);
}
fn select(&self, population: &'a GAPopulation<T>, rng_ctx: &mut GARandomCtx) -> &'a T
{
let low_score_individual;
let high_score_individual;
let individual1;
let individual2;
individual1 = self.roulette_wheel_selector.select(population, rng_ctx);
individual2 = self.roulette_wheel_selector.select(population, rng_ctx);
if self.score_selection.score(individual1)
>= self.score_selection.score(individual2)
{
low_score_individual = individual2;
high_score_individual = individual1;
}
else
{
low_score_individual = individual1;
high_score_individual = individual2;
}
match population.order()
{
GAPopulationSortOrder::HighIsBest => high_score_individual,
GAPopulationSortOrder::LowIsBest => low_score_individual
}
}
}