From 38c9f26881203f8967502629c28219caf17827df Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 1 Nov 2025 10:22:48 +0100 Subject: [PATCH] feat(lib): add two point and n point crossovers --- codes/eoa_lib/src/crossover.rs | 178 ++++++++++++++++++++++++++++++++- 1 file changed, 175 insertions(+), 3 deletions(-) diff --git a/codes/eoa_lib/src/crossover.rs b/codes/eoa_lib/src/crossover.rs index cdc563201100c42cfb1adb1d29ae5c6b849467b5..1f5c62c1979a3e5468f399d0b796205ac5f1e626 100644 --- a/codes/eoa_lib/src/crossover.rs +++ b/codes/eoa_lib/src/crossover.rs @@ -1,7 +1,8 @@ use std::marker::PhantomData; use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, Scalar, U1}; -use rand::{Rng, RngCore}; +use rand::{seq::IteratorRandom, Rng, RngCore}; +use rand_distr::Uniform; use crate::{binary_string::BinaryString, pairing::ParentPairing, replacement::{EvaluatedPopulation, Population}}; @@ -69,10 +70,14 @@ where ) = (&population.population[pair.x], &population.population[pair.y]); let ( - chromosome1, - chromosome2 + 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( @@ -87,6 +92,173 @@ where } } +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,