use crate::chromosomes::Range as RangeChromosome;
use crate::error::GaError;
use crate::operations::mutation::gaussian::GaussianConvertible;
use crate::traits::LinearChromosome;
use rand::Rng;
use std::any::Any;
use std::borrow::Cow;
pub fn differential_mutation<U>(
individual: &mut U,
chromosomes: &[U],
target_idx: usize,
f: f64,
) -> Result<(), GaError>
where
U: LinearChromosome + 'static,
{
if chromosomes.len() < 4 {
return Err(GaError::MutationError(
"Differential mutation requires at least 4 chromosomes in the population \
(target + 3 distinct donors)"
.to_string(),
));
}
if target_idx >= chromosomes.len() {
return Err(GaError::MutationError(format!(
"Differential mutation: target_idx {} is out of bounds (population size {})",
target_idx,
chromosomes.len()
)));
}
crate::log_debug!(target: "mutation_events", "Starting differential mutation f={}", f);
macro_rules! try_type {
($t:ty) => {
if (individual as &dyn Any).is::<RangeChromosome<$t>>() {
let pop_len = chromosomes.len();
let mut rng = crate::rng::make_rng();
let mut r1 = rng.random_range(0..pop_len);
while r1 == target_idx {
r1 = rng.random_range(0..pop_len);
}
let mut r2 = rng.random_range(0..pop_len);
while r2 == target_idx || r2 == r1 {
r2 = rng.random_range(0..pop_len);
}
let mut r3 = rng.random_range(0..pop_len);
while r3 == target_idx || r3 == r1 || r3 == r2 {
r3 = rng.random_range(0..pop_len);
}
let x1 = (&chromosomes[r1] as &dyn Any)
.downcast_ref::<RangeChromosome<$t>>()
.ok_or_else(|| {
GaError::MutationError(
"Differential donor mismatched type".to_string(),
)
})?;
let x2 = (&chromosomes[r2] as &dyn Any)
.downcast_ref::<RangeChromosome<$t>>()
.ok_or_else(|| {
GaError::MutationError(
"Differential donor mismatched type".to_string(),
)
})?;
let x3 = (&chromosomes[r3] as &dyn Any)
.downcast_ref::<RangeChromosome<$t>>()
.ok_or_else(|| {
GaError::MutationError(
"Differential donor mismatched type".to_string(),
)
})?;
let target = (individual as &mut dyn Any)
.downcast_mut::<RangeChromosome<$t>>()
.unwrap();
let n = target.dna().len();
let mut new_dna = target.dna().to_vec();
for i in 0..n {
if new_dna[i].ranges.is_empty() {
continue;
}
let a = <$t as GaussianConvertible>::to_f64(x1.dna()[i].value);
let b = <$t as GaussianConvertible>::to_f64(x2.dna()[i].value);
let c = <$t as GaussianConvertible>::to_f64(x3.dna()[i].value);
let mutant_f = a + f * (b - c);
let (lo, hi) = new_dna[i].ranges[0];
let lo_f = <$t as GaussianConvertible>::to_f64(lo);
let hi_f = <$t as GaussianConvertible>::to_f64(hi);
let clamped = mutant_f.clamp(lo_f, hi_f);
new_dna[i].value = <$t as GaussianConvertible>::from_f64(clamped);
}
target.set_dna(Cow::Owned(new_dna));
crate::log_debug!(target: "mutation_events", "Finished differential mutation");
return Ok(());
}
};
}
try_type!(f64);
try_type!(f32);
try_type!(i32);
try_type!(i64);
Err(GaError::MutationError(
"Differential mutation requires a Range<T> chromosome \
(T = f64, f32, i32, or i64)"
.to_string(),
))
}