genetic_algorithm 0.27.1

A genetic algorithm implementation
Documentation
use super::Crossover;
use crate::genotype::{EvolveGenotype, SupportsGeneCrossover};
use crate::strategy::evolve::{EvolveConfig, EvolveState};
use crate::strategy::{StrategyAction, StrategyReporter, StrategyState};
use itertools::Itertools;
use rand::distributions::{Bernoulli, Distribution};
use rand::Rng;
use std::marker::PhantomData;
use std::time::Instant;

/// Crossover with 50% probability for each gene to come from one of the two parents.
/// Actually implemented as `CrossoverMultiGene::new(.., <genes_size> / 2, allow_duplicates=true)`
///
/// Not allowed for [UniqueGenotype](crate::genotype::UniqueGenotype) and
/// [MultiUniqueGenotype](crate::genotype::MultiUniqueGenotype) as it would not preserve the gene
/// uniqueness in the children.
#[derive(Clone, Debug)]
pub struct Uniform<G: EvolveGenotype + SupportsGeneCrossover> {
    _phantom: PhantomData<G>,
    pub selection_rate: f32,
    pub crossover_rate: f32,
    pub crossover_sampler: Bernoulli,
}

impl<G: EvolveGenotype + SupportsGeneCrossover> Crossover for Uniform<G> {
    type Genotype = G;

    fn call<R: Rng, SR: StrategyReporter<Genotype = G>>(
        &mut self,
        genotype: &G,
        state: &mut EvolveState<G>,
        _config: &EvolveConfig,
        _reporter: &mut SR,
        rng: &mut R,
    ) {
        let now = Instant::now();
        let number_of_crossovers = genotype.genes_size() / 2;
        let existing_population_size = state.population.chromosomes.len();
        let selected_population_size =
            (existing_population_size as f32 * self.selection_rate).ceil() as usize;
        state
            .population
            .extend_from_within(selected_population_size);
        let iterator = state
            .population
            .chromosomes
            .iter_mut()
            .skip(existing_population_size);
        for (father, mother) in iterator.tuples() {
            if self.crossover_sampler.sample(rng) {
                genotype.crossover_chromosome_genes(
                    number_of_crossovers,
                    true,
                    father,
                    mother,
                    rng,
                );
            } else {
                father.reset_age();
                mother.reset_age();
            }
        }
        if selected_population_size % 2 == 1 {
            if let Some(chromosome) = state.population.chromosomes.last_mut() {
                chromosome.reset_age();
            }
        }

        state.add_duration(StrategyAction::Crossover, now.elapsed());
    }
}

impl<G: EvolveGenotype + SupportsGeneCrossover> Uniform<G> {
    /// Create a new Uniform crossover strategy.
    /// * `selection_rate` - fraction of parents selected for reproduction (0.5-0.8 typical)
    /// * `crossover_rate` - probability parent pair crosses over vs cloning (0.5-0.9 typical)
    pub fn new(selection_rate: f32, crossover_rate: f32) -> Self {
        let crossover_sampler = Bernoulli::new(crossover_rate as f64).unwrap();
        Self {
            _phantom: PhantomData,
            selection_rate,
            crossover_rate,
            crossover_sampler,
        }
    }
}