use std::sync::Arc;
use derive_builder::Builder;
use crate::{
algorithms::GeneticAlgorithm,
algorithms::helpers::{
AlgorithmContextBuilder,
validators::{validate_bounds, validate_positive, validate_probability},
},
duplicates::{NoDuplicatesCleaner, PopulationCleaner},
evaluator::{ConstraintsFn, EvaluatorBuilder, FitnessFn, NoConstraints},
operators::{
CrossoverOperator, EvolveBuilder, MutationOperator, NoRepair, RepairOperator,
SamplingOperator, SelectionOperator, SurvivalOperator,
},
random::MOORandomGenerator,
};
#[derive(Builder, Debug)]
#[builder(
pattern = "owned",
name = "AlgorithmBuilder",
build_fn(name = "build_params", validate = "Self::validate")
)]
pub struct GeneticAlgorithmParams<S, Sel, Sur, Cross, Mut, F, G = NoConstraints>
where
S: SamplingOperator,
Sel: SelectionOperator<FDim = F::Dim>,
Sur: SurvivalOperator<FDim = F::Dim>,
Cross: CrossoverOperator,
Mut: MutationOperator,
F: FitnessFn,
G: ConstraintsFn,
{
sampler: S,
selector: Sel,
survivor: Sur,
crossover: Cross,
mutation: Mut,
#[builder(default = "Arc::new(NoDuplicatesCleaner)", setter(custom))]
duplicates_cleaner: Arc<dyn PopulationCleaner>,
#[builder(default = "Arc::new(NoRepair)", setter(custom))]
repair: Arc<dyn RepairOperator>,
fitness_fn: F,
constraints_fn: G,
num_vars: usize,
population_size: usize,
num_offsprings: usize,
#[builder(field(vis = "pub"))]
num_iterations: usize,
#[builder(default = "0.2")]
mutation_rate: f64,
#[builder(default = "0.9")]
crossover_rate: f64,
#[builder(default = "true")]
keep_infeasible: bool,
#[builder(default = "false")]
verbose: bool,
#[builder(setter(strip_option), default = "None")]
seed: Option<u64>,
}
impl<S, Sel, Sur, Cross, Mut, F, G> AlgorithmBuilder<S, Sel, Sur, Cross, Mut, F, G>
where
S: SamplingOperator,
Sel: SelectionOperator<FDim = F::Dim>,
Sur: SurvivalOperator<FDim = F::Dim>,
Cross: CrossoverOperator,
Mut: MutationOperator,
F: FitnessFn,
G: ConstraintsFn,
{
pub fn duplicates_cleaner(mut self, v: impl PopulationCleaner + 'static) -> Self {
self.duplicates_cleaner = Some(Arc::new(v));
self
}
pub fn repair(mut self, v: impl RepairOperator + 'static) -> Self {
self.repair = Some(Arc::new(v));
self
}
fn validate(&self) -> Result<(), AlgorithmBuilderError> {
if let Some(num_vars) = self.num_vars {
validate_positive(num_vars, "Number of variables")?;
}
if let Some(population_size) = self.population_size {
validate_positive(population_size, "Population size")?;
}
if let Some(crossover_rate) = self.crossover_rate {
validate_probability(crossover_rate, "Crossover rate")?;
}
if let Some(mutation_rate) = self.mutation_rate {
validate_probability(mutation_rate, "Mutation rate")?;
}
if let Some(num_offsprings) = self.num_offsprings {
validate_positive(num_offsprings, "Number of offsprings")?;
}
if let Some(num_iterations) = self.num_iterations {
validate_positive(num_iterations, "Number of iterations")?;
}
if let Some(cf) = &self.constraints_fn {
if let (Some(lower), Some(upper)) = (cf.lower_bound(), cf.upper_bound()) {
validate_bounds(lower, upper)?;
}
}
Ok(())
}
pub fn build(
self,
) -> Result<GeneticAlgorithm<S, Sel, Sur, Cross, Mut, F, G>, AlgorithmBuilderError> {
let params = self.build_params()?;
let lb = params.constraints_fn.lower_bound();
let ub = params.constraints_fn.upper_bound();
let evaluator = EvaluatorBuilder::default()
.fitness(params.fitness_fn)
.constraints(params.constraints_fn)
.keep_infeasible(params.keep_infeasible)
.build()
.expect("Params already validated in build_params");
let context = AlgorithmContextBuilder::default()
.num_vars(params.num_vars)
.population_size(params.population_size)
.num_offsprings(params.num_offsprings)
.num_iterations(params.num_iterations)
.lower_bound(lb)
.upper_bound(ub)
.build()
.expect("Params already validated in build_params");
let evolve = EvolveBuilder::default()
.selection(params.selector)
.crossover(params.crossover)
.mutation(params.mutation)
.duplicates_cleaner(params.duplicates_cleaner)
.repair(params.repair)
.crossover_rate(params.crossover_rate)
.mutation_rate(params.mutation_rate)
.lower_bound(lb)
.upper_bound(ub)
.build()
.expect("Params already validated in build_params");
let rng = MOORandomGenerator::new_from_seed(params.seed);
Ok(GeneticAlgorithm::new(
None,
params.sampler,
params.survivor,
evolve,
evaluator,
context,
params.verbose,
rng,
))
}
}