~ruther/ctu-fee-eoa

79a2ec1f864b259b69e909656d2751aad38b45b9 — Rutherther 11 days ago 51a8da0
feat: add convenient function for stochastic ranking evaluation
1 files changed, 132 insertions(+), 15 deletions(-)

M codes/eoa_lib/src/constraints.rs
M codes/eoa_lib/src/constraints.rs => codes/eoa_lib/src/constraints.rs +132 -15
@@ 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<const CONSTRAINTS: usize, TOut> {
    fitness: TOut,
    constraints: [TOut; CONSTRAINTS],
    weighted_sum: TOut
    weighted_sum: TOut,
    is_feasible: bool,
}

impl<const CONSTRAINTS: usize, TOut: PartialOrd> PartialOrd for ConstrainedEvaluation<CONSTRAINTS, TOut> {


@@ 245,12 246,12 @@ pub struct ConstrainedEvalFitness<'a,
    TConstraint: ConstraintFunction<Chromosome = TIn, Out = TOut>> {
    fitness: &'a TFitness,
    constraints: [&'a TConstraint; CONSTRAINTS],
    constraint_weights: Vec<TOut>
    constraint_weights: [TOut; CONSTRAINTS]
}

impl <'a,
      const CONSTRAINTS: usize,
      TOut: std::ops::Mul<Output = TOut> + std::ops::AddAssign + Copy,
      TOut: std::ops::Mul<Output = TOut> + std::ops::AddAssign + Copy + Default,
      TIn,
      TFitness: FitnessFunction<In = TIn, Out = TOut>,
      TConstraint: ConstraintFunction<Chromosome = TIn, Out = TOut>>


@@ 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<const CONSTRAINTS: usize, TIn, TOut: PartialOrd + Def
    indices
}

pub struct StochasticRankingSelection<TSelection, TBetterThan> {
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<const CONSTRAINTS: usize,
impl<'a,
     const CONSTRAINTS: usize,
     TChromosome,
     TResult: PartialOrd + Default,
     TSelection: Selection<(), usize>,
     TBetterThan: BetterThanOperator<TResult>>
    Selection<TChromosome, ConstrainedEvaluation<CONSTRAINTS, TResult>> for StochasticRankingSelection<TSelection, TBetterThan> {
    Selection<TChromosome, ConstrainedEvaluation<CONSTRAINTS, TResult>> for StochasticRankingSelection<'a, TSelection, TBetterThan> {
    fn select(&self,
              count: usize,
              evaluations: &EvaluatedPopulation<TChromosome, ConstrainedEvaluation<CONSTRAINTS, TResult>>,


@@ 339,7 351,7 @@ impl<const CONSTRAINTS: usize,
    ) -> impl Iterator<Item = usize> {
        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<const CONSTRAINTS: usize,
            .into_iter()
    }
}

pub fn stochastic_ranking_evolution_algorithm
    <TChromosome: Clone,
     TResult: PartialOrd + std::ops::Mul<Output = TResult> + std::ops::AddAssign + Copy + Default,
     const DParents: usize,
     const CONSTRAINTS: usize,
     TFitness: FitnessFunction<In = TChromosome, Out = TResult>,
     TSelection: Selection<(), usize>,
     TPairing: Pairing<DParents, Chromosome = TChromosome, Out = ConstrainedEvaluation<CONSTRAINTS, TResult>>,
     TCrossover: Crossover<DParents, Chromosome = TChromosome, Out = ConstrainedEvaluation<CONSTRAINTS, TResult>>,
     TReplacement: Replacement<TChromosome, ConstrainedEvaluation<CONSTRAINTS, TResult>>,
     TConstraint: ConstraintFunction<Chromosome = TChromosome, Out = TResult>,
     TPerturbation: PerturbationOperator<Chromosome = TChromosome>>(
        initial_population: Population<TChromosome>,
        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<ConstrainedEvaluation<CONSTRAINTS, TResult>> + BetterThanOperator<TResult>),
        // TODO: termination condition
        iterations: usize,
        rng: &mut dyn RngCore,
        // mut evolutionary_strategy: impl FnMut(
        //     usize,
        //     &EvolutionStats<TChromosome, TResult>,
        //     &EvaluatedPopulation<TChromosome, TResult>,

        //     &mut TPairing,
        //     &mut TCrossover,
        //     &mut TPerturbation,
        //     &mut TReplacement,
        //     &mut ConstrainedEvalFitness<CONSTRAINTS, TChromosome, TResult, TFitness, TConstraint>,
        // ),
    ) -> Result<(EvolutionResult<TChromosome, TResult>, Vec<f64>), Box<dyn Error>>
{
    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
               ))
}