use crate::{
core::{context::Context, offspring::Offspring, population::Population, state::State},
operators::GeneticOperator,
};
use std::num::NonZero;
use super::fill::{Fill, FixedSize, GetSize, PopSize};
#[derive(Debug, Clone)]
pub struct Proportional<O, S = PopSize> {
operators: O,
size: S,
}
impl<O> Proportional<O> {
pub fn new(operators: O) -> Self {
Self {
operators,
size: PopSize::new(),
}
}
}
impl<O> Proportional<O, FixedSize> {
pub fn with_size(operators: O, size: usize) -> Self {
Self {
operators,
size: FixedSize::new(size),
}
}
}
impl<G, F, Fe, R, C, O, S> GeneticOperator<G, F, Fe, R, C>
for Proportional<&[(O, NonZero<u16>)], S>
where
O: GeneticOperator<G, F, Fe, R, C>,
S: GetSize<G, F>,
{
fn apply(&self, state: &State<G, F>, ctx: &mut Context<Fe, R, C>) -> Offspring<G, F> {
let target_size = self.size.get_size(state);
let total_weight: u16 = self.operators.iter().map(|(_, w)| w.get()).sum();
let mut population = Population::with_capacity(target_size);
let mut remaining = target_size;
for (i, (operator, weight)) in self.operators.iter().enumerate() {
let target = if i == self.operators.len() - 1 {
remaining
} else {
let t = (weight.get() as usize * target_size) / total_weight as usize;
remaining -= t;
t
};
let fill = Fill::from_fixed_size(operator, target);
let offspring = fill.apply(state, ctx);
population.add_offspring(offspring);
}
Offspring::Multiple(population)
}
}
macro_rules! impl_genetic_proportional {
() => {};
($($Op:ident, $W:ident),+) => {
impl<G, F, Fe, R, C, S, $($Op),*> GeneticOperator<G, F, Fe, R, C>
for Proportional<( $(($Op, NonZero<u16>),)* ), S>
where
$($Op: GeneticOperator<G, F, Fe, R, C>),*,
S: GetSize<G, F>,
{
#[allow(unused_assignments, non_snake_case)]
fn apply(&self, state: &State<G, F>, ctx: &mut Context<Fe, R, C>) -> Offspring<G, F> {
let target_size = self.size.get_size(state);
let ( $( ($Op, $W), )* ) = &self.operators;
let total_weight: u16 = 0 $( + $W.get() )*;
let count: usize = 0 $( + { let _ = $W; 1 } )*;
let mut population = Population::with_capacity(target_size);
let mut remaining = target_size;
let mut current: usize = 0;
$(
current += 1;
let target = if current == count {
remaining
} else {
let t = ($W.get() as usize * target_size) / total_weight as usize;
remaining -= t;
t
};
let fill = Fill::from_fixed_size($Op, target);
let offspring = fill.apply(state, ctx);
population.add_offspring(offspring);
)*
Offspring::Multiple(population)
}
}
impl_genetic_proportional_recurse!($($Op, $W),+);
};
}
macro_rules! impl_genetic_proportional_recurse {
($OpHead:ident, $WHead:ident, $($OpRest:ident, $WRest:ident),+) => {
impl_genetic_proportional!($($OpRest, $WRest),+);
};
($OpHead:ident, $WHead:ident) => {};
}
impl_genetic_proportional!(
O1, W1, O2, W2, O3, W3, O4, W4, O5, W5, O6, W6, O7, W7, O8, W8, O9, W9, O10, W10, O11, W11,
O12, W12, O13, W13, O14, W14, O15, W15, O16, W16
);