mod bistring;
mod generic;
mod realvalued;
use std::error::Error;
use std::fmt::Display;
use std::marker::PhantomData;
use super::individual::Chromosome;
use super::operators::replacement::ReplacementOperator;
use super::operators::selection::SelectionOperator;
use super::population::PopulationGenerator;
use super::{CrossoverOperator, GAConfig, GAParams, MutationOperator, Probe};
use crate::ga::operators::fitness::Fitness;
pub use bistring::BitStringBuilder;
pub use generic::GenericBuilder;
pub use realvalued::RealValuedBuilder;
type FitnessFn<S> = fn(&S) -> f64;
#[derive(Debug, Clone)]
enum ConfigError {
MissingParam(String),
MissingOperator(String),
MissingPopulationFactory,
NoProbe,
NoParams,
}
impl Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::MissingParam(param) => write!(f, "Unspecified parameter: {}", param),
Self::MissingOperator(op) => write!(f, "Unspecified operator: {}", op),
Self::MissingPopulationFactory => write!(f, "Unspecified population factory"),
Self::NoProbe => write!(f, "Unspecified probe"),
Self::NoParams => write!(f, "No parameters were specified"),
}
}
}
impl Error for ConfigError {}
#[derive(Debug, Clone)]
pub(self) struct GAParamsOpt {
pub selection_rate: Option<f64>,
pub mutation_rate: Option<f64>,
pub population_size: Option<usize>,
pub generation_limit: Option<usize>,
pub max_duration: Option<std::time::Duration>,
}
impl GAParamsOpt {
pub fn new() -> Self {
Self {
selection_rate: None,
mutation_rate: None,
population_size: None,
generation_limit: None,
max_duration: None,
}
}
pub fn fill_from(&mut self, other: &GAParams) {
self.selection_rate.get_or_insert(other.selection_rate);
self.mutation_rate.get_or_insert(other.mutation_rate);
self.population_size.get_or_insert(other.population_size);
self.generation_limit.get_or_insert(other.generation_limit);
self.max_duration.get_or_insert(other.max_duration);
}
}
impl TryFrom<GAParamsOpt> for GAParams {
type Error = ConfigError;
fn try_from(params_opt: GAParamsOpt) -> Result<Self, Self::Error> {
let Some(selection_rate) = params_opt.selection_rate else {
return Err(ConfigError::MissingParam("Unspecified selection rate".to_owned()));
};
let Some(mutation_rate) = params_opt.mutation_rate else {
return Err(ConfigError::MissingParam("Unspecified mutation rate".to_owned()));
};
let Some(population_size) = params_opt.population_size else {
return Err(ConfigError::MissingParam("Unspecified population size".to_owned()));
};
let Some(generation_limit) = params_opt.generation_limit else {
return Err(ConfigError::MissingParam("Unspecified generation_limit".to_owned()));
};
let Some(max_duration) = params_opt.max_duration else {
return Err(ConfigError::MissingParam("Unspecified max duration".to_owned()));
};
Ok(GAParams {
selection_rate,
mutation_rate,
population_size,
generation_limit,
max_duration,
})
}
}
pub(self) struct GAConfigOpt<T, M, C, S, R, P, F, Pr>
where
T: Chromosome,
M: MutationOperator<T>,
C: CrossoverOperator<T>,
S: SelectionOperator<T>,
R: ReplacementOperator<T>,
P: PopulationGenerator<T>,
F: Fitness<T>,
Pr: Probe<T>,
{
pub params: GAParamsOpt,
pub fitness_fn: Option<F>,
pub mutation_operator: Option<M>,
pub crossover_operator: Option<C>,
pub selection_operator: Option<S>,
pub replacement_operator: Option<R>,
pub population_factory: Option<P>,
pub probe: Option<Pr>,
phantom: PhantomData<T>,
}
impl<T, M, C, S, R, P, F, Pr> GAConfigOpt<T, M, C, S, R, P, F, Pr>
where
T: Chromosome,
M: MutationOperator<T>,
C: CrossoverOperator<T>,
S: SelectionOperator<T>,
R: ReplacementOperator<T>,
P: PopulationGenerator<T>,
F: Fitness<T>,
Pr: Probe<T>,
{
pub fn new() -> Self {
Self {
params: GAParamsOpt::new(),
fitness_fn: None,
mutation_operator: None,
crossover_operator: None,
selection_operator: None,
replacement_operator: None,
population_factory: None,
probe: None,
phantom: Default::default(),
}
}
}
impl<T, M, C, S, R, P, F, Pr> TryFrom<GAConfigOpt<T, M, C, S, R, P, F, Pr>>
for GAConfig<T, M, C, S, R, P, F, Pr>
where
T: Chromosome,
M: MutationOperator<T>,
C: CrossoverOperator<T>,
S: SelectionOperator<T>,
R: ReplacementOperator<T>,
P: PopulationGenerator<T>,
F: Fitness<T>,
Pr: Probe<T>,
{
type Error = ConfigError;
fn try_from(config_opt: GAConfigOpt<T, M, C, S, R, P, F, Pr>) -> Result<Self, Self::Error> {
let params = GAParams::try_from(config_opt.params)?;
let Some(fitness_fn) = config_opt.fitness_fn else {
return Err(ConfigError::MissingOperator("No fitness function specified".to_owned()));
};
let Some(mutation_operator) = config_opt.mutation_operator else {
return Err(ConfigError::MissingOperator("No mutation operator specified".to_owned()));
};
let Some(crossover_operator) = config_opt.crossover_operator else {
return Err(ConfigError::MissingOperator("No crossover operator specified".to_owned()));
};
let Some(selection_operator) = config_opt.selection_operator else {
return Err(ConfigError::MissingOperator("No selection operator specified".to_owned()));
};
let Some(replacement_operator) = config_opt.replacement_operator else {
return Err(ConfigError::MissingOperator("No replacement operator specified".to_owned()));
};
let Some(population_factory) = config_opt.population_factory else {
return Err(ConfigError::MissingPopulationFactory);
};
let Some(probe) = config_opt.probe else {
return Err(ConfigError::NoProbe);
};
Ok(GAConfig {
params,
fitness_fn,
mutation_operator,
crossover_operator,
selection_operator,
replacement_operator,
population_factory,
probe,
phantom: PhantomData::default(),
})
}
}
pub struct Builder;
impl Builder {
#[allow(clippy::new_ret_no_self)]
pub fn new<T, M, C, S, R, P, F, Pr>() -> GenericBuilder<T, M, C, S, R, P, F, Pr>
where
T: Chromosome,
M: MutationOperator<T>,
C: CrossoverOperator<T>,
S: SelectionOperator<T>,
R: ReplacementOperator<T>,
P: PopulationGenerator<T>,
F: Fitness<T>,
Pr: Probe<T>,
{
GenericBuilder::<T, M, C, S, R, P, F, Pr>::new()
}
pub fn with_rvc<F: Fitness<realvalued::Rvc>>() -> RealValuedBuilder<F> {
RealValuedBuilder::new()
}
pub fn with_bsc<F: Fitness<bistring::Bsc>>() -> BitStringBuilder<F> {
BitStringBuilder::new()
}
}
pub trait DefaultParams {
const DEFAULT_PARAMS: GAParams = GAParams {
selection_rate: 1.0,
mutation_rate: 0.05,
population_size: 100,
generation_limit: usize::MAX,
max_duration: std::time::Duration::MAX,
};
}
#[cfg(test)]
mod test {
use super::GAParamsOpt;
use crate::ga::{builder::ConfigError, GAParams};
fn convert_gaparamsopt_to_ga_params(params_opt: GAParamsOpt) -> Result<GAParams, ConfigError> {
params_opt.try_into()
}
#[test]
fn new_param_opt_is_empty() {
let params = GAParamsOpt::new();
assert!(params.selection_rate.is_none());
assert!(params.mutation_rate.is_none());
assert!(params.population_size.is_none());
assert!(params.generation_limit.is_none());
assert!(params.max_duration.is_none());
}
#[test]
fn param_opt_fills_correctly() {
let mut params_opt = GAParamsOpt::new();
params_opt.selection_rate = Some(0.5);
params_opt.generation_limit = Some(100);
let params = GAParams {
selection_rate: 1.0,
mutation_rate: 1.0,
population_size: 100,
generation_limit: 200,
max_duration: std::time::Duration::from_secs(1),
};
params_opt.fill_from(¶ms);
assert!(params_opt.selection_rate.is_some() && params_opt.selection_rate.unwrap() == 0.5);
assert!(params_opt.mutation_rate.is_some() && params_opt.mutation_rate.unwrap() == 1.0);
assert!(params_opt.population_size.is_some() && params_opt.population_size.unwrap() == 100);
assert!(params_opt.generation_limit.is_some() && params_opt.generation_limit.unwrap() == 100);
assert!(
params_opt.max_duration.is_some()
&& params_opt.max_duration.unwrap() == std::time::Duration::from_secs(1)
);
}
#[test]
fn conversion_works_as_expected() {
let mut params_opt = GAParamsOpt::new();
params_opt.selection_rate = Some(1.0);
assert!(convert_gaparamsopt_to_ga_params(params_opt.clone()).is_err());
params_opt.mutation_rate = Some(0.0);
assert!(convert_gaparamsopt_to_ga_params(params_opt.clone()).is_err());
params_opt.population_size = Some(200);
assert!(convert_gaparamsopt_to_ga_params(params_opt.clone()).is_err());
params_opt.generation_limit = Some(200);
assert!(convert_gaparamsopt_to_ga_params(params_opt.clone()).is_err());
params_opt.max_duration = Some(std::time::Duration::from_micros(10));
assert!(convert_gaparamsopt_to_ga_params(params_opt).is_ok());
}
}