use std::{any::Any, marker::PhantomData}; use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, SVector}; use rand::{distr::Distribution, Rng, RngCore, prelude::IteratorRandom}; use rand_distr::{uniform, Normal, NormalError, Uniform}; use crate::binary_string::BinaryString; pub trait AsAny: Any { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; } pub enum Wrapped<'a, T> { Single, Wrapped(&'a dyn PerturbationOperator), ListWrapped(Vec<&'a (dyn PerturbationOperator)>), } pub enum WrappedMut<'a, T> { Single, Wrapped(&'a mut dyn PerturbationOperator), ListWrapped(Vec<&'a mut dyn PerturbationOperator>), } pub struct NoParams; pub trait PerturbationOperator { type Chromosome; fn try_get_params(&self) -> Option<&dyn Any> { None } fn try_get_params_mut(&mut self) -> Option<&mut dyn Any> { None } fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore); fn wrapped(&self) -> Wrapped<'_, Self::Chromosome> { Wrapped::Single } fn wrapped_mut(&mut self) -> WrappedMut<'_, Self::Chromosome> { WrappedMut::Single } } impl AsAny for T { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } } pub struct IdentityPerturbation { _phantom: PhantomData } impl PerturbationOperator for IdentityPerturbation { type Chromosome = TChromosome; fn perturb(&self, _: &mut Self::Chromosome, _: &mut dyn RngCore) { // Do nothing. } } pub struct BinaryStringBitPerturbation { pub 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 BinaryStringFlipNPerturbation { n: usize, _phantom: PhantomData, } impl BinaryStringFlipNPerturbation { pub fn new(n: usize) -> Self { Self { n, _phantom: PhantomData } } } impl PerturbationOperator for BinaryStringFlipNPerturbation where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { let index = rng.random_range(0..chromosome.vec.len()); for i in index..(index + self.n).min(chromosome.vec.len()) { chromosome.vec[i] = 1 - chromosome.vec[i]; } } } pub struct RandomDistributionParameter { pub parameter: f64, _phantom: PhantomData } pub struct RandomDistributionPerturbation> { distribution: TDistribution, params: RandomDistributionParameter } impl RandomDistributionPerturbation> { pub fn normal(std_dev: f64) -> Result { Ok(Self { distribution: Normal::new(0.0, std_dev)?, params: RandomDistributionParameter { parameter: std_dev, _phantom: PhantomData } }) } pub fn std_dev(&self) -> f64 { self.params.parameter } pub fn set_std_dev(&mut self, std_dev: f64) -> Result { self.params.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)?, params: RandomDistributionParameter { parameter: range, _phantom: PhantomData } }) } pub fn range(&self) -> f64 { self.params.parameter } pub fn set_range(&mut self, range: f64) -> Result { self.params.parameter = range; self.distribution = Uniform::new(-range/2.0, range/2.0)?; Ok(range) } } impl + 'static, 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)); } fn try_get_params(&self) -> Option<&dyn Any> { Some(&self.params) } fn try_get_params_mut(&mut self) -> Option<&mut dyn Any> { Some(&mut self.params) } } 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 } } 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) } } fn wrapped(&self) -> Wrapped<'_, Self::Chromosome> { Wrapped::Wrapped(&self.perturbation) } fn wrapped_mut(&mut self) -> WrappedMut<'_, Self::Chromosome> { WrappedMut::Wrapped(&mut self.perturbation) } } /// Perform given perturbation only with given probability pub struct MutationPerturbationParams { pub probability: f64 } pub struct MutationPerturbation<'a, T> { perturbation: Box + 'a>, params: MutationPerturbationParams } impl<'a, T> MutationPerturbation<'a, T> { pub fn new(perturbation: Box + 'a>, probability: f64) -> Self { Self { perturbation, params: MutationPerturbationParams { probability } } } pub fn apply_to_mutations( base_perturbation: &mut dyn PerturbationOperator, apply: &mut dyn FnMut(&mut MutationPerturbationParams) ) { apply_to_perturbations(base_perturbation, apply); } } pub fn apply_to_perturbations( base_perturbation: &mut dyn PerturbationOperator, apply: &mut dyn FnMut(&mut U) ) { if let Some(params) = base_perturbation.try_get_params_mut().map(|p| p.downcast_mut::()).flatten() { apply(params) } match base_perturbation.wrapped_mut() { WrappedMut::Single => (), WrappedMut::Wrapped(wrapped) => { apply_to_perturbations(wrapped, apply); }, WrappedMut::ListWrapped(wrapped) => { for wrapped in wrapped { apply_to_perturbations(wrapped, apply); } } }; } impl<'a, T> PerturbationOperator for MutationPerturbation<'a, T> { type Chromosome = T; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { if rng.random_bool(self.params.probability) { self.perturbation.perturb(chromosome, rng); } } fn wrapped(&self) -> Wrapped<'_, Self::Chromosome> { Wrapped::Wrapped(self.perturbation.as_ref()) } fn wrapped_mut(&mut self) -> WrappedMut<'_, Self::Chromosome> { WrappedMut::Wrapped(self.perturbation.as_mut()) } fn try_get_params(&self) -> Option<&dyn Any> { Some(self.params.as_any()) } fn try_get_params_mut(&mut self) -> Option<&mut dyn Any> { Some(self.params.as_any_mut()) } } pub struct CombinedPerturbation<'a, T> { perturbations: Vec + 'a>>, } impl<'a, T> CombinedPerturbation<'a, T> { pub fn new(perturbations: Vec + 'a>>) -> Self { Self { perturbations, } } } impl<'a, T> PerturbationOperator for CombinedPerturbation<'a, T> { type Chromosome = T; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { for perturbation in self.perturbations.iter() { perturbation.perturb(chromosome, rng); } } fn wrapped(&self) -> Wrapped<'_, Self::Chromosome> { Wrapped::ListWrapped( self.perturbations .iter() .map(|p| p.as_ref()).collect()) } fn wrapped_mut(&mut self) -> WrappedMut<'_, Self::Chromosome> { WrappedMut::ListWrapped( self.perturbations .iter_mut() .map::<&mut (dyn PerturbationOperator + '_), _> (|p| p.as_mut()).collect() ) } } pub struct OneOfPerturbation<'a, T> { perturbations: Vec + 'a>> } impl<'a, T> OneOfPerturbation<'a, T> { pub fn new(perturbations: Vec + 'a>>) -> Self { Self { perturbations } } } impl<'a, T> PerturbationOperator for OneOfPerturbation<'a, T> { type Chromosome = T; fn perturb(&self, chromosome: &mut Self::Chromosome, rng: &mut dyn RngCore) { let chosen = (0..self.perturbations.len()).choose(rng); if let Some(chosen) = chosen { self.perturbations[chosen].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] ); } }