use radiate_core::{
AlterContext, AlterResult, BoundedGene, Chromosome, Crossover, FloatGene, Gene, Rate, Valid,
random_provider,
};
use radiate_utils::Float;
#[derive(Clone, Debug)]
pub struct IntermediateCrossover {
rate: Rate,
alpha: f32,
}
impl IntermediateCrossover {
pub fn new(rate: impl Into<Rate>, alpha: f32) -> Self {
let rate = rate.into();
if !rate.is_valid() {
panic!("Rate {rate:?} is not valid. Must be between 0.0 and 1.0",);
}
if !(0.0..=1.0).contains(&alpha) {
panic!("Alpha must be between 0 and 1");
}
IntermediateCrossover { rate, alpha }
}
}
impl<F, C> Crossover<C> for IntermediateCrossover
where
F: Float,
C: Chromosome<Gene = FloatGene<F>>,
{
fn rate(&self) -> Rate {
self.rate.clone()
}
#[inline]
fn cross_chromosomes(
&self,
chrom_one: &mut C,
chrom_two: &mut C,
ctx: &mut AlterContext,
) -> AlterResult {
let mut cross_count = 0;
let alpha = F::from(self.alpha).unwrap();
random_provider::with_rng(|rand| {
for i in 0..std::cmp::min(chrom_one.len(), chrom_two.len()) {
if rand.bool(ctx.rate()) {
let gene_one = chrom_one.get_mut(i);
let gene_two = chrom_two.get_mut(i);
let allele_one = gene_one.allele().clone();
let allele_two = gene_two.allele().clone();
let alpha = rand.range(F::ZERO..alpha);
let new_allele_one = allele_one * alpha + allele_two * (F::ONE - alpha);
let new_allele_two = allele_two * alpha + allele_one * (F::ONE - alpha);
let (one_min, one_max) = gene_one.bounds();
let (two_min, two_max) = gene_two.bounds();
*gene_one.allele_mut() = new_allele_one.clamp(*one_min, *one_max);
*gene_two.allele_mut() = new_allele_two.clamp(*two_min, *two_max);
cross_count += 1;
}
}
});
cross_count.into()
}
}