use std::error::Error; use crate::{comparison::BetterThanOperator, crossover::Crossover, fitness::FitnessFunction, pairing::Pairing, perturbation::PerturbationOperator, replacement::{EvaluatedChromosome, EvaluatedPopulation, Population, Replacement}, selection::Selection}; #[derive(Clone, Debug)] pub struct EvolutionCandidate { pub evaluated_chromosome: EvaluatedChromosome, pub iteration: usize } #[derive(Clone, Debug)] pub struct EvolutionCandidatePopulation { pub current_population: EvaluatedPopulation, pub iteration: usize } #[derive(Clone, Debug)] pub struct EvolutionStats { pub best_candidates: Vec>, } #[derive(Clone, Debug)] pub struct EvolutionResult { pub population: EvaluatedPopulation, pub stats: EvolutionStats, pub best_candidate: EvaluatedChromosome, pub iterations: usize } pub fn evolution_algorithm( initial_population: Population, parents_count: usize, fitness: &impl FitnessFunction, selection: &mut impl Selection, pairing: &mut impl Pairing, crossover: &mut impl Crossover, perturbation: &mut impl PerturbationOperator, replacement: &mut impl Replacement, better_than: &impl BetterThanOperator, // TODO: termination condition iterations: usize ) -> Result, Box> { let mut current_population = initial_population.evaluate(fitness)?; let mut last_best_candidate = EvolutionCandidate { evaluated_chromosome: current_population.best_candidate(better_than).clone(), iteration: 0 }; let mut stats: EvolutionStats = EvolutionStats { best_candidates: vec![] }; for iteration in 0..iterations { // Figure out best candidate and save it if better than last time let best_candidate = current_population.best_candidate(better_than); if better_than.better_than( &best_candidate.evaluation, &last_best_candidate.evaluated_chromosome.evaluation ) { stats.best_candidates.push(last_best_candidate); last_best_candidate = EvolutionCandidate { evaluated_chromosome: best_candidate.clone(), iteration } } // Selection let parents = selection.select(parents_count, ¤t_population, better_than); let parent_pairings = pairing.pair(¤t_population, parents); // Crossover let mut offsprings = crossover.crossover(¤t_population, parent_pairings); // Mutation for offspring in offsprings.iter_mut() { perturbation.perturb(offspring); } let evaluated_offsprings = offsprings.evaluate(fitness)?; // Replace current_population = replacement.replace(current_population, evaluated_offsprings, better_than); } let best_candidate = last_best_candidate.evaluated_chromosome.clone(); stats.best_candidates.push(last_best_candidate); Ok(EvolutionResult { population: current_population, best_candidate, stats, iterations }) } #[cfg(test)] pub mod tests { use nalgebra::Const; use crate::{binary_string::BinaryString, comparison::MinimizingOperator, crossover::BinaryOnePointCrossover, fitness::one_max::OneMax, initializer::{Initializer, RandomInitializer}, pairing::AdjacentPairing, perturbation::{BinaryStringBitPerturbation, MutationPerturbation}, replacement::{BestReplacement, Population, TournamentReplacement}, selection::{BestSelection, TournamentSelection}}; use super::evolution_algorithm; #[test] pub fn test_one_max() { const D: usize = 512; let optimum = BinaryString::>::new(vec![0; D]); let one_max = OneMax::>::new(); let mut initializer = RandomInitializer::, BinaryString::>>::new_binary(); let population_size = 512; let population = Population::from_iterator( initializer.initialize(Const::, population_size) ); let result = evolution_algorithm( population, population_size / 4, &one_max, // TODO: tournament should somehow accept sorting? // TODO: deterministic and nondeterministic tournament ordering &mut TournamentSelection::new(3, 0.8), &mut AdjacentPairing::new(), &mut BinaryOnePointCrossover::new(), &mut MutationPerturbation::new( Box::new(BinaryStringBitPerturbation::new(0.05)), 0.1), &mut BestReplacement::new(), &MinimizingOperator, 1000 ).unwrap(); println!("{:?}", result.stats.best_candidates .iter() .map(|candidate| candidate.evaluated_chromosome.evaluation) .collect::>()); println!("{:?}", result.stats.best_candidates .iter() .map(|candidate| candidate.iteration) .collect::>()); println!("{:?}", result.population.best_candidate(&MinimizingOperator)); println!("{:?}", result.best_candidate); // println!("{:?}", res); assert_eq!( result.best_candidate.evaluation, 0 ); assert_eq!( result.best_candidate.chromosome, optimum ); } }