use std::marker::PhantomData;
use rand::{seq::SliceRandom, Rng};
use crate::{
fitness::{par_evaluate, MultiObjective},
repro_rng::thread_rng,
select::{Select, Stochastic},
utils::Cached,
Solution,
};
pub trait Algorithm<T: Solution> {
fn step(&self, population: &mut Vec<Cached<T>>);
fn pop_size(&self) -> usize;
}
#[derive(Clone, Debug)]
pub struct Simple<T, S>
where
T: Solution,
S: Select<T> + Stochastic,
{
pop_size: usize,
cxpb: f64,
mutpb: f64,
selector: S,
_phantom: PhantomData<T>,
}
impl<T, S> Simple<T, S>
where
T: Solution,
S: Select<T> + Stochastic,
{
pub fn new(pop_size: usize, cxpb: f64, mutpb: f64, selector: S) -> Self {
Simple {
pop_size,
cxpb,
mutpb,
selector,
_phantom: PhantomData,
}
}
}
impl<T, S> Algorithm<T> for Simple<T, S>
where
T: Solution,
S: Select<T> + Stochastic,
{
fn pop_size(&self) -> usize {
self.pop_size
}
fn step(&self, population: &mut Vec<Cached<T>>) {
debug_assert_eq!(self.pop_size, population.len());
self.selector.select(self.pop_size, population);
var_and(population, self.cxpb, self.mutpb);
}
}
#[derive(Clone, Debug)]
pub struct MuPlusLambda<T, S>
where
T: Solution,
S: Select<T>,
{
mu: usize,
lambda: usize,
cxpb: f64,
mutpb: f64,
selector: S,
_phantom: PhantomData<T>,
}
impl<T, S> MuPlusLambda<T, S>
where
T: Solution,
S: Select<T>,
{
pub fn new(mu: usize, lambda: usize, cxpb: f64, mutpb: f64, selector: S) -> Self {
MuPlusLambda {
mu,
lambda,
cxpb,
mutpb,
selector,
_phantom: PhantomData,
}
}
}
impl<T, S> Algorithm<T> for MuPlusLambda<T, S>
where
T: Solution,
S: Select<T>,
{
fn pop_size(&self) -> usize {
self.mu
}
fn step(&self, population: &mut Vec<Cached<T>>) {
population.append(&mut gen_or(population, self.lambda, self.cxpb, self.mutpb));
par_evaluate(population);
self.selector.select(self.mu, population);
}
}
#[derive(Clone, Debug)]
pub struct MuCommaLambda<T, S>
where
T: Solution,
S: Select<T>,
{
mu: usize,
lambda: usize,
cxpb: f64,
mutpb: f64,
selector: S,
_phantom: PhantomData<T>,
}
impl<T, S> MuCommaLambda<T, S>
where
T: Solution,
S: Select<T>,
{
pub fn new(mu: usize, lambda: usize, cxpb: f64, mutpb: f64, selector: S) -> Self {
if mu > lambda {
panic!("(μ, λ) requires μ < λ");
}
MuCommaLambda {
mu,
lambda,
cxpb,
mutpb,
selector,
_phantom: PhantomData,
}
}
}
impl<T, S> Algorithm<T> for MuCommaLambda<T, S>
where
T: Solution,
S: Select<T>,
{
fn pop_size(&self) -> usize {
self.mu
}
fn step(&self, population: &mut Vec<Cached<T>>) {
*population = gen_or(population, self.lambda, self.cxpb, self.mutpb);
par_evaluate(population);
self.selector.select(self.mu, population);
}
}
#[derive(Clone, Debug)]
pub struct NSGA2 {
pop_size: usize,
cxpb: f64,
mutpb: f64,
}
impl NSGA2 {
pub fn new(pop_size: usize, cxpb: f64, mutpb: f64) -> Self {
NSGA2 {
pop_size,
cxpb,
mutpb,
}
}
}
impl<T, const M: usize> Algorithm<T> for NSGA2
where
T: Solution<Fitness = MultiObjective<M>>,
{
fn pop_size(&self) -> usize {
self.pop_size
}
fn step(&self, population: &mut Vec<Cached<T>>) {
population.append(&mut gen_or(
population,
self.pop_size,
self.cxpb,
self.mutpb,
));
par_evaluate(population);
crate::select::NSGA2.select(self.pop_size, population);
}
}
pub fn var_and<T>(pop: &mut [T], cxpb: f64, mutpb: f64)
where
T: Solution,
{
let mut rng = thread_rng();
for i in 0..pop.len() {
if i != 0 && rng.gen_bool(cxpb) {
let (head, tail) = pop.split_at_mut(i);
let a = head.last_mut().unwrap();
let b = tail.first_mut().unwrap();
T::crossover(a, b);
}
if rng.gen_bool(mutpb) {
pop[i].mutate();
}
}
}
pub fn gen_or<T: Solution>(pop: &[T], n_offspring: usize, cxpb: f64, mutpb: f64) -> Vec<T> {
let mut offspring: Vec<T> = Vec::with_capacity(n_offspring);
for _ in 0..n_offspring {
let mut rng = thread_rng();
let choice: f64 = rng.gen();
offspring.push(if choice < cxpb {
let mut iter = pop.choose_multiple(&mut rng, 2).cloned();
let mut a = iter.next().unwrap();
let mut b = iter.next().unwrap();
T::crossover(&mut a, &mut b);
a
} else if choice < cxpb + mutpb {
let mut chosen = pop.choose(&mut rng).unwrap().clone();
chosen.mutate();
chosen
} else {
pop.choose(&mut rng).unwrap().clone()
});
}
offspring
}