use std::marker::PhantomData; use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, SVector}; use rand::{distr::Distribution, Rng, RngCore}; use rand_distr::{uniform, Normal, NormalError, Uniform}; use crate::binary_string::BinaryString; pub trait PerturbationOperator { type Chromosome; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore); } pub struct BinaryStringBitPerturbation { p: f64, _phantom: PhantomData } impl BinaryStringBitPerturbation { pub fn new(p: f64) -> Self { Self { p, _phantom: PhantomData } } } impl PerturbationOperator for BinaryStringBitPerturbation where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { chromosome.perturb(rng, self.p); } } pub struct BinaryStringSingleBitPerturbation { _phantom: PhantomData } impl BinaryStringSingleBitPerturbation { pub fn new() -> Self { Self { _phantom: PhantomData } } } impl PerturbationOperator for BinaryStringSingleBitPerturbation where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { let bit_range = 0..chromosome.vec.len(); let flip_bit = rng.random_range(bit_range); chromosome.vec[flip_bit] = 1 - chromosome.vec[flip_bit]; } } pub struct BinaryStringFlipPerturbation { _phantom: PhantomData } impl BinaryStringFlipPerturbation { pub fn new() -> Self { Self { _phantom: PhantomData } } } impl PerturbationOperator for BinaryStringFlipPerturbation where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; fn perturb(&self, chromosome: &mut Self::Chromosome, _: &mut dyn RngCore) { chromosome.vec .apply(|c| *c = 1 - *c); } } pub struct RandomDistributionPerturbation> { distribution: TDistribution, parameter: f64 } impl RandomDistributionPerturbation> { pub fn normal(std_dev: f64) -> Result { Ok(Self { distribution: Normal::new(0.0, std_dev)?, parameter: std_dev }) } pub fn std_dev(&self) -> f64 { self.parameter } pub fn set_std_dev(&mut self, std_dev: f64) -> Result { self.parameter = std_dev; self.distribution = Normal::new(0.0, std_dev)?; Ok(std_dev) } } impl RandomDistributionPerturbation> { pub fn uniform(range: f64) -> Result { Ok(Self { distribution: Uniform::new(-range/2.0, range/2.0)?, parameter: range, }) } pub fn range(&self) -> f64 { self.parameter } pub fn set_range(&mut self, range: f64) -> Result { self.parameter = range; self.distribution = Uniform::new(-range/2.0, range/2.0)?; Ok(range) } } impl, const LEN: usize> PerturbationOperator for RandomDistributionPerturbation { type Chromosome = SVector; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { *chromosome += Self::Chromosome::zeros().map(|_| self.distribution.sample(rng)); } } pub struct PatternPerturbation { d: f64 } impl PatternPerturbation { pub fn new(d: f64) -> Self { Self { d } } } impl PerturbationOperator for PatternPerturbation { type Chromosome = SVector::; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { // 1. Choose dimension let idx = rng.random_range(0..LEN); // 2. Direction let d = if rng.random_bool(0.5) { self.d } else { -self.d }; // Apply chromosome[idx] += d; } } pub enum BoundedPerturbationStrategy { /// Trims the value to get a value within bounds Trim, /// Retries calling the underlying perturbation until /// value within bounds is returned. If argument is given, /// this is the maximum number of retries to do and then /// fall back to trimming. Zero means retry indefinitely. Retry(usize) } pub struct BoundedPerturbation>> { min_max: SVector<(f64, f64), LEN>, strategy: BoundedPerturbationStrategy, perturbation: T, } impl>> BoundedPerturbation { pub fn new( perturbation: T, min: SVector, max: SVector, strategy: BoundedPerturbationStrategy ) -> Self { let min_max = min.zip_map(&max, |min, max| (min, max)); Self { min_max, strategy, perturbation } } pub fn inner(&self) -> &T { &self.perturbation } pub fn inner_mut(&mut self) -> &mut T { &mut self.perturbation } fn within_bounds(&self, chromosome: &SVector) -> bool { chromosome.iter() .zip(self.min_max.iter()) .all(|(&c, &(min, max))| c <= max && c >= min) } fn bound(&self, mut chromosome: SVector) -> SVector { chromosome .zip_apply(&self.min_max, |c, (min, max)| *c = c.clamp(min, max)); chromosome } fn retry_perturb(&self, chromosome: &mut SVector, retries: Option, rng: &mut dyn RngCore) { let mut perturbed = chromosome.clone(); self.perturbation.perturb(&mut perturbed, rng); if self.within_bounds(&perturbed) { *chromosome = perturbed; return; } match retries { Some(0) | None => *chromosome = self.bound(perturbed), Some(retries) => { *chromosome = perturbed; self.retry_perturb(chromosome, Some(retries - 1), rng); } } } } impl PerturbationOperator for BoundedPerturbation where T: PerturbationOperator> { type Chromosome = SVector; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { match self.strategy { BoundedPerturbationStrategy::Trim => self.retry_perturb(chromosome, None, rng), BoundedPerturbationStrategy::Retry(retries) => self.retry_perturb(chromosome, Some(retries), rng) } } } /// Perform given perturbation only with given probability pub struct MutationPerturbation { perturbation: Box>, probability: f64 } impl MutationPerturbation { pub fn new(perturbation: Box>, probability: f64) -> Self { Self { perturbation, probability } } } impl PerturbationOperator for MutationPerturbation { type Chromosome = T; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { if rng.random_bool(self.probability) { self.perturbation.perturb(chromosome, rng); } } } pub struct CombinedPerturbation { perturbations: Vec>>, } impl CombinedPerturbation { pub fn new(perturbations: Vec>>) -> Self { Self { perturbations, } } } impl PerturbationOperator for CombinedPerturbation { type Chromosome = T; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { for perturbation in self.perturbations.iter() { perturbation.perturb(chromosome, rng); } } } #[cfg(test)] pub mod tests { use crate::binary_string::BinaryString; #[test] 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!( *binary_string1 .vec() .iter() .map(|&x| x) .collect::>(), 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!( *binary_string2 .vec() .iter() .map(|&x| x) .collect::>(), vec![1, 1, 0, 0] ); } }