From abba3169b78ea3914957f461db9f411d3bcdd9ee Mon Sep 17 00:00:00 2001 From: Rutherther Date: Fri, 5 Dec 2025 21:19:25 +0100 Subject: [PATCH] feat: implement feasible crossover wrapper --- codes/constr_hw02/src/main.rs | 117 +++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 2 deletions(-) diff --git a/codes/constr_hw02/src/main.rs b/codes/constr_hw02/src/main.rs index 8c5b5a93d6d4a7f939f565b47d6ca55a0a9cf362..2805509b3c2d38a05a418d97f51c29193b35f3c9 100644 --- a/codes/constr_hw02/src/main.rs +++ b/codes/constr_hw02/src/main.rs @@ -1,10 +1,10 @@ use std::{convert::Infallible, env, fs, io::Write, rc::Rc}; use eoa_lib::{ - bounded::BoundedOVector, comparison::MinimizingOperator, constraints::{stochastic_ranking_evolution_algorithm, ConstrainedEvalFitness, ConstrainedFitnessFunction, ConstraintFunction, LowerThanConstraintFunction}, crossover::{ArithmeticCrossover, BoundedCrossover, BoundedCrossoverStrategy}, evolution::{EvolutionCandidate, EvolutionResult, EvolutionStats}, fitness::FitnessFunction, initializer::{Initializer, RandomInitializer}, multi_objective_evolution::{nsga_2, constrained_nsga_2}, pairing::AdjacentPairing, perturbation::{BoundedPerturbation, BoundedPerturbationStrategy, MutationPerturbation, RandomDistributionPerturbation}, population::{EvaluatedChromosome, EvaluatedPopulation, Population}, replacement::{BestReplacement, GenerationalReplacement}, selection::{BestSelection, TournamentSelection} + bounded::BoundedOVector, comparison::MinimizingOperator, constraints::{stochastic_ranking_evolution_algorithm, ConstrainedEvalFitness, ConstrainedFitnessFunction, ConstraintFunction, LowerThanConstraintFunction}, crossover::{ArithmeticCrossover, BoundedCrossover, BoundedCrossoverStrategy, Crossover}, evolution::{EvolutionCandidate, EvolutionResult, EvolutionStats}, fitness::FitnessFunction, initializer::{Initializer, RandomInitializer}, multi_objective_evolution::{constrained_nsga_2, nsga_2, NSGAEvaluation}, pairing::{AdjacentPairing, ParentPairing}, perturbation::{BoundedPerturbation, BoundedPerturbationStrategy, MutationPerturbation, RandomDistributionPerturbation}, population::{EvaluatedChromosome, EvaluatedPopulation, Population}, replacement::{BestReplacement, GenerationalReplacement}, selection::{BestSelection, TournamentSelection} }; use nalgebra::{ArrayStorage, Const, Matrix, SVector}; -use rand::RngCore; +use rand::{Rng, RngCore}; use rand_distr::Normal; use chrono::prelude::*; @@ -36,6 +36,119 @@ impl FitnessFunction for ArbitraryFitness { } } +pub struct FeasibleCrossoverWrapper>> { + // 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); + + 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(&joined_population, new_pairs.into_iter(), rng) + } +} + + /// A constrained optimization problem with clear field definitions pub struct ConstrainedProblem { pub name: String,