use crate::chromosomes::Range as RangeChromosome;
use crate::error::GaError;
use crate::operations::mutation::gaussian::GaussianConvertible;
use crate::traits::SelfAdaptive;
use rand::Rng;
use std::fmt::Debug;
pub fn self_adaptive_gaussian_mutation<T>(
individual: &mut RangeChromosome<T>,
tau: f64,
tau_prime: f64,
sigma_min: f64,
sigma_max: Option<f64>,
) -> Result<(), GaError>
where
T: Sync + Send + Clone + Default + Debug + PartialOrd + Copy + 'static + GaussianConvertible,
RangeChromosome<T>: SelfAdaptive,
{
let len = individual.dna.len();
if len == 0 {
return Ok(());
}
individual.adapt_strategy_params(tau, tau_prime, sigma_min);
if let Some(max) = sigma_max {
let mut capped = individual.strategy_params().to_vec();
for s in capped.iter_mut() {
*s = s.min(max);
}
individual.set_strategy_params(capped);
}
let mut rng = crate::rng::make_rng();
let idx = rng.random_range(0..len);
let sigma = individual
.strategy_params()
.get(idx)
.copied()
.unwrap_or(1.0);
let mut gene = individual.dna[idx].clone();
if gene.ranges.is_empty() {
return Ok(());
}
let range_idx = rng.random_range(0..gene.ranges.len());
let (lo, hi) = gene.ranges[range_idx];
let current: f64 = T::to_f64(gene.value);
let lo_f64: f64 = T::to_f64(lo);
let hi_f64: f64 = T::to_f64(hi);
let u1: f64 = rng.random_range(f64::EPSILON..1.0);
let u2: f64 = rng.random_range(0.0..std::f64::consts::TAU);
let noise: f64 = (-2.0 * u1.ln()).sqrt() * u2.cos() * sigma;
let new_val = (current + noise).clamp(lo_f64, hi_f64);
gene.value = T::from_f64(new_val);
individual.dna[idx] = gene;
crate::log_debug!(
target: "mutation_events",
"SelfAdaptiveGaussian mutation applied at idx={} sigma={}",
idx,
sigma
);
Ok(())
}