~ruther/ctu-fee-eoa

26456170e0bb9f706af4570dddb4a11743576738 — Rutherther a month ago b08d8cf
refactor: do perturbation in place instead of cloning
M codes/eoa_lib/src/binary_string.rs => codes/eoa_lib/src/binary_string.rs +1 -2
@@ 71,10 71,9 @@ where
        &self.vec
    }

    pub fn perturb(mut self, rng: &mut dyn RngCore, p: f64) -> Self
    pub fn perturb(&mut self, rng: &mut dyn RngCore, p: f64)
    {
        self.vec.apply(|c| *c = if rng.random::<f64>() <= p { 1 - *c } else { *c });
        self
    }

    fn to_real_internal<'a, T: DoubleEndedIterator<Item = &'a i8>>(vec: T, len: usize, min: f64, max: f64) -> f64

M codes/eoa_lib/src/evolution.rs => codes/eoa_lib/src/evolution.rs +1 -1
@@ 70,7 70,7 @@ pub fn evolution_algorithm<TChromosome: Clone, TResult: Clone>(

        // Mutation
        for offspring in offsprings.iter_mut() {
            *offspring = perturbation.perturb(offspring);
            perturbation.perturb(offspring);
        }

        let evaluated_offsprings = offsprings.evaluate(fitness)?;

M codes/eoa_lib/src/local_search/mod.rs => codes/eoa_lib/src/local_search/mod.rs +2 -1
@@ 151,7 151,8 @@ where
    let mut cycle: usize = 0;

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

        // Minimize

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

    fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome;
    fn perturb(&mut self, chromosome: &mut Self::Chromosome);
}

pub struct BinaryStringBitPerturbation<D> {


@@ 35,8 35,8 @@ where
{
    type Chromosome = BinaryString<D>;

    fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
        chromosome.clone().perturb(&mut self.rng, self.p)
    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        chromosome.perturb(&mut self.rng, self.p);
    }
}



@@ 89,8 89,8 @@ 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(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
        chromosome + Self::Chromosome::zeros().map(|_| self.distribution.sample(&mut self.rng))
    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        *chromosome += Self::Chromosome::zeros().map(|_| self.distribution.sample(&mut self.rng));
    }
}



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

    fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
        let mut chromosome = chromosome.clone();

    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        // 1. Choose dimension
        let idx = self.rng.random_range(0..LEN);
        // 2. Direction


@@ 125,8 123,6 @@ impl<const LEN: usize> PerturbationOperator for PatternPerturbation<LEN> {

        // Apply
        chromosome[idx] += d;

        chromosome
    }
}



@@ 182,16 178,21 @@ impl<const LEN: usize, T: PerturbationOperator<Chromosome = SVector<f64, LEN>>> 
        chromosome
    }

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

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

        match retries {
            Some(0) | None => self.bound(perturbed),
            Some(retries) => self.retry_perturb(chromosome, Some(retries - 1))
            Some(0) | None => *chromosome = self.bound(perturbed),
            Some(retries) => {
                *chromosome = perturbed;
                self.retry_perturb(chromosome, Some(retries - 1));
            }
        }
    }
}


@@ 202,7 203,7 @@ where
{
    type Chromosome = SVector<f64, LEN>;

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


@@ 211,13 212,13 @@ where
}

/// Perform given perturbation only with given probability
pub struct MutationPerturbation<T: Clone> {
pub struct MutationPerturbation<T> {
    perturbation: Box<dyn PerturbationOperator<Chromosome = T>>,
    rng: Box<dyn RngCore>,
    probability: f64
}

impl<T: Clone> MutationPerturbation<T> {
impl<T> MutationPerturbation<T> {
    pub fn new(perturbation: Box<dyn PerturbationOperator<Chromosome = T>>, probability: f64) -> Self {
        Self {
            perturbation,


@@ 227,23 228,21 @@ impl<T: Clone> MutationPerturbation<T> {
    }
}

impl<T: Clone> PerturbationOperator for MutationPerturbation<T> {
impl<T> PerturbationOperator for MutationPerturbation<T> {
    type Chromosome = T;

    fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        if self.rng.random_bool(self.probability) {
            self.perturbation.perturb(chromosome)
        } else {
            chromosome.clone()
            self.perturbation.perturb(chromosome);
        }
    }
}

pub struct CombinedPerturbation<T: Clone> {
pub struct CombinedPerturbation<T> {
    perturbations: Vec<Box<dyn PerturbationOperator<Chromosome = T>>>,
}

impl<T: Clone> CombinedPerturbation<T> {
impl<T> CombinedPerturbation<T> {
    pub fn new(perturbations: Vec<Box<dyn PerturbationOperator<Chromosome = T>>>) -> Self {
        Self {
            perturbations,


@@ 251,16 250,13 @@ impl<T: Clone> CombinedPerturbation<T> {
    }
}

impl<T: Clone> PerturbationOperator for CombinedPerturbation<T> {
impl<T> PerturbationOperator for CombinedPerturbation<T> {
    type Chromosome = T;

    fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome {
        let mut current_chromosome = chromosome.clone();
    fn perturb(&mut self, chromosome: &mut Self::Chromosome) {
        for perturbation in self.perturbations.iter_mut() {
            current_chromosome = perturbation.perturb(&current_chromosome);
            perturbation.perturb(chromosome);
        }

        current_chromosome
    }
}



@@ 272,9 268,10 @@ pub mod tests {
    fn test_perturb() {
        let mut rng = rand::rng();

        let mut binary_string1 = BinaryString::new_dyn(vec![1, 1, 0, 0]);
        binary_string1.perturb(&mut rng, 1.0);
        assert_eq!(
            *BinaryString::new_dyn(vec![1, 1, 0, 0])
                .perturb(&mut rng, 1.0)
            *binary_string1
                .vec()
                .iter()
                .map(|&x| x)


@@ 282,10 279,10 @@ pub mod tests {
            vec![0, 0, 1, 1]
        );


        let mut binary_string2 = BinaryString::new_dyn(vec![1, 1, 0, 0]);
        binary_string2.perturb(&mut rng, 0.0);
        assert_eq!(
            *BinaryString::new_dyn(vec![1, 1, 0, 0])
                .perturb(&mut rng, 0.0)
            *binary_string2
                .vec()
                .iter()
                .map(|&x| x)