~ruther/ctu-fee-eoa

38c9f26881203f8967502629c28219caf17827df — Rutherther a month ago f304d07
feat(lib): add two point and n point crossovers
1 files changed, 175 insertions(+), 3 deletions(-)

M codes/eoa_lib/src/crossover.rs
M codes/eoa_lib/src/crossover.rs => codes/eoa_lib/src/crossover.rs +175 -3
@@ 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<D: Dim, TOutput> {
    _phantom1: PhantomData<D>,
    _phantom2: PhantomData<TOutput>
}

impl<D: Dim, TOutput> BinaryTwoPointCrossover<D, TOutput>
where
    DefaultAllocator: Allocator<D>
{
    pub fn new() -> Self {
        Self {
            _phantom1: PhantomData,
            _phantom2: PhantomData,
        }
    }

    fn find_cross_points(&self, chromosome: &BinaryString<D>, 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<D, TOutput> Crossover<2> for BinaryTwoPointCrossover<D, TOutput>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    type Chromosome = BinaryString<D>;
    type Out = TOutput;

    fn crossover(
        &self,
        population: &EvaluatedPopulation<Self::Chromosome, Self::Out>,
        pairs: impl Iterator<Item = ParentPairing<2>>,
        rng: &mut dyn RngCore
    ) -> Population<Self::Chromosome> {

        let chromosome = &population.population[0].chromosome.vec;
        let len = population.population[0].chromosome.vec.len();
        let indices = OVector::<usize, D>::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<const N: usize, D: Dim, TOutput> {
    _phantom1: PhantomData<D>,
    _phantom2: PhantomData<TOutput>,
}

impl<const N: usize, D: Dim, TOutput> BinaryNPointCrossover<N, D, TOutput>
where
    DefaultAllocator: Allocator<D>
{
    pub fn new() -> Self {
        Self {
            _phantom1: PhantomData,
            _phantom2: PhantomData,
        }
    }

    fn find_cross_points(&self, chromosome: &BinaryString<D>, 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<const N: usize, D, TOutput> Crossover<2> for BinaryNPointCrossover<N, D, TOutput>
where
    D: Dim,
    DefaultAllocator: Allocator<D>
{
    type Chromosome = BinaryString<D>;
    type Out = TOutput;

    fn crossover(
        &self,
        population: &EvaluatedPopulation<Self::Chromosome, Self::Out>,
        pairs: impl Iterator<Item = ParentPairing<2>>,
        rng: &mut dyn RngCore
    ) -> Population<Self::Chromosome> {

        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<D: Dim, T: Scalar, TOutput> {
    _phantom1: PhantomData<D>,