#![allow(box_pointers)]
use core::marker::PhantomData;
use rand::seq::SliceRandom;
use rand::Rng;
use crate::genotype::Genotype;
use super::Mutation;
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct RandomOrder;
#[derive(Copy, Clone, Debug)]
#[non_exhaustive]
pub struct Selective;
#[allow(missing_debug_implementations)] pub struct Combination<S, G, RNG, const STRATEGIES: usize>
where
G: Genotype,
RNG: Rng,
{
strategy: PhantomData<S>,
rng: PhantomData<RNG>,
genotype: PhantomData<G>,
strategies: [Box<dyn Mutation<G, RNG>>; STRATEGIES],
}
impl<G, RNG> Combination<RandomOrder, G, RNG, 0>
where
G: Genotype,
RNG: Rng,
{
#[must_use]
pub fn random() -> Combination<RandomOrder, G, RNG, 0> {
Combination {
strategy: PhantomData::<RandomOrder>,
rng: PhantomData::<RNG>,
genotype: PhantomData::<G>,
strategies: [],
}
}
}
impl<G, RNG> Combination<Selective, G, RNG, 0>
where
G: Genotype,
RNG: Rng,
{
#[must_use]
pub fn selective() -> Combination<Selective, G, RNG, 0> {
Combination {
strategy: PhantomData::<Selective>,
rng: PhantomData::<RNG>,
genotype: PhantomData::<G>,
strategies: [],
}
}
}
impl<S, G, RNG, const STRATEGIES: usize> Combination<S, G, RNG, STRATEGIES>
where
G: Genotype,
RNG: Rng,
{
pub fn and<M>(self, mutation: M) -> Combination<S, G, RNG, { STRATEGIES + 1 }>
where
M: Mutation<G, RNG> + 'static,
{
let boxed: Box<dyn Mutation<G, RNG>> = Box::new(mutation);
let mut iter = self.strategies.into_iter().chain(Some(boxed));
#[allow(clippy::expect_used)]
let strategies = core::array::from_fn(|_| iter.next().expect("constructed iterator has known length"));
Combination {
strategy: self.strategy,
rng: self.rng,
genotype: self.genotype,
strategies,
}
}
}
impl<G, RNG, const STRATEGIES: usize> Mutation<G, RNG> for Combination<RandomOrder, G, RNG, STRATEGIES>
where
G: Genotype,
RNG: Rng,
{
fn mutate(&self, genotype: &mut G, rng: &mut RNG) {
assert!(STRATEGIES > 0, "combination mutation should contain strategies");
for strategy in self.strategies.choose_multiple(rng, STRATEGIES) {
strategy.mutate(genotype, rng);
}
}
}
impl<G, RNG, const STRATEGIES: usize> Mutation<G, RNG> for Combination<Selective, G, RNG, STRATEGIES>
where
G: Genotype,
RNG: Rng,
{
fn mutate(&self, genotype: &mut G, rng: &mut RNG) {
assert!(STRATEGIES > 0, "combination mutation should contain strategies");
#[allow(clippy::expect_used)]
let strategy = self
.strategies
.choose(rng)
.expect("previous assert makes this always return some");
strategy.mutate(genotype, rng);
}
}
#[cfg(test)]
mod tests {
use rand::rngs::SmallRng;
use rand::SeedableRng;
use crate::operators::mutation::{add::Add, random::single::uniform::Uniform, remove::Remove, swap::Swap};
use super::{Combination, Mutation};
#[test]
fn random() {
let add = Add {
chance: 1.0_f64,
max_genes: None,
};
let random = Uniform { chance: 1.0 };
let combination = Combination::random().and(add).and(random);
let mut rng = SmallRng::seed_from_u64(1);
let mut array = vec![0_u8, 1, 2, 3, 4];
combination.mutate(&mut array, &mut rng);
assert_eq!(array, [229, 0, 1, 237, 3, 4]);
}
#[test]
fn selective() {
let swap = Swap { chance: 1.0 };
let remove = Remove {
chance: 1.0_f64,
min_genes: 1.try_into().unwrap(),
};
let combination = Combination::selective().and(swap).and(remove);
let mut rng = SmallRng::seed_from_u64(0);
let mut array = vec![0_u8, 1, 2, 3, 4];
combination.mutate(&mut array, &mut rng);
assert_eq!(array, [0, 1, 3, 4]);
}
}