use ndarray::Array1;
use crate::operators::CrossoverOperator;
use crate::random::RandomGenerator;
#[derive(Debug, Clone)]
pub struct SimulatedBinaryCrossover {
pub distribution_index: f64,
}
impl SimulatedBinaryCrossover {
pub fn new(distribution_index: f64) -> Self {
Self { distribution_index }
}
}
pub fn sbx_crossover_array(
p1: &Array1<f64>,
p2: &Array1<f64>,
distribution_index: f64,
prob_exchange: f64,
rng: &mut impl RandomGenerator,
) -> (Array1<f64>, Array1<f64>) {
let n = p1.len();
let eps = 1e-16;
let mut offspring1 = p1.clone();
let mut offspring2 = p2.clone();
for i in 0..n {
let gene1 = p1[i];
let gene2 = p2[i];
if (gene1 - gene2).abs() < eps {
continue;
}
let r_beta = rng.gen_proability();
let r_exchange = rng.gen_proability();
let (y1, y2) = if gene1 < gene2 {
(gene1, gene2)
} else {
(gene2, gene1)
};
let delta = y2 - y1;
let beta_q = if r_beta <= 0.5 {
(2.0 * r_beta).powf(1.0 / (distribution_index + 1.0))
} else {
(1.0 / (2.0 * (1.0 - r_beta))).powf(1.0 / (distribution_index + 1.0))
};
let c1 = 0.5 * ((y1 + y2) - beta_q * delta);
let c2 = 0.5 * ((y1 + y2) + beta_q * delta);
let (new1, new2) = if r_exchange < prob_exchange {
(c2, c1)
} else {
(c1, c2)
};
offspring1[i] = new1;
offspring2[i] = new2;
}
(offspring1, offspring2)
}
impl CrossoverOperator for SimulatedBinaryCrossover {
fn crossover(
&self,
parent_a: &Array1<f64>,
parent_b: &Array1<f64>,
rng: &mut impl RandomGenerator,
) -> (Array1<f64>, Array1<f64>) {
sbx_crossover_array(parent_a, parent_b, self.distribution_index, 0.0, rng)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::random::{RandomGenerator, TestDummyRng};
use ndarray::array;
struct FakeRandom {
probability_values: Vec<f64>,
dummy: TestDummyRng,
}
impl FakeRandom {
fn new(probability_values: Vec<f64>) -> Self {
Self {
probability_values,
dummy: TestDummyRng,
}
}
}
impl RandomGenerator for FakeRandom {
type R = TestDummyRng;
fn rng(&mut self) -> &mut TestDummyRng {
&mut self.dummy
}
fn gen_proability(&mut self) -> f64 {
self.probability_values.remove(0)
}
}
#[test]
fn test_simulated_binary_crossover() {
let parent_a = array![1.0, 5.0];
let parent_b = array![3.0, 5.0];
let operator = SimulatedBinaryCrossover::new(2.0);
let mut fake_rng = FakeRandom::new(vec![
0.25, 0.9, ]);
let (child_a, child_b) = operator.crossover(&parent_a, &parent_b, &mut fake_rng);
let tol = 1e-8;
assert!(
(child_a[0] - 1.2062994741).abs() < tol,
"Gene 0 of child_a not as expected"
);
assert!(
(child_b[0] - 2.7937005259).abs() < tol,
"Gene 0 of child_b not as expected"
);
assert!(
(child_a[1] - 5.0).abs() < tol,
"Gene 1 of child_a not as expected"
);
assert!(
(child_b[1] - 5.0).abs() < tol,
"Gene 1 of child_b not as expected"
);
}
}