~ruther/ctu-fee-eoa

abba3169b78ea3914957f461db9f411d3bcdd9ee — Rutherther 6 days ago 84ad018
feat: implement feasible crossover wrapper
1 files changed, 115 insertions(+), 2 deletions(-)

M codes/constr_hw02/src/main.rs
M codes/constr_hw02/src/main.rs => codes/constr_hw02/src/main.rs +115 -2
@@ 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,