use eoa_lib::{ crossover::Crossover, multi_objective_evolution::NSGAEvaluation, pairing, population::{EvaluatedChromosome, EvaluatedPopulation, Population}, }; use rand::{Rng, RngCore}; pub struct FeasibleCrossoverWrapper< const OBJECTIVES: usize, TChromosome, TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation>, > { // If there is just one infesiable, replace it // with this probability. pub p_single_replaced: f64, // If there are two infesiable, replace // first one with this probability pub p_double_first_replaced: f64, // If there are two infesiable, also // replace the second with this probability. // This is tried only if the first one is // replaced. pub p_double_second_replaced: f64, pub archived_count: usize, pub archived_population: Vec>>, pub crossover: TCrossover, } impl< const OBJECTIVES: usize, TChromosome: Clone, TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation>, > FeasibleCrossoverWrapper { pub fn update_archive( &mut self, population: &EvaluatedPopulation>, ) { // Find all feasible solutions in population. Those are candidates // to replace in archive. // Lotta allocations... let mut feasible_individuals = population .population .iter() .filter(|individual| { individual .evaluation .evaluations .iter() .skip(1) .all(|&constr| constr <= 0.0) }) .cloned() .collect::>(); // TODO: this could definitely be allocated in a smarter way for better effectivity self.archived_population.append(&mut feasible_individuals); self.archived_population.sort_unstable_by(|a, b| { a.evaluation.evaluations[0].total_cmp(&b.evaluation.evaluations[0]) }); self.archived_population.truncate(self.archived_count); } } impl< const OBJECTIVES: usize, TChromosome: Clone, TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation>, > Crossover<2> for FeasibleCrossoverWrapper { type Chromosome = TChromosome; type Out = NSGAEvaluation; fn crossover( &self, parents: &EvaluatedPopulation>, pairs: impl Iterator>, rng: &mut dyn RngCore, ) -> Population { // Lotta allocations! :( let parents_count = parents.population.len(); let mut joined_population = parents.clone(); joined_population.join(EvaluatedPopulation::from_vec( self.archived_population.clone(), )); let full_population = joined_population.population.len(); let mut new_pairs = pairs.collect::>(); for pair in new_pairs.iter_mut() { let a = &joined_population.population[pair[0]]; let b = &joined_population.population[pair[1]]; let a_feasible = a .evaluation .evaluations .iter() .skip(1) .all(|&constr| constr <= 0.0); let b_feasible = b .evaluation .evaluations .iter() .skip(1) .all(|&constr| constr <= 0.0); // Only proceed with replacement if we have archived feasible solutions if full_population > parents_count { match (a_feasible, b_feasible) { (false, true) => { if rng.random_bool(self.p_single_replaced) { pair[0] = rng.random_range(parents_count..full_population); } } (true, false) => { if rng.random_bool(self.p_single_replaced) { pair[1] = rng.random_range(parents_count..full_population); } } (false, false) => { if rng.random_bool(self.p_double_first_replaced) { pair[0] = rng.random_range(parents_count..full_population); if rng.random_bool(self.p_double_second_replaced) { pair[1] = rng.random_range(parents_count..full_population); } } } (true, true) => { // Do nothing. } } } } self.crossover .crossover(&joined_population, new_pairs.into_iter(), rng) } }