From 0420a0ccdbf94339e78c75a06d8350ff5796811b Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 8 Nov 2025 19:39:05 +0100 Subject: [PATCH] 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. --- codes/eoa_lib/src/evolution.rs | 98 ++++++++++++++++---------------- codes/eoa_lib/src/fitness/mod.rs | 15 ++++- codes/eoa_lib/src/population.rs | 28 +-------- 3 files changed, 64 insertions(+), 77 deletions(-) diff --git a/codes/eoa_lib/src/evolution.rs b/codes/eoa_lib/src/evolution.rs index cbb350ed5f7d9e7b72c891b65b34abc286ab308e..0126ff5902b0523cdd35e4310137f27116338797 100644 --- a/codes/eoa_lib/src/evolution.rs +++ b/codes/eoa_lib/src/evolution.rs @@ -72,53 +72,53 @@ pub fn evolution_algorithm best_candidates: vec![] }; - fn get_fitness_fn( + fn apply_new_eval( current_evaluation: &mut usize, better_than: &impl BetterThanOperator, fitness: &impl FitnessFunction, current_iteration: &usize, stats: &mut EvolutionStats, + population: &EvaluatedPopulation, last_best_candidate: &mut Option> - ) -> 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, + ¤t_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, + ¤t_population, + &mut last_best_candidate); // Replace current_population = replacement.replace(current_population, evaluated_offsprings, better_than, rng); diff --git a/codes/eoa_lib/src/fitness/mod.rs b/codes/eoa_lib/src/fitness/mod.rs index ef1ede5fa679e822554f2aae9105712dfad63a92..e257dc1da709b3672ff36656a4be80c2fa257938 100644 --- a/codes/eoa_lib/src/fitness/mod.rs +++ b/codes/eoa_lib/src/fitness/mod.rs @@ -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; + fn fit(&self, inp: &Self::In) -> Result; + + fn fit_population(&self, inp: Vec) -> Result>, Self::Err> { + inp + .into_iter() + .map(|chromosome| + Ok(EvaluatedChromosome { + evaluation: self.fit(&chromosome)?, + chromosome, + })) + .collect::, _>>() + } } pub struct BinaryFitnessWrapper diff --git a/codes/eoa_lib/src/population.rs b/codes/eoa_lib/src/population.rs index 686ba771ef2166ea66a232b80486b054d4da244e..826140febdfe4fe0f7611e63d1e3659fb5f926ca 100644 --- a/codes/eoa_lib/src/population.rs +++ b/codes/eoa_lib/src/population.rs @@ -34,13 +34,6 @@ impl Population { ) } - pub fn evaluate_mut(self, func: &mut dyn FnMut(&TChromosome) -> TResult) -> EvaluatedPopulation { - EvaluatedPopulation::evaluate_mut( - self.population, - func - ) - } - pub fn into_iter(self) -> impl Iterator { self.population.into_iter() } @@ -68,25 +61,8 @@ impl EvaluatedPopulation { } pub fn evaluate>(chromosomes: Vec, func: &T) -> Result { - Ok(EvaluatedPopulation::from_vec( - chromosomes.into_iter() - .map(|chromosome| - Ok(EvaluatedChromosome { - evaluation: func.fit(&chromosome)?, - chromosome - })) - .collect::>()?)) - } - - pub fn evaluate_mut(chromosomes: Vec, func: &mut dyn FnMut(&TChromosome) -> TResult) -> Self { - EvaluatedPopulation::from_vec( - chromosomes.into_iter() - .map(|chromosome| - EvaluatedChromosome { - evaluation: func(&chromosome), - chromosome - }) - .collect::>()) + func.fit_population(chromosomes) + .map(|population| Self::from_vec(population)) } pub fn from_vec(vec: Vec>) -> Self {