From 79a2ec1f864b259b69e909656d2751aad38b45b9 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 30 Nov 2025 15:24:43 +0100 Subject: [PATCH] feat: add convenient function for stochastic ranking evaluation --- codes/eoa_lib/src/constraints.rs | 147 +++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 15 deletions(-) diff --git a/codes/eoa_lib/src/constraints.rs b/codes/eoa_lib/src/constraints.rs index 454bc9d7608124e32555eb76689601ea2dc5a11f..333b5b1171d7bc0601f337e0d1da7ced6162cc29 100644 --- a/codes/eoa_lib/src/constraints.rs +++ b/codes/eoa_lib/src/constraints.rs @@ -3,7 +3,7 @@ use std::{collections::VecDeque, convert::Infallible, error::Error}; use rand::{Rng, RngCore}; use thiserror::Error; -use crate::{comparison::{BetterThanOperator, MinimizingOperator}, crossover::Crossover, evolution::EvolutionStats, fitness::FitnessFunction, pairing::Pairing, perturbation::PerturbationOperator, population::{EvaluatedChromosome, EvaluatedPopulation, Population}, replacement::Replacement, selection::Selection}; +use crate::{comparison::{BetterThanOperator, MinimizingOperator}, crossover::Crossover, evolution::{evolution_algorithm_best_candidate, EvolutionCandidate, EvolutionResult, EvolutionStats}, fitness::FitnessFunction, pairing::Pairing, perturbation::PerturbationOperator, population::{EvaluatedChromosome, EvaluatedPopulation, Population}, replacement::Replacement, selection::Selection}; pub trait ConstraintFunction { type Chromosome; @@ -224,11 +224,12 @@ pub fn evolve_constraint_penalty_weight_tau_target } } -#[derive(PartialEq, Debug)] +#[derive(PartialEq, Clone, Debug)] pub struct ConstrainedEvaluation { fitness: TOut, constraints: [TOut; CONSTRAINTS], - weighted_sum: TOut + weighted_sum: TOut, + is_feasible: bool, } impl PartialOrd for ConstrainedEvaluation { @@ -245,12 +246,12 @@ pub struct ConstrainedEvalFitness<'a, TConstraint: ConstraintFunction> { fitness: &'a TFitness, constraints: [&'a TConstraint; CONSTRAINTS], - constraint_weights: Vec + constraint_weights: [TOut; CONSTRAINTS] } impl <'a, const CONSTRAINTS: usize, - TOut: std::ops::Mul + std::ops::AddAssign + Copy, + TOut: std::ops::Mul + std::ops::AddAssign + Copy + Default, TIn, TFitness: FitnessFunction, TConstraint: ConstraintFunction> @@ -267,21 +268,31 @@ impl <'a, }; let mut weighted_sum = fit; let mut constraints = [fit; CONSTRAINTS]; + let mut is_feasible = true; for (i, (constraint, weight)) in self.constraints.iter().zip(self.constraint_weights.iter()).enumerate() { - let constraint = match constraint.evaluate(inp) { + let constraint_eval = match constraint.evaluate(inp) { Ok(constraint) => constraint, Err(err) => return Err(ConstrainedFitnessErr::ConstraintErr(err)) }; - constraints[i] = constraint; - weighted_sum += weight.clone() * constraint; + constraints[i] = constraint_eval; + + weighted_sum += match constraint.is_feasible(inp) { + Ok(true) => weight.clone() * constraint_eval, + Ok(false) => { + is_feasible = false; + Default::default() + }, + Err(err) => return Err(ConstrainedFitnessErr::ConstraintErr(err)) + }; } Ok(ConstrainedEvaluation { fitness: fit, constraints, - weighted_sum + weighted_sum, + is_feasible }) } } @@ -316,21 +327,22 @@ fn stochastic_ranking_sort { +pub struct StochasticRankingSelection<'a, TSelection, TBetterThan> { N: usize, p: f64, - selection: TSelection, - better_than: TBetterThan + selection: &'a mut TSelection, + better_than: &'a TBetterThan } const MINIMIZING_OPERATOR: MinimizingOperator = MinimizingOperator; -impl, TBetterThan: BetterThanOperator> - Selection> for StochasticRankingSelection { + Selection> for StochasticRankingSelection<'a, TSelection, TBetterThan> { fn select(&self, count: usize, evaluations: &EvaluatedPopulation>, @@ -339,7 +351,7 @@ impl impl Iterator { let sorted_indices = stochastic_ranking_sort( evaluations.population.as_slice(), - self.N, self.p, &self.better_than, rng + self.N, self.p, self.better_than, rng ); let mut rankings = vec![EvaluatedChromosome { chromosome: (), @@ -363,3 +375,108 @@ impl + std::ops::AddAssign + Copy + Default, + const DParents: usize, + const CONSTRAINTS: usize, + TFitness: FitnessFunction, + TSelection: Selection<(), usize>, + TPairing: Pairing>, + TCrossover: Crossover>, + TReplacement: Replacement>, + TConstraint: ConstraintFunction, + TPerturbation: PerturbationOperator>( + initial_population: Population, + parents_count: usize, + N: usize, + p: f64, + fitness: &mut TFitness, + constraints: [&TConstraint; CONSTRAINTS], + constraint_weights: [TResult; CONSTRAINTS], + pairing: &mut TPairing, + selection: &mut TSelection, + crossover: &mut TCrossover, + perturbation: &mut TPerturbation, + replacement: &mut TReplacement, + better_than: &(impl BetterThanOperator> + BetterThanOperator), + // TODO: termination condition + iterations: usize, + rng: &mut dyn RngCore, + // mut evolutionary_strategy: impl FnMut( + // usize, + // &EvolutionStats, + // &EvaluatedPopulation, + + // &mut TPairing, + // &mut TCrossover, + // &mut TPerturbation, + // &mut TReplacement, + // &mut ConstrainedEvalFitness, + // ), + ) -> Result<(EvolutionResult, Vec), Box> +{ + let mut constrained_fitness = ConstrainedEvalFitness { + fitness, + constraints, + constraint_weights, + }; + + let mut stochastic_ranking_selection = + StochasticRankingSelection { + N, + p, + selection, + better_than + }; + + let mut feasible_fractions = Vec::with_capacity(iterations); + + evolution_algorithm_best_candidate( + initial_population, + parents_count, + &mut constrained_fitness, + &mut stochastic_ranking_selection, + pairing, + crossover, + perturbation, + replacement, + better_than, + iterations, + rng, + |iteration, stats, population, fitness, selection, pairing, crossover, perturbation, replacement| { + let feasible_fraction = population.population + .iter() + .map(|x| x.evaluation.is_feasible) + .count() as f64 / population.population.len() as f64; + + feasible_fractions.push(feasible_fraction); + + // evolutionary_strategy( + // iteration, + // stats, + // population, + // fitness + // ) + }, + |_, evaluation, best_candidate| { + // Do not save enfeasible solutions! + if !evaluation.is_feasible { + return false; + } + + if best_candidate.is_none() { + return true; + } + + better_than.better_than( + evaluation, + &best_candidate.as_ref().unwrap().evaluated_chromosome.evaluation + ) + }).map(|res| + ( + res.map(|evaluation| evaluation.fitness), + feasible_fractions + )) +}