use std::convert::Infallible;
use nalgebra::SVector;
use rand_distr::{Normal, NormalError};
use crate::{local_search::{LocalSearchCandidate, LocalSearchStats}, perturbation::{BoundedPerturbation, PerturbationOperator, RandomDistributionPerturbation}};
pub trait EvolutionaryStrategy<TOut, TPerturbation: PerturbationOperator> {
type Err;
fn step(&mut self,
perturbation: &mut TPerturbation,
better: bool,
stats: &LocalSearchStats<TPerturbation::Chromosome, TOut>
) -> Result<(), Self::Err>;
}
fn normal_one_to_five<const LEN: usize>(perturbation: &mut RandomDistributionPerturbation<LEN, Normal<f64>>, better: bool) -> Result<(), NormalError> {
let exp: f64 = if better { 1.0 } else { 0.0 } - 0.2;
let sigma = perturbation.std_dev();
let new_sigma = sigma * exp.exp().powf(1.0 / LEN as f64);
perturbation.set_std_dev(new_sigma)?;
Ok(())
}
pub struct OneToFiveStrategy;
impl<const LEN: usize, TOut> EvolutionaryStrategy<TOut, RandomDistributionPerturbation<LEN, Normal<f64>>> for OneToFiveStrategy {
type Err = NormalError;
fn step(&mut self,
perturbation: &mut RandomDistributionPerturbation<LEN, Normal<f64>>,
better: bool,
_: &LocalSearchStats<SVector::<f64, LEN>, TOut>
) -> Result<(), Self::Err> {
normal_one_to_five(perturbation, better)
}
}
// TODO: I don't really like this to be honest. This would basically have to take care of any perturbation wrapper
// that there is. But that just seems wrong.
impl<const LEN: usize, TOut> EvolutionaryStrategy<TOut, BoundedPerturbation<LEN, RandomDistributionPerturbation<LEN, Normal<f64>>>> for OneToFiveStrategy {
type Err = NormalError;
fn step(&mut self,
perturbation: &mut BoundedPerturbation<LEN, RandomDistributionPerturbation<LEN, Normal<f64>>>,
better: bool,
_: &LocalSearchStats<<BoundedPerturbation<LEN, RandomDistributionPerturbation<LEN, Normal<f64>>> as PerturbationOperator>::Chromosome, TOut>
) -> Result<(), Self::Err> {
normal_one_to_five(perturbation.inner_mut(), better)
}
}
pub struct IdentityStrategy;
impl<TOut, TPerturbation: PerturbationOperator> EvolutionaryStrategy<TOut, TPerturbation> for IdentityStrategy {
type Err = Infallible;
fn step(&mut self,
_: &mut TPerturbation,
_: bool,
_: &LocalSearchStats<TPerturbation::Chromosome, TOut>
) -> Result<(), Self::Err> {
Ok(())
}
}