use std::marker::PhantomData; use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, Scalar, U1}; use rand::{seq::IteratorRandom, Rng, RngCore}; use crate::{binary_string::BinaryString, pairing::ParentPairing, population::{EvaluatedPopulation, Population}}; pub trait Crossover { type Chromosome; type Out; fn crossover( &self, parents: &EvaluatedPopulation, pairs: impl Iterator>, rng: &mut dyn RngCore ) -> Population; } pub struct BinaryOnePointCrossover { _phantom1: PhantomData, _phantom2: PhantomData } impl BinaryOnePointCrossover where DefaultAllocator: Allocator { pub fn new() -> Self { Self { _phantom1: PhantomData, _phantom2: PhantomData, } } fn find_cross_point(&self, chromosome: &BinaryString, rng: &mut dyn RngCore) -> usize { let (min, max) = (0, chromosome.vec.len()); rng.random_range(min..max) } } // TODO: make common functions for ovector that will be used from both BinaryOnePointCrossover and OVectorOnePointCrossover // for not repeating the code. impl Crossover<2> for BinaryOnePointCrossover where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; type Out = TOutput; fn crossover( &self, population: &EvaluatedPopulation, pairs: impl Iterator>, rng: &mut dyn RngCore ) -> Population { let chromosome = &population.population[0].chromosome.vec; let len = population.population[0].chromosome.vec.len(); let indices = OVector::::from_iterator_generic(chromosome.shape_generic().0, U1, 0..len); let mut offsprings = Vec::new(); for pair in pairs { let ( parent1, parent2 ) = (&population.population[pair.x], &population.population[pair.y]); let ( mut chromosome1, mut chromosome2 ) = (&parent1.chromosome, &parent2.chromosome); if rng.random_bool(0.5) { (chromosome1, chromosome2) = (chromosome2, chromosome1) } let cross_point = self.find_cross_point(&population.population[0].chromosome, rng); offsprings.push(BinaryString::from_ovector( chromosome1.vec.zip_zip_map( &chromosome2.vec, &indices, |first, second, i| if i <= cross_point { first } else { second } ))); } Population::from_vec(offsprings) } } pub struct BinaryTwoPointCrossover { _phantom1: PhantomData, _phantom2: PhantomData } impl BinaryTwoPointCrossover where DefaultAllocator: Allocator { pub fn new() -> Self { Self { _phantom1: PhantomData, _phantom2: PhantomData, } } fn find_cross_points(&self, chromosome: &BinaryString, rng: &mut dyn RngCore) -> [usize; 2] { let (min, max) = (0, chromosome.vec.len()); let first = rng.random_range(min..max); let second = rng.random_range(min..max); [ first.min(second), first.max(second) ] } } impl Crossover<2> for BinaryTwoPointCrossover where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; type Out = TOutput; fn crossover( &self, population: &EvaluatedPopulation, pairs: impl Iterator>, rng: &mut dyn RngCore ) -> Population { let chromosome = &population.population[0].chromosome.vec; let len = population.population[0].chromosome.vec.len(); let indices = OVector::::from_iterator_generic(chromosome.shape_generic().0, U1, 0..len); let mut offsprings = Vec::new(); for pair in pairs { let ( parent1, parent2 ) = (&population.population[pair.x], &population.population[pair.y]); let ( mut chromosome1, mut chromosome2 ) = (&parent1.chromosome, &parent2.chromosome); if rng.random_bool(0.5) { (chromosome1, chromosome2) = (chromosome2, chromosome1) } let cross_point = self.find_cross_points(&population.population[0].chromosome, rng); offsprings.push(BinaryString::from_ovector( chromosome1.vec.zip_zip_map( &chromosome2.vec, &indices, |first, second, i| if i <= cross_point[0] { first } else if i < cross_point[1] { second } else { first } ))); } Population::from_vec(offsprings) } } pub struct BinaryNPointCrossover { _phantom1: PhantomData, _phantom2: PhantomData, } impl BinaryNPointCrossover where DefaultAllocator: Allocator { pub fn new() -> Self { Self { _phantom1: PhantomData, _phantom2: PhantomData, } } fn find_cross_points(&self, chromosome: &BinaryString, rng: &mut dyn RngCore) -> [usize; N] { let mut res = [0; N]; (0..chromosome.vec.len()).choose_multiple_fill(rng, &mut res); res.as_mut_slice().sort_unstable(); res } } impl Crossover<2> for BinaryNPointCrossover where D: Dim, DefaultAllocator: Allocator { type Chromosome = BinaryString; type Out = TOutput; fn crossover( &self, population: &EvaluatedPopulation, pairs: impl Iterator>, rng: &mut dyn RngCore ) -> Population { let chromosome = &population.population[0].chromosome; let len = chromosome.vec.len(); let mut offsprings = Vec::new(); for pair in pairs { let ( parent1, parent2 ) = (&population.population[pair.x], &population.population[pair.y]); let ( mut chromosome1, mut chromosome2 ) = (&parent1.chromosome.vec, &parent2.chromosome.vec); if rng.random_bool(0.5) { (chromosome1, chromosome2) = (chromosome2, chromosome1) } let parents = [chromosome1, chromosome2]; let cross_points = self.find_cross_points(chromosome, rng); let mut offspring = OVector::zeros_generic(chromosome1.shape_generic().0, U1); let mut start = 0; let mut parent = 0; for cross_point in cross_points { for i in start..cross_point { offspring[i] = parents[parent][i]; } parent = 1 - parent; // switch parent start = cross_point; } for i in start..len { offspring[i] = parents[parent][i]; } offsprings.push(BinaryString::from_ovector(offspring)); } Population::from_vec(offsprings) } } pub struct OVectorOnePointCrossover { _phantom1: PhantomData, _phantom2: PhantomData, _phantom3: PhantomData } impl OVectorOnePointCrossover where T: Scalar, DefaultAllocator: Allocator { pub fn new() -> Self { Self { _phantom1: PhantomData, _phantom2: PhantomData, _phantom3: PhantomData, } } fn find_cross_point(&self, chromosome: &OVector, rng: &mut dyn RngCore) -> usize { let (min, max) = (0, chromosome.len() - 1); rng.random_range(min..max) } } impl Crossover<2> for OVectorOnePointCrossover where T: Scalar, D: Dim, DefaultAllocator: Allocator { type Chromosome = OVector; type Out = TOutput; fn crossover( &self, population: &EvaluatedPopulation, pairs: impl Iterator>, rng: &mut dyn RngCore ) -> Population { let chromosome = &population.population[0].chromosome; let len = population.population[0].chromosome.len(); let indices = OVector::::from_iterator_generic(chromosome.shape_generic().0, U1, 0..len); let mut offsprings = Vec::new(); for pair in pairs { let ( parent1, parent2 ) = (&population.population[pair.x], &population.population[pair.y]); let ( chromosome1, chromosome2 ) = (&parent1.chromosome, &parent2.chromosome); let cross_point = self.find_cross_point(&population.population[0].chromosome, rng); offsprings.push( chromosome1.zip_zip_map( &chromosome2, &indices, |first, second, i| if i <= cross_point { first } else { second } )); } Population::from_vec(offsprings) } } pub struct ArithmeticCrossover { _phantom1: PhantomData, _phantom2: PhantomData, } impl ArithmeticCrossover { pub fn new() -> Self { Self { _phantom1: PhantomData, _phantom2: PhantomData, } } } impl Crossover<2> for ArithmeticCrossover where D: Dim, DefaultAllocator: Allocator { type Chromosome = OVector; type Out = TOutput; fn crossover( &self, population: &EvaluatedPopulation, pairs: impl Iterator>, rng: &mut dyn RngCore ) -> Population { let mut offsprings = Vec::new(); for pair in pairs { let ( parent1, parent2 ) = (&population.population[pair.x], &population.population[pair.y]); let ( chromosome1, chromosome2 ) = (&parent1.chromosome, &parent2.chromosome); offsprings.push( chromosome1.zip_map( &chromosome2, |first, second| { let alpha = rng.random_range(0.0..=1.0); alpha * first + (1.0 - alpha) * second } )); offsprings.push( chromosome1.zip_map( &chromosome2, |first, second| { let alpha = rng.random_range(0.0..=1.0); alpha * second + (1.0 - alpha) * first } )); } Population::from_vec(offsprings) } }