use ndarray::{Array1, Array2};
use rand::Rng;
use rand::seq::SliceRandom;
use crate::mutant_rand1::mutant_rand1;
pub(crate) fn mutant_adaptive<R: Rng + ?Sized>(
i: usize,
pop: &Array2<f64>,
sorted_indices: &[usize],
w: f64,
f: f64,
rng: &mut R,
) -> Array1<f64> {
let w_size = ((w * pop.nrows() as f64) as usize)
.max(1)
.min(pop.nrows() - 1);
let top_indices = &sorted_indices[0..w_size];
let gr_better_idx = top_indices[rng.random_range(0..w_size)];
let mut available: Vec<usize> = (0..pop.nrows())
.filter(|&idx| idx != i && idx != gr_better_idx)
.collect();
available.shuffle(rng);
if available.len() < 2 {
return mutant_rand1(i, pop, f, rng);
}
let r1 = available[0];
let r2 = available[1];
pop.row(i).to_owned()
+ &((pop.row(gr_better_idx).to_owned() - pop.row(i).to_owned() + pop.row(r1).to_owned()
- pop.row(r2).to_owned())
* f)
}
#[cfg(test)]
mod tests {
use crate::{AdaptiveConfig, DEConfigBuilder, Mutation, Strategy, differential_evolution};
use autoeq_testfunctions::quadratic;
#[test]
fn test_adaptive_basic() {
let bounds = [(-5.0, 5.0), (-5.0, 5.0)];
let adaptive_config = AdaptiveConfig {
adaptive_mutation: true,
wls_enabled: false, w_max: 0.9,
w_min: 0.1,
..AdaptiveConfig::default()
};
let config = DEConfigBuilder::new()
.seed(42)
.maxiter(100)
.popsize(30)
.strategy(Strategy::AdaptiveBin)
.mutation(Mutation::Adaptive { initial_f: 0.8 })
.adaptive(adaptive_config)
.build();
let result = differential_evolution(&quadratic, &bounds, config);
assert!(
result.fun < 1e-3,
"Adaptive DE should converge: f={}",
result.fun
);
for &xi in result.x.iter() {
assert!(
xi.abs() < 0.5,
"Solution component should be close to 0: {}",
xi
);
}
}
#[test]
fn test_adaptive_with_wls() {
let bounds = [(-5.0, 5.0), (-5.0, 5.0)];
let adaptive_config = AdaptiveConfig {
adaptive_mutation: true,
wls_enabled: true,
wls_prob: 0.2, wls_scale: 0.1,
..AdaptiveConfig::default()
};
let config = DEConfigBuilder::new()
.seed(123)
.maxiter(200)
.popsize(40)
.strategy(Strategy::AdaptiveExp)
.adaptive(adaptive_config)
.build();
let result = differential_evolution(&quadratic, &bounds, config);
assert!(
result.fun < 1e-4,
"Adaptive DE with WLS should converge well: f={}",
result.fun
);
for &xi in result.x.iter() {
assert!(
xi.abs() < 0.2,
"Solution should be very close to 0 with WLS: {}",
xi
);
}
}
}