@@ 2,6 2,8 @@ use std::convert::Infallible;
use std::ops::{AddAssign, Sub};
use std::{cmp::Ordering, error::Error};
use std::fmt::Debug;
+use crate::selection::Selection;
+use crate::replacement::Replacement;
use rand::RngCore;
@@ 534,6 536,164 @@ impl<'a,
}
}
+// NOTE: this is a copy of evolution_algorithm from evolution.rs,
+// this is mainly for lack of time to provide a more generalized implementeation.
+// The problem with the original implementation is that it evaluates the offsprings
+// without evaluating the parents. But for getting proper non-dominated front sorting,
+// we need to evaluate joined population.
+pub fn evolution_algorithm_best_candidate_modified
+ <TChromosome: Clone,
+ TResult: Clone,
+ const DParents: usize,
+ TSelection: Selection<TChromosome, TResult>,
+ TFitness: FitnessFunction<In = TChromosome, Out = TResult>,
+ TPairing: Pairing<DParents, Chromosome = TChromosome, Out = TResult>,
+ TCrossover: Crossover<DParents, Chromosome = TChromosome, Out = TResult>,
+ TReplacement: Replacement<TChromosome, TResult>,
+ TPerturbation: PerturbationOperator<Chromosome = TChromosome>>(
+ initial_population: Population<TChromosome>,
+ parents_count: usize,
+ fitness: &mut TFitness,
+ selection: &mut TSelection,
+ pairing: &mut TPairing,
+ crossover: &mut TCrossover,
+ perturbation: &mut TPerturbation,
+ replacement: &mut TReplacement,
+ better_than: &impl BetterThanOperator<TResult>,
+ // TODO: termination condition
+ iterations: usize,
+ rng: &mut dyn RngCore,
+ mut evolutionary_strategy: impl FnMut(
+ usize,
+ &EvolutionStats<TChromosome, TResult>,
+ &EvaluatedPopulation<TChromosome, TResult>,
+
+ &mut TFitness,
+ &mut TSelection,
+ &mut TPairing,
+ &mut TCrossover,
+ &mut TPerturbation,
+ &mut TReplacement
+ ),
+ // For the statistics, evaluate if a candidate is better. Potential for different functrion than better_than that's used
+ // for the replacement, selection etc.
+ better_than_stats: impl Fn(&TChromosome, &TResult, &Option<EvolutionCandidate<TChromosome, TResult>>) -> bool,
+) -> Result<EvolutionResult<TChromosome, TResult>, Box<dyn Error>> {
+ let mut current_evaluation = 0;
+
+ let mut last_best_candidate: Option<EvolutionCandidate<TChromosome, TResult>> = None;
+ let mut stats: EvolutionStats<TChromosome, TResult> = EvolutionStats {
+ best_candidates: vec![]
+ };
+
+ fn apply_new_eval<TChromosome: Clone, TResult: Clone>(
+ current_evaluation: &mut usize,
+ current_iteration: &usize,
+ stats: &mut EvolutionStats<TChromosome, TResult>,
+ population: &EvaluatedPopulation<TChromosome, TResult>,
+ last_best_candidate: &mut Option<EvolutionCandidate<TChromosome, TResult>>,
+ better_than_stats: &impl Fn(&TChromosome, &TResult, &Option<EvolutionCandidate<TChromosome, TResult>>) -> bool,
+ ) {
+ for individual in population.iter() {
+ let evaluation = &individual.evaluation;
+ let chromosome = &individual.chromosome;
+
+ if better_than_stats(chromosome, evaluation, last_best_candidate) {
+ let previous_best = std::mem::replace(
+ last_best_candidate,
+ Some(EvolutionCandidate {
+ evaluated_chromosome: EvaluatedChromosome {
+ chromosome: chromosome.clone(),
+ evaluation: evaluation.clone(),
+ },
+ evaluation: *current_evaluation,
+ iteration: *current_iteration
+ }));
+
+ if let Some(previous_best) = previous_best {
+ stats.best_candidates.push(previous_best);
+ }
+ }
+ *current_evaluation += 1;
+ }
+ }
+
+ let mut current_population =
+ initial_population.evaluate(fitness)?;
+ apply_new_eval(
+ &mut current_evaluation,
+ &0,
+ &mut stats,
+ ¤t_population,
+ &mut last_best_candidate,
+ &better_than_stats);
+
+ for iteration in 1..=iterations {
+ // Selection
+ let parents = selection.select(parents_count, ¤t_population, better_than, rng).collect::<Vec<_>>();
+ let parent_pairings = pairing.pair(¤t_population, parents.into_iter());
+
+ // Crossover
+ let mut offsprings = crossover.crossover(¤t_population, parent_pairings, rng);
+
+ // Mutation
+ for offspring in offsprings.iter_mut() {
+ perturbation.perturb(offspring, rng);
+ }
+
+ // This is what was wrong.
+ // let evaluated_offsprings =
+ // offsprings.evaluate(fitness)?;
+
+ let original_population_len = current_population.population.len();
+ let reevaluated_joined = {
+ let mut curr = current_population.nonevaluated();
+ curr.join(offsprings);
+ curr.evaluate(fitness)?
+ };
+
+ apply_new_eval(
+ &mut current_evaluation,
+ &iteration,
+ &mut stats,
+ &reevaluated_joined,
+ &mut last_best_candidate,
+ &better_than_stats
+ );
+
+ let (reevaluated_current_population, evaluated_offsprings) =
+ reevaluated_joined.split_at(original_population_len);
+
+ // Replace
+ current_population = replacement.replace(reevaluated_current_population, evaluated_offsprings, better_than, rng);
+
+ evolutionary_strategy(
+ iteration,
+ &stats,
+ ¤t_population,
+ fitness,
+ selection,
+ pairing,
+ crossover,
+ perturbation,
+ replacement
+ );
+ }
+
+ let best_candidate = last_best_candidate.as_ref().map(|x| x.evaluated_chromosome.clone());
+ if last_best_candidate.is_some() {
+ stats.best_candidates.push(last_best_candidate.unwrap());
+ }
+
+ Ok(EvolutionResult {
+ population: current_population,
+ best_candidate,
+ stats,
+ iterations,
+ evaluations: current_evaluation
+ })
+}
+
pub fn nsga_2<const OBJECTIVES: usize,
TChromosome: Clone,
TResult: Clone + Debug + PartialEq + Default + Copy + PartialOrd + Into<f64>,
@@ 562,7 722,7 @@ pub fn nsga_2<const OBJECTIVES: usize,
),
better_than_stats: impl Fn(&TChromosome, &NSGAEvaluation<TResult, OBJECTIVES>, &Option<EvolutionCandidate<TChromosome, NSGAEvaluation<TResult, OBJECTIVES>>>) -> bool,
) -> Result<EvolutionResult<TChromosome, [TResult; OBJECTIVES]>, Box<dyn Error>> {
- let result = evolution_algorithm_best_candidate(
+ let result = evolution_algorithm_best_candidate_modified(
initial_population,
parents_count,
&mut NSGAFitness::new(objectives, better_than),
@@ 616,7 776,7 @@ pub fn constrained_nsga_2<
),
better_than_stats: impl Fn(&TChromosome, &NSGAEvaluation<TResult, OBJECTIVES>, &Option<EvolutionCandidate<TChromosome, NSGAEvaluation<TResult, OBJECTIVES>>>) -> bool,
) -> Result<EvolutionResult<TChromosome, [TResult; OBJECTIVES]>, Box<dyn Error>> {
- let result = evolution_algorithm_best_candidate(
+ let result = evolution_algorithm_best_candidate_modified(
initial_population,
parents_count,
&mut ConstrainedNSGAFitness::new(objectives, constraints, better_than),
@@ 17,6 17,12 @@ pub struct EvaluatedPopulation<TChromosome, TResult> {
}
impl<TChromosome> Population<TChromosome> {
+ pub fn empty() -> Self {
+ Self {
+ population: vec![]
+ }
+ }
+
pub fn from_vec(vec: Vec<TChromosome>) -> Self {
Self {
population: vec
@@ 34,6 40,10 @@ impl<TChromosome> Population<TChromosome> {
)
}
+ pub fn join(&mut self, mut offsprings: Population<TChromosome>) {
+ self.population.append(&mut offsprings.population);
+ }
+
pub fn into_iter(self) -> impl Iterator<Item = TChromosome> {
self.population.into_iter()
}
@@ 54,6 64,12 @@ impl<TInput, TResult> EvaluatedChromosome<TInput, TResult> {
}
impl<TChromosome, TResult> EvaluatedPopulation<TChromosome, TResult> {
+ pub fn empty() -> Self {
+ Self {
+ population: vec![]
+ }
+ }
+
pub fn new() -> Self {
Self {
population: vec![]
@@ 82,6 98,16 @@ impl<TChromosome, TResult> EvaluatedPopulation<TChromosome, TResult> {
best_so_far
}
+ pub fn split_at(self, len: usize) -> (Self, Self) {
+ let mut left = self.population;
+ let right = left.split_off(len);
+
+ (
+ Self::from_vec(left),
+ Self::from_vec(right),
+ )
+ }
+
pub fn add(&mut self, c: EvaluatedChromosome<TChromosome, TResult>) {
self.population.push(c)
}
@@ 90,6 116,13 @@ impl<TChromosome, TResult> EvaluatedPopulation<TChromosome, TResult> {
self.population
}
+ pub fn nonevaluated(self) -> Population<TChromosome> {
+ Population::from_vec(self.deconstruct()
+ .into_iter()
+ .map(|individual| individual.chromosome)
+ .collect::<Vec<_>>())
+ }
+
pub fn join(&mut self, mut offsprings: EvaluatedPopulation<TChromosome, TResult>) {
self.population.append(&mut offsprings.population);
}