~ruther/ctu-fee-eoa

7adc5812e51c320898762ce9611540bd5f79b997 — Rutherther a month ago 4ef3d98
refactor: pass rng as argument

Instead of having the Rng stored
inside the structs, pass it through
the functions. This means it's no longer
necessary to pass perturbations etc. as mutable.
M codes/eoa_lib/src/crossover.rs => codes/eoa_lib/src/crossover.rs +18 -19
@@ 10,16 10,16 @@ pub trait Crossover {
    type Out;

    fn crossover(
        &mut self,
        &self,
        parents: &EvaluatedPopulation<Self::Chromosome, Self::Out>,
        pairs: impl Iterator<Item = ParentPairing>
        pairs: impl Iterator<Item = ParentPairing>,
        rng: &mut dyn RngCore
    ) -> Population<Self::Chromosome>;
}

pub struct BinaryOnePointCrossover<D: Dim, TOutput> {
    _phantom1: PhantomData<D>,
    _phantom2: PhantomData<TOutput>,
    rng: Box<dyn RngCore>
    _phantom2: PhantomData<TOutput>
}

impl<D: Dim, TOutput> BinaryOnePointCrossover<D, TOutput>


@@ 28,15 28,14 @@ where
{
    pub fn new() -> Self {
        Self {
            rng: Box::new(rand::rng()),
            _phantom1: PhantomData,
            _phantom2: PhantomData,
        }
    }

    fn find_cross_point(&mut self, chromosome: &BinaryString<D>) -> usize {
    fn find_cross_point(&self, chromosome: &BinaryString<D>, rng: &mut dyn RngCore) -> usize {
        let (min, max) = (0, chromosome.vec.len());
        self.rng.random_range(min..max)
        rng.random_range(min..max)
    }
}



@@ 52,9 51,10 @@ where
    type Out = TOutput;

    fn crossover(
        &mut self,
        &self,
        population: &EvaluatedPopulation<Self::Chromosome, Self::Out>,
        pairs: impl Iterator<Item = ParentPairing>
        pairs: impl Iterator<Item = ParentPairing>,
        rng: &mut dyn RngCore
    ) -> Population<Self::Chromosome> {

        let chromosome = &population.population[0].chromosome.vec;


@@ 73,7 73,7 @@ where
                chromosome2
            ) = (&parent1.chromosome, &parent2.chromosome);

            let cross_point = self.find_cross_point(&population.population[0].chromosome);
            let cross_point = self.find_cross_point(&population.population[0].chromosome, rng);

            offsprings.push(BinaryString::from_ovector(
                chromosome1.vec.zip_zip_map(


@@ 91,8 91,7 @@ where
pub struct OVectorOnePointCrossover<D: Dim, T: Scalar, TOutput> {
    _phantom1: PhantomData<D>,
    _phantom2: PhantomData<TOutput>,
    _phantom3: PhantomData<T>,
    rng: Box<dyn RngCore>
    _phantom3: PhantomData<T>
}

impl<D: Dim, T, TOutput> OVectorOnePointCrossover<D, T, TOutput>


@@ 102,16 101,15 @@ where
{
    pub fn new() -> Self {
        Self {
            rng: Box::new(rand::rng()),
            _phantom1: PhantomData,
            _phantom2: PhantomData,
            _phantom3: PhantomData,
        }
    }

    fn find_cross_point(&mut self, chromosome: &OVector<T, D>) -> usize {
    fn find_cross_point(&self, chromosome: &OVector<T, D>, rng: &mut dyn RngCore) -> usize {
        let (min, max) = (0, chromosome.len());
        self.rng.random_range(min..max)
        rng.random_range(min..max)
    }
}



@@ 125,9 123,10 @@ where
    type Out = TOutput;

    fn crossover(
        &mut self,
        &self,
        population: &EvaluatedPopulation<Self::Chromosome, Self::Out>,
        pairs: impl Iterator<Item = ParentPairing>
        pairs: impl Iterator<Item = ParentPairing>,
        rng: &mut dyn RngCore
    ) -> Population<Self::Chromosome> {

        let chromosome = &population.population[0].chromosome;


@@ 146,7 145,7 @@ where
                chromosome2
            ) = (&parent1.chromosome, &parent2.chromosome);

            let cross_point = self.find_cross_point(&population.population[0].chromosome);
            let cross_point = self.find_cross_point(&population.population[0].chromosome, rng);

            offsprings.push(
                chromosome1.zip_zip_map(


@@ 158,4 157,4 @@ where

        Population::from_vec(offsprings)
    }
}
}
\ No newline at end of file

M codes/eoa_lib/src/evolution.rs => codes/eoa_lib/src/evolution.rs +24 -19
@@ 1,4 1,5 @@
use std::error::Error;
use rand::RngCore;

use crate::{comparison::BetterThanOperator, crossover::Crossover, fitness::FitnessFunction, pairing::Pairing, perturbation::PerturbationOperator, replacement::{EvaluatedChromosome, EvaluatedPopulation, Population, Replacement}, selection::Selection};



@@ 31,14 32,15 @@ pub fn evolution_algorithm<TChromosome: Clone, TResult: Clone>(
    initial_population: Population<TChromosome>,
    parents_count: usize,
    fitness: &impl FitnessFunction<In = TChromosome, Out = TResult>,
    selection: &mut impl Selection<TChromosome, TResult>,
    selection: &impl Selection<TChromosome, TResult>,
    pairing: &mut impl Pairing<Chromosome = TChromosome, Out = TResult>,
    crossover: &mut impl Crossover<Chromosome = TChromosome, Out = TResult>,
    perturbation: &mut impl PerturbationOperator<Chromosome = TChromosome>,
    replacement: &mut impl Replacement<TChromosome, TResult>,
    crossover: &impl Crossover<Chromosome = TChromosome, Out = TResult>,
    perturbation: &impl PerturbationOperator<Chromosome = TChromosome>,
    replacement: &impl Replacement<TChromosome, TResult>,
    better_than: &impl BetterThanOperator<TResult>,
    // TODO: termination condition
    iterations: usize
    iterations: usize,
    rng: &mut dyn RngCore,
) -> Result<EvolutionResult<TChromosome, TResult>, Box<dyn Error>> {
    let mut current_population = initial_population.evaluate(fitness)?;



@@ 67,21 69,21 @@ pub fn evolution_algorithm<TChromosome: Clone, TResult: Clone>(
        }

        // Selection
        let parents = selection.select(parents_count, &current_population, better_than);
        let parent_pairings = pairing.pair(&current_population, parents);
        let parents = selection.select(parents_count, &current_population, better_than, rng).collect::<Vec<_>>();
        let parent_pairings = pairing.pair(&current_population, parents.into_iter());

        // Crossover
        let mut offsprings = crossover.crossover(&current_population, parent_pairings);
        let mut offsprings = crossover.crossover(&current_population, parent_pairings, rng);

        // Mutation
        for offspring in offsprings.iter_mut() {
            perturbation.perturb(offspring);
            perturbation.perturb(offspring, rng);
        }

        let evaluated_offsprings = offsprings.evaluate(fitness)?;

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

    let best_candidate = last_best_candidate.evaluated_chromosome.clone();


@@ 99,7 101,7 @@ pub fn evolution_algorithm<TChromosome: Clone, TResult: Clone>(
pub mod tests {
    use nalgebra::Const;

    use crate::{binary_string::BinaryString, comparison::MinimizingOperator, crossover::BinaryOnePointCrossover, fitness::one_max::OneMax, initializer::{Initializer, RandomInitializer}, pairing::AdjacentPairing, perturbation::{BinaryStringBitPerturbation, MutationPerturbation}, replacement::{BestReplacement, Population, TournamentReplacement}, selection::{BestSelection, TournamentSelection}};
    use crate::{binary_string::BinaryString, comparison::MinimizingOperator, crossover::BinaryOnePointCrossover, fitness::one_max::OneMax, initializer::{Initializer, RandomInitializer}, pairing::AdjacentPairing, perturbation::{BinaryStringBitPerturbation, MutationPerturbation}, replacement::{BestReplacement, Population}, selection::TournamentSelection};

    use super::evolution_algorithm;



@@ 109,28 111,31 @@ pub mod tests {
        let optimum = BinaryString::<Const<D>>::new(vec![0; D]);
        let one_max = OneMax::<Const<D>>::new();

        let mut initializer = RandomInitializer::<Const<D>, BinaryString::<Const<D>>>::new_binary();
        let initializer = RandomInitializer::<Const<D>, BinaryString::<Const<D>>>::new_binary();
        let population_size = 512;

        let population = Population::from_iterator(
            initializer.initialize(Const::<D>, population_size)
        let mut rng_init = rand::rng();
        let population = Population::from_vec(
            initializer.initialize(Const::<D>, population_size, &mut rng_init)
        );

        let mut rng = rand::rng();
        let result = evolution_algorithm(
            population,
            population_size / 4,
            &one_max,
            // TODO: tournament should somehow accept sorting?
            // TODO: deterministic and nondeterministic tournament ordering
            &mut TournamentSelection::new(3, 0.8),
            &TournamentSelection::new(3, 0.8),
            &mut AdjacentPairing::new(),
            &mut BinaryOnePointCrossover::new(),
            &mut MutationPerturbation::new(
            &BinaryOnePointCrossover::new(),
            &MutationPerturbation::new(
                Box::new(BinaryStringBitPerturbation::new(0.05)),
                0.1),
            &mut BestReplacement::new(),
            &BestReplacement::new(),
            &MinimizingOperator,
            1000
            1000,
            &mut rng
        ).unwrap();

        println!("{:?}", result.stats.best_candidates

M codes/eoa_lib/src/initializer/mod.rs => codes/eoa_lib/src/initializer/mod.rs +10 -14
@@ 4,10 4,9 @@ use rand::RngCore;
use crate::{binary_string::BinaryString, bounded::{Bounded, BoundedBinaryString}};

pub trait Initializer<D: Dim, T> {
    fn initialize_single(&mut self, size: D) -> T;
    fn initialize(&mut self, size: D, count: usize) -> impl Iterator<Item = T> {
        let size = size;
        (0..count).map(move |_| self.initialize_single(size))
    fn initialize_single(&self, size: D, rng: &mut dyn RngCore) -> T;
    fn initialize(&self, size: D, count: usize, rng: &mut dyn RngCore) -> Vec<T> {
        (0..count).map(|_| self.initialize_single(size, rng)).collect()
    }
}



@@ 26,9 25,9 @@ where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    fn initialize_single(&mut self, size: D) -> BinaryString<D> {
    fn initialize_single(&self, size: D, rng: &mut dyn RngCore) -> BinaryString<D> {
        BinaryString::<D>::from_ovector(
            <Self as Initializer<D, OVector<i8, D>>>::initialize_single(self, size)
            <Self as Initializer<D, OVector<i8, D>>>::initialize_single(self, size, rng)
        )
    }
}


@@ 39,20 38,18 @@ where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    fn initialize_single(&mut self, size: D) -> OVector<T, D> {
    fn initialize_single(&self, size: D, _rng: &mut dyn RngCore) -> OVector<T, D> {
        OVector::<T, D>::from_element_generic(size, U1, Default::default())
    }
}

pub struct RandomInitializer<D: Dim, T> {
    rng: Box<dyn RngCore>,
    bounded: Box<dyn Bounded<D, Item = T>>
}

impl<T, D: Dim> RandomInitializer<D, T> {
    pub fn new(bounded: Box<dyn Bounded<D, Item = T>>) -> Self {
        Self {
            rng: Box::new(rand::rng()),
            bounded
        }
    }


@@ 65,7 62,6 @@ where
{
    pub fn new_binary() -> Self {
        Self {
            rng: Box::new(rand::rng()),
            bounded: Box::new(BoundedBinaryString::unbounded())
        }
    }


@@ 76,8 72,8 @@ where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    fn initialize_single(&mut self, size: D) -> BinaryString<D> {
        self.bounded.next_random(size, &mut self.rng)
    fn initialize_single(&self, size: D, rng: &mut dyn RngCore) -> BinaryString<D> {
        self.bounded.next_random(size, rng)
    }
}



@@ 87,7 83,7 @@ where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    fn initialize_single(&mut self, size: D) -> OVector<T, D> {
        self.bounded.next_random(size, &mut self.rng)
    fn initialize_single(&self, size: D, rng: &mut dyn RngCore) -> OVector<T, D> {
        self.bounded.next_random(size, rng)
    }
}

M codes/eoa_lib/src/local_search/mod.rs => codes/eoa_lib/src/local_search/mod.rs +24 -6
@@ 1,5 1,6 @@
use std::error::Error;
use std::fmt::Debug;
use rand::RngCore;
use crate::binary_string::{BinaryString, BinaryStringConversionError};
use crate::evolutionary_strategy::{EvolutionaryStrategy, IdentityStrategy};
use crate::fitness::FitnessFunction;


@@ 103,7 104,8 @@ pub fn local_search_first_improving<
    terminating_condition: &mut TTerminatingCondition,
    perturbation_operator: &mut TPerturbationOperator,
    better_than_operator: &TBetterThanOperator,
    initial: &TInput
    initial: &TInput,
    rng: &mut dyn RngCore
) -> Result<LocalSearchResult<TInput, TResult>, Box<dyn Error>>
where
    TResult: Clone,


@@ 119,7 121,8 @@ where
        perturbation_operator,
        better_than_operator,
        &mut IdentityStrategy,
        initial
        initial,
        rng
    )
}



@@ 130,7 133,8 @@ pub fn local_search_first_improving_evolving<
    perturbation_operator: &mut TPerturbationOperator,
    better_than_operator: &TBetterThanOperator,
    evolutionary_strategy: &mut TEvolutionaryStrategy,
    initial: &TInput
    initial: &TInput,
    rng: &mut dyn RngCore
) -> Result<LocalSearchResult<TInput, TResult>, Box<dyn Error>>
where
    TResult: Clone,


@@ 152,7 156,7 @@ where

    while !terminating_condition.should_terminate(&best_candidate, &stats, cycle) {
        let mut perturbed = best_candidate.pos.clone();
        perturbation_operator.perturb(&mut perturbed);
        perturbation_operator.perturb(&mut perturbed, rng);
        let perturbed_fit = fit.fit(&perturbed)?;

        // Minimize


@@ 303,6 307,7 @@ pub mod tests {
        let sphere = Sphere::new(optimum_real);
        let sphere_wrapped = BinaryFitnessWrapper::new(sphere, min.clone(), max.clone());

        let mut rng = rand::rng();
        let result = local_search_first_improving(
            &sphere_wrapped,
            &mut


@@ 315,6 320,7 @@ pub mod tests {
            &mut BinaryStringBitPerturbation::new(0.3),
            &MinimizingOperator::new(),
            &BinaryString::new(vec![1; 10]),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);


@@ 337,6 343,7 @@ pub mod tests {
        let optimum = SVector::<f64, 2>::repeat(4.0);
        let sphere = Sphere::new(optimum);

        let mut rng = rand::rng();
        let result = local_search_first_improving_evolving(
            &sphere,
            &mut


@@ 350,6 357,7 @@ pub mod tests {
            &MinimizingOperator::new(),
            &mut IdentityStrategy,
            &SVector::<f64, 2>::repeat(-5.0),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);


@@ 370,6 378,7 @@ pub mod tests {
        let one_max = OneMax::<U10>::new();
        let optimum = BinaryString::<U10>::new(vec![0; 10]);

        let mut rng = rand::rng();
        let result = local_search_first_improving(
            &one_max,
            &mut


@@ 382,6 391,7 @@ pub mod tests {
            &mut BinaryStringBitPerturbation::new(0.3),
            &MinimizingOperator::new(),
            &BinaryString::<U10>::new(vec![1; 10]),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);


@@ 438,6 448,7 @@ pub mod tests {
        let max = SVector::<f64, 2>::from_element(15.0);
        let rosenbrock_wrapped = BinaryFitnessWrapper::new(rosenbrock, min, max);

        let mut rng = rand::rng();
        let result = local_search_first_improving(
            &rosenbrock_wrapped,
            &mut


@@ 450,6 461,7 @@ pub mod tests {
            &mut BinaryStringBitPerturbation::new(0.1),
            &MinimizingOperator::new(),
            &BinaryString::new(vec![0; 10]),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);


@@ 473,11 485,12 @@ pub mod tests {
        let max = SVector::<f64, 2>::from_vec(vec![10.0, 10.0]);
        let min = -SVector::<f64, 2>::from_vec(vec![10.0, 10.0]);

        let mut initializer =
        let initializer =
            RandomInitializer::<U2, OVector<f64, U2>>::new(Box::new(BoundedOVector::<U2>::new(min, max)));

        let linear = Linear::new(7.0, SVector::<f64, 2>::from_vec(vec![0.2, -0.5]));

        let mut rng = rand::rng();
        let result = local_search_first_improving(
            &linear,
            &mut


@@ 493,7 506,8 @@ pub mod tests {
                max,
                BoundedPerturbationStrategy::Retry(10)),
            &MinimizingOperator::new(),
            &initializer.initialize_single(U2),
            &initializer.initialize_single(U2, &mut rng),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);


@@ 519,6 533,7 @@ pub mod tests {

        let linear = Linear::new(7.0, SVector::<f64, 2>::from_vec(vec![0.2, -0.5]));

        let mut rng = rand::rng();
        let result = local_search_first_improving(
            &linear,
            &mut


@@ 535,6 550,7 @@ pub mod tests {
                BoundedPerturbationStrategy::Retry(10)),
            &MinimizingOperator::new(),
            &SVector::<f64, 2>::zeros(),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);


@@ 560,6 576,7 @@ pub mod tests {

        let linear = Linear::new(7.0, SVector::<f64, 2>::from_vec(vec![0.2, -0.5]));

        let mut rng = rand::rng();
        let result = local_search_first_improving_evolving(
            &linear,
            &mut


@@ 577,6 594,7 @@ pub mod tests {
            &MinimizingOperator::new(),
            &mut OneToFiveStrategy,
            &SVector::<f64, 2>::zeros(),
            &mut rng,
        ).unwrap();

        println!("{:?}", result);

M codes/eoa_lib/src/perturbation/mod.rs => codes/eoa_lib/src/perturbation/mod.rs +22 -31
@@ 9,11 9,10 @@ use crate::binary_string::BinaryString;
pub trait PerturbationOperator {
    type Chromosome;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome);
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore);
}

pub struct BinaryStringBitPerturbation<D> {
    rng: Box<dyn RngCore>,
    p: f64,
    _phantom: PhantomData<D>
}


@@ 21,7 20,6 @@ pub struct BinaryStringBitPerturbation<D> {
impl<D> BinaryStringBitPerturbation<D> {
    pub fn new(p: f64) -> Self {
        Self {
            rng: Box::new(rand::rng()),
            p,
            _phantom: PhantomData
        }


@@ 35,14 33,13 @@ where
{
    type Chromosome = BinaryString<D>;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        chromosome.perturb(&mut self.rng, self.p);
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        chromosome.perturb(rng, self.p);
    }
}

pub struct RandomDistributionPerturbation<const LEN: usize, TDistribution: Distribution<f64>> {
    distribution: TDistribution,
    rng: Box<dyn RngCore>,
    parameter: f64
}



@@ 50,7 47,6 @@ impl<const LEN: usize> RandomDistributionPerturbation<LEN, Normal<f64>> {
    pub fn normal(std_dev: f64) -> Result<Self, NormalError> {
        Ok(Self {
            distribution: Normal::new(0.0, std_dev)?,
            rng: Box::new(rand::rng()),
            parameter: std_dev
        })
    }


@@ 70,7 66,6 @@ impl<const LEN: usize> RandomDistributionPerturbation<LEN, Uniform<f64>> {
    pub fn uniform(range: f64) -> Result<Self, uniform::Error> {
        Ok(Self {
            distribution: Uniform::new(-range/2.0, range/2.0)?,
            rng: Box::new(rand::rng()),
            parameter: range,
        })
    }


@@ 89,21 84,19 @@ impl<const LEN: usize> RandomDistributionPerturbation<LEN, Uniform<f64>> {
impl<TDistribution: Distribution<f64>, const LEN: usize> PerturbationOperator for RandomDistributionPerturbation<LEN, TDistribution> {
    type Chromosome = SVector<f64, LEN>;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        *chromosome += Self::Chromosome::zeros().map(|_| self.distribution.sample(&mut self.rng));
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        *chromosome += Self::Chromosome::zeros().map(|_| self.distribution.sample(rng));
    }
}

pub struct PatternPerturbation<const LEN: usize> {
    d: f64,
    rng: Box<dyn RngCore>
    d: f64
}

impl<const LEN: usize> PatternPerturbation<LEN> {
    pub fn new(d: f64) -> Self {
        Self {
            d,
            rng: Box::new(rand::rng())
            d
        }
    }
}


@@ 111,11 104,11 @@ impl<const LEN: usize> PatternPerturbation<LEN> {
impl<const LEN: usize> PerturbationOperator for PatternPerturbation<LEN> {
    type Chromosome = SVector::<f64, LEN>;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        // 1. Choose dimension
        let idx = self.rng.random_range(0..LEN);
        let idx = rng.random_range(0..LEN);
        // 2. Direction
        let d = if self.rng.random_bool(0.5) {
        let d = if rng.random_bool(0.5) {
            self.d
        } else {
            -self.d


@@ 178,9 171,9 @@ impl<const LEN: usize, T: PerturbationOperator<Chromosome = SVector<f64, LEN>>> 
        chromosome
    }

    fn retry_perturb(&mut self, chromosome: &mut SVector<f64, LEN>, retries: Option<usize>) {
    fn retry_perturb(&self, chromosome: &mut SVector<f64, LEN>, retries: Option<usize>, rng: &mut dyn RngCore) {
        let mut perturbed = chromosome.clone();
        self.perturbation.perturb(&mut perturbed);
        self.perturbation.perturb(&mut perturbed, rng);

        if self.within_bounds(&perturbed) {
            *chromosome = perturbed;


@@ 191,7 184,7 @@ impl<const LEN: usize, T: PerturbationOperator<Chromosome = SVector<f64, LEN>>> 
            Some(0) | None => *chromosome = self.bound(perturbed),
            Some(retries) => {
                *chromosome = perturbed;
                self.retry_perturb(chromosome, Some(retries - 1));
                self.retry_perturb(chromosome, Some(retries - 1), rng);
            }
        }
    }


@@ 203,10 196,10 @@ where
{
    type Chromosome = SVector<f64, LEN>;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        match self.strategy {
            BoundedPerturbationStrategy::Trim => self.retry_perturb(chromosome, None),
            BoundedPerturbationStrategy::Retry(retries) => self.retry_perturb(chromosome, Some(retries))
            BoundedPerturbationStrategy::Trim => self.retry_perturb(chromosome, None, rng),
            BoundedPerturbationStrategy::Retry(retries) => self.retry_perturb(chromosome, Some(retries), rng)
        }
    }
}


@@ 214,7 207,6 @@ where
/// Perform given perturbation only with given probability
pub struct MutationPerturbation<T> {
    perturbation: Box<dyn PerturbationOperator<Chromosome = T>>,
    rng: Box<dyn RngCore>,
    probability: f64
}



@@ 222,7 214,6 @@ impl<T> MutationPerturbation<T> {
    pub fn new(perturbation: Box<dyn PerturbationOperator<Chromosome = T>>, probability: f64) -> Self {
        Self {
            perturbation,
            rng: Box::new(rand::rng()),
            probability
        }
    }


@@ 231,9 222,9 @@ impl<T> MutationPerturbation<T> {
impl<T> PerturbationOperator for MutationPerturbation<T> {
    type Chromosome = T;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        if self.rng.random_bool(self.probability) {
            self.perturbation.perturb(chromosome);
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        if rng.random_bool(self.probability) {
            self.perturbation.perturb(chromosome, rng);
        }
    }
}


@@ 253,9 244,9 @@ impl<T> CombinedPerturbation<T> {
impl<T> PerturbationOperator for CombinedPerturbation<T> {
    type Chromosome = T;

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        for perturbation in self.perturbations.iter_mut() {
            perturbation.perturb(chromosome);
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        for perturbation in self.perturbations.iter() {
            perturbation.perturb(chromosome, rng);
        }
    }
}

M codes/eoa_lib/src/replacement.rs => codes/eoa_lib/src/replacement.rs +19 -20
@@ 134,10 134,11 @@ impl<TChromosome, TResult: Copy> EvaluatedPopulation<TChromosome, TResult> {

pub trait Replacement<TChromosome, TResult> {
    fn replace(
        &mut self,
        &self,
        parents_evaluations: EvaluatedPopulation<TChromosome, TResult>,
        offsprings_evaluations: EvaluatedPopulation<TChromosome, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
        better_than: &dyn BetterThanOperator<TResult>,
        rng: &mut dyn RngCore
    ) -> EvaluatedPopulation<TChromosome, TResult>;
}



@@ 150,10 151,11 @@ impl BestReplacement {

impl<TChromosome, TResult: Copy + Debug> Replacement<TChromosome, TResult> for BestReplacement {
    fn replace(
        &mut self,
        &self,
        parents_evaluations: EvaluatedPopulation<TChromosome, TResult>,
        offsprings_evaluations: EvaluatedPopulation<TChromosome, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
        better_than: &dyn BetterThanOperator<TResult>,
        _rng: &mut dyn RngCore
    ) -> EvaluatedPopulation<TChromosome, TResult> {
        let count = parents_evaluations.population.len();
        let mut population = parents_evaluations;


@@ 177,10 179,11 @@ impl<TChromosome, TResult: Copy + Debug> Replacement<TChromosome, TResult> for B
pub struct GenerationalReplacement;
impl<TInput, TResult> Replacement<TInput, TResult> for GenerationalReplacement {
    fn replace(
        &mut self,
        &self,
        parents: EvaluatedPopulation<TInput, TResult>,
        mut offsprings: EvaluatedPopulation<TInput, TResult>,
        _: &dyn BetterThanOperator<TResult>
        _: &dyn BetterThanOperator<TResult>,
        _rng: &mut dyn RngCore
    ) -> EvaluatedPopulation<TInput, TResult> {
        let count = parents.population.len();
        if count == offsprings.population.len() {


@@ 198,24 201,21 @@ impl<TInput, TResult> Replacement<TInput, TResult> for GenerationalReplacement {
    }
}

pub struct RandomReplacement {
    rng: Box<dyn RngCore>
}
pub struct RandomReplacement;

impl RandomReplacement {
    pub fn new() -> Self {
        Self {
            rng: Box::new(rand::rng())
        }
        Self
    }
}

impl<TInput, TResult> Replacement<TInput, TResult> for RandomReplacement {
    fn replace(
        &mut self,
        &self,
        parents: EvaluatedPopulation<TInput, TResult>,
        offsprings: EvaluatedPopulation<TInput, TResult>,
        _: &dyn BetterThanOperator<TResult>
        _: &dyn BetterThanOperator<TResult>,
        rng: &mut dyn RngCore
    ) -> EvaluatedPopulation<TInput, TResult> {
        let count = parents.population.len();



@@ 223,7 223,7 @@ impl<TInput, TResult> Replacement<TInput, TResult> for RandomReplacement {
            parents.deconstruct()
                .into_iter()
                .chain(offsprings.deconstruct().into_iter())
                .choose_multiple(&mut self.rng, count))
                .choose_multiple(rng, count))
    }
}



@@ 246,19 246,18 @@ impl TournamentReplacement {

impl<TInput, TResult: Copy + Debug> Replacement<TInput, TResult> for TournamentReplacement {
    fn replace(
        &mut self,
        &self,
        parents: EvaluatedPopulation<TInput, TResult>,
        offsprings: EvaluatedPopulation<TInput, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
        better_than: &dyn BetterThanOperator<TResult>,
        rng: &mut dyn RngCore
    ) -> EvaluatedPopulation<TInput, TResult> {
        let count = parents.population.len();
        let mut population = parents;
        population.join(offsprings);

        self.evaluation_pool.clear();

        // TODO: use a pool instead of allocating vector every run of this function
        let selected = self.selection.select(count, &population, better_than)
        let selected = self.selection.select(count, &population, better_than, rng)
            .collect::<Vec<_>>();

        let population = population.deconstruct();

M codes/eoa_lib/src/selection.rs => codes/eoa_lib/src/selection.rs +15 -13
@@ 4,10 4,11 @@ use std::fmt::Debug;
use crate::{comparison::BetterThanOperator, replacement::EvaluatedPopulation};

pub trait Selection<TChromosome, TResult> {
    fn select(&mut self,
    fn select(&self,
              count: usize,
              evaluations: &EvaluatedPopulation<TChromosome, TResult>,
              better_than: &dyn BetterThanOperator<TResult>
              better_than: &dyn BetterThanOperator<TResult>,
              rng: &mut dyn RngCore
    ) -> impl Iterator<Item = usize>;
}



@@ 19,10 20,11 @@ impl BestSelection {
}

impl<TChromosome, TResult: Copy> Selection<TChromosome, TResult> for BestSelection {
    fn select(&mut self,
    fn select(&self,
              count: usize,
              evaluations: &EvaluatedPopulation<TChromosome, TResult>,
              better_than: &dyn BetterThanOperator<TResult>
              better_than: &dyn BetterThanOperator<TResult>,
              _rng: &mut dyn RngCore
    ) -> impl Iterator<Item = usize> {
        let mut idxs = (0..evaluations.population.len())
            .collect::<Vec<_>>();


@@ 36,7 38,6 @@ impl<TChromosome, TResult: Copy> Selection<TChromosome, TResult> for BestSelecti
}

pub struct TournamentSelection {
    rng: Box<dyn RngCore>,
    p: f64,
    k: usize
}


@@ 47,24 48,24 @@ impl TournamentSelection {
        assert!(k > 0);

        Self {
            rng: Box::new(rand::rng()),
            p,
            k
        }
    }

    fn tournament<TChromosome, TResult: Debug + Copy>(
        &mut self,
        &self,
        idxs: &mut Vec<usize>,
        evaluations: &EvaluatedPopulation<TChromosome, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
        better_than: &dyn BetterThanOperator<TResult>,
        rng: &mut dyn RngCore
    ) -> usize {
        idxs.sort_unstable_by(|&i, &j| better_than.ordering(
            &evaluations.population[i].evaluation,
            &evaluations.population[j].evaluation)
        );

        let mut p_selector = self.rng.random_range(0.0..=1.0f64);
        let mut p_selector = rng.random_range(0.0..=1.0f64);
        let p = self.p;
        let k = self.k;



@@ 90,18 91,19 @@ impl TournamentSelection {

impl<TChromosome, TResult: Copy + Debug> Selection<TChromosome, TResult> for TournamentSelection {
    fn select(
        &mut self,
        &self,
        count: usize,
        evaluations: &EvaluatedPopulation<TChromosome, TResult>,
        better_than: &dyn BetterThanOperator<TResult>
        better_than: &dyn BetterThanOperator<TResult>,
        rng: &mut dyn RngCore
    ) -> impl Iterator<Item = usize> {
        // Let's reuse a single vector for the indices
        let mut k_selected_idxs = vec![0; self.k];
        (0..count).map(move |_| {
            // Choose k. Do not care if already selected previously.
            (0..evaluations.population.len()).choose_multiple_fill(&mut self.rng, &mut k_selected_idxs);
            (0..evaluations.population.len()).choose_multiple_fill(rng, &mut k_selected_idxs);
            // Tournament between the k
            let index = self.tournament(&mut k_selected_idxs, evaluations, better_than);
            let index = self.tournament(&mut k_selected_idxs, evaluations, better_than, rng);
            index
        })
    }

M codes/tsp_hw01/src/tsp.rs => codes/tsp_hw01/src/tsp.rs +11 -17
@@ 139,8 139,7 @@ where
    D: Dim,
    DefaultAllocator: Allocator<D, D>,
{
    _phantom: PhantomData<D>,
    rng: Box<dyn RngCore>
    _phantom: PhantomData<D>
}

impl<D> Initializer<D, NodePermutation<D>> for TSPRandomInitializer<D>


@@ 149,18 148,17 @@ where
    DefaultAllocator: Allocator<D, D>,
    DefaultAllocator: Allocator<D>,
{
    fn initialize_single(&mut self, size: D) -> NodePermutation<D> {
    fn initialize_single(&self, size: D, rng: &mut dyn RngCore) -> NodePermutation<D> {
        let len = size.value();
        let mut indices = OVector::<usize, D>::from_iterator_generic(size, U1, 0..len);
        indices.as_mut_slice().shuffle(&mut self.rng);
        indices.as_mut_slice().shuffle(rng);

        NodePermutation { permutation: indices }
    }
}

pub struct SwapPerturbation<D> {
    _phantom: PhantomData<D>,
    rng: Box<dyn RngCore>,
    _phantom: PhantomData<D>
}

impl<D> PerturbationOperator for SwapPerturbation<D>


@@ 171,20 169,16 @@ where
{
    type Chromosome = NodePermutation<D>;

    fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
        let first = self.rng.random_range(0..=chromosome.permutation.len());
        let second = self.rng.random_range(0..=chromosome.permutation.len());

        let mut new = chromosome.clone();
    fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) {
        let first = rng.random_range(0..=chromosome.permutation.len());
        let second = rng.random_range(0..=chromosome.permutation.len());

        (
            new.permutation[first],
            new.permutation[second]
            chromosome.permutation[first],
            chromosome.permutation[second]
        ) = (
            new.permutation[second],
            new.permutation[first]
            chromosome.permutation[second],
            chromosome.permutation[first]
        );

        new
    }
}