use std::{convert::Infallible, error::Error};
use nalgebra::SVector;
use rand_distr::{Normal, NormalError};
use crate::{local_search::LocalSearchStats, perturbation::{apply_to_perturbations, PerturbationOperator, RandomDistributionParameter, RandomDistributionPerturbation}};
pub trait EvolutionaryStrategy<TOut, TPerturbation: PerturbationOperator> {
type Err: Error + 'static;
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>(params: &mut RandomDistributionParameter<Normal<f64>>, better: bool) -> Result<(), NormalError> {
let exp: f64 = if better { 1.0 } else { 0.0 } - 0.2;
let sigma = params.parameter;
let new_sigma = sigma * exp.exp().powf(1.0 / LEN as f64);
// Hopefully prevent cases when the sigma goes too low
let new_sigma = if new_sigma < 0.000000001 {
0.000000001
} else {
new_sigma
};
params.parameter = new_sigma;
Ok(())
}
pub struct OneToFiveStrategy;
impl<const LEN: usize,
TPerturbation: PerturbationOperator<Chromosome = SVector<f64, LEN>>,
TOut> EvolutionaryStrategy<TOut, TPerturbation> for OneToFiveStrategy {
type Err = NormalError;
fn step(&mut self,
perturbation: &mut TPerturbation,
better: bool,
_: &LocalSearchStats<SVector::<f64, LEN>, TOut>
) -> Result<(), Self::Err> {
let mut found = false;
let mut result = Ok(());
apply_to_perturbations::<_, RandomDistributionParameter<Normal<f64>>>(
perturbation,
&mut |params| {
found = true;
result = normal_one_to_five::<LEN>(params, better);
}
);
if !found {
panic!("There is no random distribution perturbation!");
}
result
}
}
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(())
}
}