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 multiple genes between the parents. The gene positions are chosen with uniform
/// probability.
/// Choose between allowing duplicate crossovers of the same gene or not (~2x slower).
///
/// 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 MultiGene<G: EvolveGenotype + SupportsGeneCrossover> {
    _phantom: PhantomData<G>,
    pub selection_rate: f32,
    pub crossover_rate: f32,
    pub crossover_sampler: Bernoulli,
    pub number_of_crossovers: usize,
    pub allow_duplicates: bool,
}
impl<G: EvolveGenotype + SupportsGeneCrossover> Crossover for MultiGene<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 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(
                    self.number_of_crossovers,
                    self.allow_duplicates,
                    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> MultiGene<G> {
    /// Create a new MultiGene 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)
    /// * `number_of_crossovers` - number of genes to exchange between parents
    /// * `allow_duplicates` - allow the same gene index to be selected twice
    pub fn new(
        selection_rate: f32,
        crossover_rate: f32,
        number_of_crossovers: usize,
        allow_duplicates: bool,
    ) -> Self {
        let crossover_sampler = Bernoulli::new(crossover_rate as f64).unwrap();
        Self {
            _phantom: PhantomData,
            selection_rate,
            crossover_rate,
            crossover_sampler,
            number_of_crossovers,
            allow_duplicates,
        }
    }
}