From 06f3259b7694559e224c43dac7a4fcf200762721 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sun, 5 Oct 2025 20:12:29 +0200 Subject: [PATCH] feat: add BoundedPerturbation applies bounds on real numbers when performing the perturbation. --- env/src/perturbation/mod.rs | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/env/src/perturbation/mod.rs b/env/src/perturbation/mod.rs index 25e202cda3db32995312908f2638289ca49bf54a..df04b6377949baa43ea0eb1a9e9eb4cfadd8b90e 100644 --- a/env/src/perturbation/mod.rs +++ b/env/src/perturbation/mod.rs @@ -54,6 +54,82 @@ impl, const LEN: usize> Perturbation } } +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: SVector, + max: SVector, + 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, + 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: &mut Self, chromosome: &SVector, retries: Option) -> SVector { + let perturbed = self.perturbation.perturb(chromosome); + + if self.within_bounds(&perturbed) { + return perturbed; + } + + match retries { + Some(0) | None => self.bound(perturbed), + Some(retries) => self.retry_perturb(chromosome, Some(retries - 1)) + } + } +} + +impl PerturbationOperator for BoundedPerturbation +where + T: PerturbationOperator> +{ + type Chromosome = SVector; + + fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome { + match self.strategy { + BoundedPerturbationStrategy::Trim => self.retry_perturb(chromosome, None), + BoundedPerturbationStrategy::Retry(retries) => self.retry_perturb(chromosome, Some(retries)) + } + } +} + #[cfg(test)] pub mod tests { use crate::binary_string::BinaryString;