use super::*;
use std::ops::Range;
pub struct AdaptiveGeneticAlgorithmParameters {
pub population_size: usize,
pub elitism_rate: f64,
pub crossover_rate_range: Range<f64>,
pub mutation_rate_range: Range<f64>,
pub selection_strategy: SelectionStrategy,
pub parameter_adjustment_interval: usize,
}
impl Default for AdaptiveGeneticAlgorithmParameters {
fn default() -> Self {
Self {
population_size: 100,
elitism_rate: 0.01,
crossover_rate_range: 0.3..1.0,
mutation_rate_range: 0.1..1.0,
selection_strategy: SelectionStrategy::RouletteWheel,
parameter_adjustment_interval: 1,
}
}
}
impl EvolutionParameters for AdaptiveGeneticAlgorithmParameters {
fn initial_population_size(&self) -> usize {
self.population_size
}
fn max_population_size(&self) -> usize {
self.population_size
}
fn validate(&self) {
assert!(self.population_size != 0);
assert!((0.0..1.0).contains(&self.elitism_rate));
assert!((0.0..=1.0).contains(&self.crossover_rate_range.start));
assert!((0.0..=1.0).contains(&self.crossover_rate_range.end));
assert!((0.0..=1.0).contains(&self.mutation_rate_range.start));
assert!((0.0..=1.0).contains(&self.mutation_rate_range.end));
}
}
pub struct AdaptiveGeneticAlgorithm<M: EvolutionModel> {
model: M,
population_size: usize,
elite_size: usize,
adjustment_interval: usize,
crossover_rate_range: Range<f64>,
mutation_rate_range: Range<f64>,
breeding: ReproductionParameters<M>,
reference_std: f64,
}
impl<M: EvolutionModel> EvolutionAlgorithm<M> for AdaptiveGeneticAlgorithm<M> {
type Parameters = AdaptiveGeneticAlgorithmParameters;
#[inline]
fn model(&self) -> &M {
&self.model
}
#[inline]
fn reproduction_parameters(&self) -> (&ReproductionParameters<M>, usize) {
(&self.breeding, self.population_size)
}
#[inline]
fn configure(&mut self, parameters: &AdaptiveGeneticAlgorithmParameters) {
parameters.validate();
self.population_size = parameters.population_size;
self.elite_size = interpolate_usize(0, parameters.population_size, parameters.elitism_rate);
self.adjustment_interval = parameters.parameter_adjustment_interval;
self.crossover_rate_range = parameters.crossover_rate_range.clone();
self.mutation_rate_range = parameters.mutation_rate_range.clone();
self.breeding.parent_selector = parameters.selection_strategy.create_selector();
}
#[inline]
fn select_elites(
&self,
population: &Population<M::Individual>,
elites: &mut Vec<M::Individual>,
) {
if self.elite_size != 0 {
let range_start = population.len().saturating_sub(self.elite_size);
elites.extend(population[range_start..].iter().cloned());
}
}
#[inline]
fn prepare_for_next_population(&mut self, population: &Population<M::Individual>) {
match population.generation() {
0 => {
self.reference_std = population.stats().std();
self.breeding.crossover_rate = self.crossover_rate_range.end;
self.breeding.mutation_rate = self.mutation_rate_range.start;
}
n if self.adjustment_interval != 0 && n % self.adjustment_interval == 0 => {
let diversity = (population.stats().std() / self.reference_std).clamp(0.0, 1.0);
self.breeding.crossover_rate = interpolate_f64(
self.crossover_rate_range.start,
self.crossover_rate_range.end,
diversity,
);
self.breeding.mutation_rate = interpolate_f64(
self.mutation_rate_range.start,
self.mutation_rate_range.end,
1.0 - diversity,
);
}
_ => {}
}
self.model.pre_generation(population, &mut self.breeding);
self.breeding.parent_selector.prepare(population);
}
}
impl<M: EvolutionModel> From<M> for AdaptiveGeneticAlgorithm<M> {
fn from(model: M) -> Self {
Self {
model,
population_size: Default::default(),
elite_size: Default::default(),
breeding: Default::default(),
reference_std: Default::default(),
adjustment_interval: Default::default(),
crossover_rate_range: Default::default(),
mutation_rate_range: Default::default(),
}
}
}
impl<M: EvolutionModel + Default> Default for AdaptiveGeneticAlgorithm<M> {
fn default() -> Self {
Self::from(M::default())
}
}