@@ 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<const SIZE: usize> FitnessFunction for ArbitraryFitness<SIZE> {
}
}
+pub struct FeasibleCrossoverWrapper<const OBJECTIVES: usize,
+ TChromosome,
+ TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation<f64, OBJECTIVES>>> {
+ // 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<EvaluatedChromosome<TChromosome, NSGAEvaluation<f64, OBJECTIVES>>>,
+ pub crossover: TCrossover
+}
+
+
+impl<
+ const OBJECTIVES: usize,
+ TChromosome: Clone,
+ TCrossover: Crossover<2, Chromosome = TChromosome, Out = NSGAEvaluation<f64, OBJECTIVES>>>
+ FeasibleCrossoverWrapper<OBJECTIVES, TChromosome, TCrossover> {
+ pub fn update_archive(
+ &mut self,
+ population: &EvaluatedPopulation<TChromosome, NSGAEvaluation<f64, OBJECTIVES>>
+ ) {
+ // 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::<Vec<_>>();
+
+ // 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<f64, OBJECTIVES>>>
+ Crossover<2> for FeasibleCrossoverWrapper<OBJECTIVES, TChromosome, TCrossover> {
+ type Chromosome = TChromosome;
+ type Out = NSGAEvaluation<f64, OBJECTIVES>;
+
+ fn crossover(
+ &self,
+ parents: &EvaluatedPopulation<Self::Chromosome, NSGAEvaluation<f64, OBJECTIVES>>,
+ pairs: impl Iterator<Item = eoa_lib::pairing::ParentPairing<2>>,
+ rng: &mut dyn RngCore
+ ) -> Population<Self::Chromosome> {
+ // 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::<Vec<_>>();
+
+ 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<const DIM: usize, const CONSTRAINTS: usize> {
pub name: String,