use crate::{
core::{
context::Context, individual::Individual, offspring::Offspring, population::Population,
state::State,
},
fitness::FitnessEvaluator,
operators::GeneticOperator,
};
use rand::{Rng, RngExt};
#[derive(Debug, Default, Clone, Copy)]
pub struct Arithmetic;
impl Arithmetic {
pub fn new() -> Self {
Self
}
}
macro_rules! impl_arithmetic_crossover {
($float:ty, $cast:expr) => {
impl<F, Fe, R, C, const N: usize> GeneticOperator<[$float; N], F, Fe, R, C> for Arithmetic
where
R: Rng,
Fe: FitnessEvaluator<[$float; N], F>,
{
fn apply(
&self,
state: &State<[$float; N], F>,
ctx: &mut Context<Fe, R, C>,
) -> Offspring<[$float; N], F> {
let mut population = Population::with_capacity(state.population().len());
for chunk in state.population().chunks_exact(2) {
let p1 = unsafe { chunk.get_unchecked(0) }.genome();
let p2 = unsafe { chunk.get_unchecked(1) }.genome();
let alpha: f64 = ctx.rng().random();
let child1: [$float; N] = std::array::from_fn(|i| $cast(alpha * p1[i] as f64 + (1.0 - alpha) * p2[i] as f64));
let child2: [$float; N] = std::array::from_fn(|i| $cast((1.0 - alpha) * p1[i] as f64 + alpha * p2[i] as f64));
population.add(Individual::new(child1));
population.add(Individual::new(child2));
}
Offspring::Multiple(population)
}
}
impl<F, Fe, R, C> GeneticOperator<Vec<$float>, F, Fe, R, C> for Arithmetic
where
R: Rng,
Fe: FitnessEvaluator<Vec<$float>, F>,
{
fn apply(
&self,
state: &State<Vec<$float>, F>,
ctx: &mut Context<Fe, R, C>,
) -> Offspring<Vec<$float>, F> {
let mut population = Population::with_capacity(state.population().len());
for chunk in state.population().chunks_exact(2) {
let p1 = unsafe { chunk.get_unchecked(0) }.genome();
let p2 = unsafe { chunk.get_unchecked(1) }.genome();
let alpha: f64 = ctx.rng().random();
let len = p1.len().min(p2.len());
let child1: Vec<$float> = (0..len)
.map(|i| $cast(alpha * p1[i] as f64 + (1.0 - alpha) * p2[i] as f64))
.collect();
let child2: Vec<$float> = (0..len)
.map(|i| $cast((1.0 - alpha) * p1[i] as f64 + alpha * p2[i] as f64))
.collect();
population.add(Individual::new(child1));
population.add(Individual::new(child2));
}
Offspring::Multiple(population)
}
}
};
}
impl_arithmetic_crossover!(f64, |x: f64| x);
impl_arithmetic_crossover!(f32, |x: f64| x as f32);