use genetic_algorithms::chromosomes::{Binary as BinaryChromosome, Range as RangeChromosome};
use genetic_algorithms::genotypes::Range as RangeGenotype;
use genetic_algorithms::operations::mutation;
use genetic_algorithms::operations::mutation::self_adaptive_gaussian::self_adaptive_gaussian_mutation;
use genetic_algorithms::operations::{Mutation, SelfAdaptiveGaussianParams};
use genetic_algorithms::traits::{LinearChromosome, MutationOperator, SelfAdaptive};
use std::borrow::Cow;
fn build_f64_chromosome(n: usize) -> RangeChromosome<f64> {
let mut c = RangeChromosome::<f64>::new();
let dna: Vec<_> = (0..n)
.map(|i| RangeGenotype::new(i as i32, vec![(-10.0, 10.0)], 0.0))
.collect();
c.set_dna(Cow::Owned(dna));
c
}
#[test]
fn self_adaptive_sigma_min_enforced() {
let mut c = build_f64_chromosome(4);
c.set_strategy_params(vec![1e-8; 4]);
let sigma_min = 1e-5;
for _ in 0..100 {
let _ = self_adaptive_gaussian_mutation(&mut c, 0.0, 0.0, sigma_min, None);
for &sigma in c.strategy_params() {
assert!(
sigma >= sigma_min,
"Sigma {} dropped below sigma_min {}",
sigma,
sigma_min
);
}
}
}
#[test]
#[ignore]
fn self_adaptive_sigma_spread_evolves() {
let mut c = build_f64_chromosome(8);
c.set_strategy_params(vec![0.5; 8]);
let tau = 0.5;
let tau_prime = 0.5;
let sigma_min = 1e-5;
for _ in 0..200 {
let _ = self_adaptive_gaussian_mutation(&mut c, tau, tau_prime, sigma_min, None);
}
let sigmas = c.strategy_params();
let final_max = sigmas.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let final_min = sigmas.iter().cloned().fold(f64::INFINITY, f64::min);
assert!(
final_max > final_min * 10.0,
"Sigma spread should have occurred across dimensions. Max={}, Min={}, All={:?}",
final_max,
final_min,
sigmas
);
assert!(
final_min >= sigma_min,
"No sigma should drop below sigma_min={}. Got {}",
sigma_min,
final_min
);
}
#[test]
fn self_adaptive_gaussian_returns_error_for_non_self_adaptive() {
let mut binary_chrom = BinaryChromosome::new();
let m = Mutation::SelfAdaptiveGaussian(SelfAdaptiveGaussianParams {
tau: None,
tau_prime: None,
sigma_min: None,
sigma_max: None,
});
let result = m.mutate(&mut binary_chrom, &m);
assert!(
result.is_err(),
"SelfAdaptiveGaussian must return Err for BinaryChromosome (not a SelfAdaptive chromosome), got Ok"
);
}
#[test]
fn self_adaptive_gaussian_inline_params_work() {
let mut c = build_f64_chromosome(4);
c.set_strategy_params(vec![0.1; 4]);
let m = Mutation::SelfAdaptiveGaussian(SelfAdaptiveGaussianParams {
tau: Some(0.5),
tau_prime: Some(0.5),
sigma_min: Some(1e-5),
sigma_max: None,
});
for _ in 0..50 {
m.mutate(&mut c, &m).unwrap();
for &sigma in c.strategy_params() {
assert!(sigma >= 1e-5, "Sigma {} dropped below sigma_min", sigma);
}
}
}
#[test]
fn self_adaptive_gaussian_default_params_stay_in_range() {
let mut c = build_f64_chromosome(4);
c.set_strategy_params(vec![0.5; 4]);
let m = Mutation::SelfAdaptiveGaussian(SelfAdaptiveGaussianParams {
tau: None,
tau_prime: None,
sigma_min: None,
sigma_max: None,
});
for _ in 0..100 {
m.mutate(&mut c, &m).unwrap();
for gene in c.dna() {
let (lo, hi) = gene.ranges[0];
assert!(
gene.value >= lo && gene.value <= hi,
"Gene value {} out of range [{}, {}]",
gene.value,
lo,
hi
);
}
}
}
#[test]
fn factory_self_adaptive_with_explicit_params() {
let mut c = build_f64_chromosome(4);
c.set_strategy_params(vec![0.1; 4]);
let result = mutation::factory_self_adaptive(&mut c, Some(0.5), Some(0.5), Some(1e-5), None);
assert!(
result.is_ok(),
"factory_self_adaptive should succeed: {:?}",
result
);
}