~ruther/ctu-fee-eoa

0420a0ccdbf94339e78c75a06d8350ff5796811b — Rutherther a month ago 5119e5e
refactor(lib): Allow fitting whole population at once

The fitness function gets fit_population function. The default
implementation is going over fit() one by one, but it can be
reimplemented by specific types.
M codes/eoa_lib/src/evolution.rs => codes/eoa_lib/src/evolution.rs +49 -49
@@ 72,53 72,53 @@ pub fn evolution_algorithm
        best_candidates: vec![]
    };

    fn get_fitness_fn<TChromosome: Clone, TResult: Clone>(
    fn apply_new_eval<TChromosome: Clone, TResult: Clone>(
        current_evaluation: &mut usize,
        better_than: &impl BetterThanOperator<TResult>,
        fitness: &impl FitnessFunction<In = TChromosome, Out = TResult>,
        current_iteration: &usize,
        stats: &mut EvolutionStats<TChromosome, TResult>,
        population: &EvaluatedPopulation<TChromosome, TResult>,
        last_best_candidate: &mut Option<EvolutionCandidate<TChromosome, TResult>>
    ) -> impl FnMut(&TChromosome) -> TResult {
      |chromosome| {
          let evaluation = fitness.fit(chromosome).unwrap();

          if last_best_candidate.is_none() ||
              better_than.better_than(
                  &evaluation,
                  &last_best_candidate.as_ref().unwrap().evaluated_chromosome.evaluation
              ) {
                  let previous_best = std::mem::replace(
                      last_best_candidate,
                      Some(EvolutionCandidate {
                      evaluated_chromosome: EvaluatedChromosome {
                          chromosome: chromosome.clone(),
                          evaluation: evaluation.clone(),
                      },
                      evaluation: *current_evaluation,
                      iteration: *current_iteration
                  }));

                  if let Some(previous_best) = previous_best {
                      stats.best_candidates.push(previous_best);
                  }
              }
          *current_evaluation += 1;

          evaluation
      }
    ) {
        for individual in population.iter() {
            let evaluation = &individual.evaluation;
            let chromosome = &individual.chromosome;

            if last_best_candidate.is_none() ||
                better_than.better_than(
                    evaluation,
                    &last_best_candidate.as_ref().unwrap().evaluated_chromosome.evaluation
                ) {
                    let previous_best = std::mem::replace(
                        last_best_candidate,
                        Some(EvolutionCandidate {
                            evaluated_chromosome: EvaluatedChromosome {
                                chromosome: chromosome.clone(),
                                evaluation: evaluation.clone(),
                            },
                            evaluation: *current_evaluation,
                            iteration: *current_iteration
                        }));

                    if let Some(previous_best) = previous_best {
                        stats.best_candidates.push(previous_best);
                    }
                }
            *current_evaluation += 1;
        }
    }

    let mut current_population = initial_population.evaluate_mut(
        &mut get_fitness_fn(
            &mut current_evaluation,
            better_than,
            fitness,
            &0,
            &mut stats,
            &mut last_best_candidate
        )
    );
    let mut current_population =
        initial_population.evaluate(fitness)?;
    apply_new_eval(
        &mut current_evaluation,
        better_than,
        fitness,
        &0,
        &mut stats,
        &current_population,
        &mut last_best_candidate);

    for iteration in 1..=iterations {
        // Selection


@@ 133,16 133,16 @@ pub fn evolution_algorithm
            perturbation.perturb(offspring, rng);
        }

        let evaluated_offsprings = offsprings.evaluate_mut(
            &mut get_fitness_fn(
                &mut current_evaluation,
                better_than,
                fitness,
                &iteration,
                &mut stats,
                &mut last_best_candidate
            )
        );
        let evaluated_offsprings =
            offsprings.evaluate(fitness)?;
        apply_new_eval(
            &mut current_evaluation,
            better_than,
            fitness,
            &iteration,
            &mut stats,
            &current_population,
          &mut last_best_candidate);

        // Replace
        current_population = replacement.replace(current_population, evaluated_offsprings, better_than, rng);

M codes/eoa_lib/src/fitness/mod.rs => codes/eoa_lib/src/fitness/mod.rs +13 -2
@@ 2,7 2,7 @@ use std::{convert::Infallible, error::Error, marker::PhantomData};

use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector};

use crate::binary_string::{BinaryString, BinaryStringConversionError};
use crate::{binary_string::{BinaryString, BinaryStringConversionError}, population::EvaluatedChromosome};

pub mod labs;
pub mod one_max;


@@ 15,7 15,18 @@ pub trait FitnessFunction {
    type Out;
    type Err: Error + 'static;

    fn fit(self: &Self, inp: &Self::In) -> Result<Self::Out, Self::Err>;
    fn fit(&self, inp: &Self::In) -> Result<Self::Out, Self::Err>;

    fn fit_population(&self, inp: Vec<Self::In>) -> Result<Vec<EvaluatedChromosome<Self::In, Self::Out>>, Self::Err> {
        inp
            .into_iter()
            .map(|chromosome|
                 Ok(EvaluatedChromosome {
                     evaluation: self.fit(&chromosome)?,
                     chromosome,
                 }))
            .collect::<Result<Vec<_>, _>>()
    }
}

pub struct BinaryFitnessWrapper<D, DString, TFitness>

M codes/eoa_lib/src/population.rs => codes/eoa_lib/src/population.rs +2 -26
@@ 34,13 34,6 @@ impl<TChromosome> Population<TChromosome> {
        )
    }

    pub fn evaluate_mut<TResult>(self, func: &mut dyn FnMut(&TChromosome) -> TResult) -> EvaluatedPopulation<TChromosome, TResult> {
        EvaluatedPopulation::evaluate_mut(
            self.population,
            func
        )
    }

    pub fn into_iter(self) -> impl Iterator<Item = TChromosome> {
        self.population.into_iter()
    }


@@ 68,25 61,8 @@ impl<TChromosome, TResult> EvaluatedPopulation<TChromosome, TResult> {
    }

    pub fn evaluate<T: FitnessFunction<In = TChromosome, Out = TResult>>(chromosomes: Vec<TChromosome>, func: &T) -> Result<Self, T::Err> {
        Ok(EvaluatedPopulation::from_vec(
            chromosomes.into_iter()
                .map(|chromosome|
                     Ok(EvaluatedChromosome {
                         evaluation: func.fit(&chromosome)?,
                         chromosome
                     }))
                .collect::<Result<_, _>>()?))
    }

    pub fn evaluate_mut(chromosomes: Vec<TChromosome>, func: &mut dyn FnMut(&TChromosome) -> TResult) -> Self {
        EvaluatedPopulation::from_vec(
            chromosomes.into_iter()
                .map(|chromosome|
                     EvaluatedChromosome {
                         evaluation: func(&chromosome),
                         chromosome
                     })
                .collect::<Vec<_>>())
        func.fit_population(chromosomes)
            .map(|population| Self::from_vec(population))
    }

    pub fn from_vec(vec: Vec<EvaluatedChromosome<TChromosome, TResult>>) -> Self {