use crate::ga::builder::FitnessFn;
use crate::ga::individual::{BitStringIndividual, IndividualTrait};
use crate::ga::operators::fitness::{Fitness, FnBasedFitness};
use crate::ga::operators::replacement::BothParents;
use crate::ga::Individual;
use crate::ga::{
operators::{crossover::SinglePoint, mutation::FlipBit, selection::Tournament},
population::BitStrings,
probe::StdoutProbe,
GeneticSolver,
};
use super::{DefaultParams, GAConfigOpt};
pub(super) type Bsc = Vec<bool>;
pub struct BitStringBuilder<F: Fitness<BitStringIndividual>> {
config: GAConfigOpt<
Individual<Bsc>,
FlipBit<rand::rngs::ThreadRng>,
SinglePoint<rand::rngs::ThreadRng>,
Tournament<rand::rngs::ThreadRng>,
BothParents,
BitStrings<rand::rngs::ThreadRng>,
F,
StdoutProbe,
>,
dim: Option<usize>,
}
impl BitStringBuilder<FnBasedFitness<BitStringIndividual>> {
pub fn fitness_fn(
self,
fitness_fn: FitnessFn<Bsc, <BitStringIndividual as IndividualTrait>::FitnessValueT>,
) -> Self {
self.set_fitness(FnBasedFitness::new(fitness_fn))
}
}
impl<F: Fitness<BitStringIndividual>> BitStringBuilder<F> {
pub(super) fn new() -> Self {
BitStringBuilder {
config: GAConfigOpt::new(),
dim: None,
}
}
pub fn set_selection_rate(mut self, selection_rate: f64) -> Self {
assert!((0f64..=1f64).contains(&selection_rate));
self.config.params.selection_rate = Some(selection_rate);
self
}
pub fn set_mutation_rate(mut self, mutation_rate: f64) -> Self {
assert!((0.0..=1.0).contains(&mutation_rate));
self.config.params.mutation_rate = Some(mutation_rate);
self
}
pub fn set_max_duration(mut self, max_duration: std::time::Duration) -> Self {
self.config.params.max_duration = Some(max_duration);
self
}
pub fn set_max_generation_count(mut self, max_gen_count: usize) -> Self {
assert!(max_gen_count >= 1);
self.config.params.generation_limit = Some(max_gen_count);
self
}
pub fn set_population_size(mut self, size: usize) -> Self {
assert!(size > 0);
self.config.params.population_size = Some(size);
self
}
pub fn dim(mut self, dim: usize) -> Self {
assert!(dim > 0, "Dimension of a problem must be > 0");
self.dim = Some(dim);
self
}
pub fn set_fitness(mut self, fitness: F) -> Self {
self.config.fitness_fn = Some(fitness);
self
}
pub fn build(
mut self,
) -> GeneticSolver<
Individual<Bsc>,
FlipBit<rand::rngs::ThreadRng>,
SinglePoint<rand::rngs::ThreadRng>,
Tournament<rand::rngs::ThreadRng>,
BothParents,
BitStrings<rand::rngs::ThreadRng>,
F,
StdoutProbe,
> {
self.config.params.fill_from(&Self::DEFAULT_PARAMS);
if self.config.fitness_fn.is_none() {
panic!("Fitness function must be set");
}
if self.dim.is_none() {
panic!("Problem dimension must be set");
}
self.config
.crossover_operator
.get_or_insert_with(SinglePoint::new);
self.config.mutation_operator.get_or_insert_with(FlipBit::new);
self.config
.selection_operator
.get_or_insert_with(|| Tournament::new(0.2));
self.config
.replacement_operator
.get_or_insert_with(BothParents::new);
self.config
.population_factory
.get_or_insert_with(|| BitStrings::new(self.dim.unwrap_or(10)));
self.config.probe.get_or_insert_with(StdoutProbe::new);
let config = match self.config.try_into() {
Ok(config) => config,
Err(err) => panic!("Builder panicked with error: {err}"),
};
GeneticSolver::new(config)
}
}
impl<F: Fitness<BitStringIndividual>> DefaultParams for BitStringBuilder<F> {}