radiate-selectors 1.3.0

Selection strategies for the Radiate genetic algorithm library.
Documentation
use crate::ProbabilityWheelIterator;
use radiate_core::{Chromosome, Objective, Optimize, Phenotype, Select, math::norm, pareto};
use radiate_utils::MinMax;

const MIN: f32 = 1e-6;

#[derive(Debug, Clone, Default)]
pub struct BoltzmannSelector {
    temperature: f32,
}

impl BoltzmannSelector {
    pub fn new(temperature: f32) -> Self {
        BoltzmannSelector { temperature }
    }

    fn apply_boltzmann(&self, weights: &mut [f32]) {
        let mut minmax = MinMax::default();
        for &score in weights.iter() {
            minmax.add(&score);
        }

        let min = minmax.min();
        let diff = minmax.range().abs().max(MIN);

        for score in weights.iter_mut() {
            *score = (self.temperature * ((*score - min) / diff)).exp();
        }
    }
}

impl<C: Chromosome> Select<C> for BoltzmannSelector {
    #[inline]
    fn select(
        &self,
        population: &[Phenotype<C>],
        objective: &Objective,
        count: usize,
    ) -> Vec<usize> {
        let fitness_values = match objective {
            Objective::Single(opt) => {
                let mut fitness_values = population
                    .iter()
                    .filter_map(|p| p.score().and_then(|score| score.first()))
                    .collect::<Vec<_>>();

                self.apply_boltzmann(&mut fitness_values);
                norm::scale_l1_affine_sorted(&mut fitness_values);

                if let Optimize::Minimize = opt {
                    fitness_values.reverse();
                }

                fitness_values
            }
            Objective::Multi(_) => {
                let scores = population
                    .iter()
                    .filter_map(|p| p.score())
                    .collect::<Vec<_>>();

                let mut weights = pareto::weights(&scores, objective);
                self.apply_boltzmann(&mut weights);
                norm::scale_l1(&mut weights);

                weights
            }
        };

        ProbabilityWheelIterator::new(fitness_values, count).collect()
    }
}