use crate::chromosomes::Range as RangeChromosome;
use crate::traits::ChromosomeT;
use rand::Rng;
use std::borrow::Cow;
use std::fmt::Debug;
pub fn gaussian_mutation<T>(individual: &mut RangeChromosome<T>, sigma: f64)
where
T: Sync + Send + Clone + Default + Debug + PartialOrd + Copy + 'static + GaussianConvertible,
{
let len = individual.dna().len();
if len == 0 {
return;
}
let mut rng = crate::rng::make_rng();
let idx = rng.random_range(0..len);
let mut dna = individual.dna().to_vec();
let mut gene = dna[idx].clone();
if gene.ranges.is_empty() {
return;
}
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_f64 = (current + noise).clamp(lo_f64, hi_f64);
gene.value = T::from_f64(new_val_f64);
dna[idx] = gene;
individual.set_dna(Cow::Owned(dna));
}
pub trait GaussianConvertible {
fn from_f64(val: f64) -> Self;
fn to_f64(val: Self) -> f64;
}
impl GaussianConvertible for f64 {
fn from_f64(val: f64) -> Self {
val
}
fn to_f64(val: Self) -> f64 {
val
}
}
impl GaussianConvertible for f32 {
fn from_f64(val: f64) -> Self {
val as f32
}
fn to_f64(val: Self) -> f64 {
val as f64
}
}
impl GaussianConvertible for i32 {
fn from_f64(val: f64) -> Self {
val.round() as i32
}
fn to_f64(val: Self) -> f64 {
val as f64
}
}
impl GaussianConvertible for i64 {
fn from_f64(val: f64) -> Self {
val.round() as i64
}
fn to_f64(val: Self) -> f64 {
val as f64
}
}