use super::{clamp, sample, Evaluator, Optimizer};
use crate::problem::Problem;
use crate::rng::Rng;
use crate::solution::{Report, Solution};
use crate::termination::Termination;
#[derive(Debug, Clone, Copy)]
pub struct De {
pub pop_size: usize,
pub f: f64,
pub cr: f64,
pub seed: u64,
}
impl Default for De {
fn default() -> Self {
De {
pop_size: 30,
f: 0.8,
cr: 0.9,
seed: 42,
}
}
}
impl Optimizer for De {
fn with_seed(&self, seed: u64) -> Self {
De { seed, ..*self }
}
fn optimize(&self, problem: &dyn Problem, term: &Termination) -> Report {
let bounds = problem.bounds();
let dim = bounds.len();
let np = self.pop_size.max(4);
let mut rng = Rng::new(self.seed);
let first_x = sample(bounds, &mut rng);
let first_v = finite_or_worst(problem.objective(&first_x));
let mut ev = Evaluator::new(
problem,
term,
Solution {
x: first_x.clone(),
value: first_v,
},
);
let mut pop: Vec<Vec<f64>> = Vec::with_capacity(np);
let mut fit: Vec<f64> = Vec::with_capacity(np);
pop.push(first_x);
fit.push(first_v);
for _ in 1..np {
if ev.done() {
break;
}
let x = sample(bounds, &mut rng);
let v = finite_or_worst(ev.eval(&x));
pop.push(x);
fit.push(v);
}
let np = pop.len();
let mut trial = vec![0.0; dim];
while !ev.done() {
for i in 0..np {
if ev.done() {
break;
}
let (a, b, c) = three_distinct(np, i, &mut rng);
let jrand = rng.index(dim);
for j in 0..dim {
let (lo, hi) = bounds[j];
if rng.uniform() < self.cr || j == jrand {
let mutant = pop[a][j] + self.f * (pop[b][j] - pop[c][j]);
trial[j] = reflect(mutant, lo, hi);
} else {
trial[j] = pop[i][j];
}
}
let tv = finite_or_worst(ev.eval(&trial));
if tv <= fit[i] {
pop[i].copy_from_slice(&trial);
fit[i] = tv;
}
}
}
ev.finish()
}
}
#[inline]
fn finite_or_worst(v: f64) -> f64 {
if v.is_finite() {
v
} else {
f64::INFINITY
}
}
fn reflect(x: f64, lo: f64, hi: f64) -> f64 {
let mut v = x;
if v < lo {
v = lo + (lo - v);
} else if v > hi {
v = hi - (v - hi);
}
clamp(v, lo, hi)
}
fn three_distinct(np: usize, target: usize, rng: &mut Rng) -> (usize, usize, usize) {
let pick = |rng: &mut Rng, exclude: &[usize]| loop {
let r = rng.index(np);
if r != target && !exclude.contains(&r) {
return r;
}
};
let a = pick(rng, &[]);
let b = pick(rng, &[a]);
let c = pick(rng, &[a, b]);
(a, b, c)
}