From 44fb8c9207de16114ae0b0017575ba6b94f3ecb6 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 1 Nov 2025 14:24:24 +0100 Subject: [PATCH] feat(tsp): add cycle, no and partially mapped crossovers --- codes/tsp_hw01/src/crossovers.rs | 200 ++++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 1 deletion(-) diff --git a/codes/tsp_hw01/src/crossovers.rs b/codes/tsp_hw01/src/crossovers.rs index 27f1ae423935b4e081aed3a49261a3fca04c4c39..581dddd9effc6c3d4a116071d5be358abb1a5391 100644 --- a/codes/tsp_hw01/src/crossovers.rs +++ b/codes/tsp_hw01/src/crossovers.rs @@ -1,11 +1,45 @@ use std::marker::PhantomData; -use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, Const, OMatrix, OVector}; +use nalgebra::{allocator::Allocator, Const, DefaultAllocator, Dim, OMatrix, OVector, U1}; use rand::{prelude::IteratorRandom, Rng, RngCore}; use eoa_lib::replacement::Population; use itertools::Itertools; use eoa_lib::crossover::Crossover; use crate::tsp::NodePermutation; +pub struct NoCrossover { + _phantom: PhantomData +} + +impl NoCrossover { + pub fn new() -> Self { + Self { _phantom: PhantomData } + } +} + +impl Crossover<2> for NoCrossover +where + D: Dim, + DefaultAllocator: Allocator, +{ + type Chromosome = NodePermutation; + type Out = f64; + + fn crossover( + &self, + parents: &eoa_lib::replacement::EvaluatedPopulation, + pairs: impl Iterator>, + _: &mut dyn RngCore + ) -> Population { + let mut offsprings = vec![]; + for pair in pairs { + offsprings.push(parents.population[pair[0]].chromosome.clone()); + offsprings.push(parents.population[pair[1]].chromosome.clone()); + } + + Population::from_vec(offsprings) + } +} + pub struct EdgeRecombinationCrossover { _phantom: PhantomData } @@ -138,3 +172,167 @@ where Population::from_vec(offsprings) } } + +pub struct CycleCrossover { + _phantom: PhantomData +} + +impl CycleCrossover +where + D: Dim, + DefaultAllocator: Allocator, +{ + pub fn new() -> Self { + Self { _phantom: PhantomData } + } + + fn perform_crossover( + &self, + parent1: &OVector, + parent2: &OVector, + city_positions: &mut [usize] + ) -> NodePermutation { + let mut offspring = parent2.clone(); + + for (i, &city) in parent1.iter().enumerate() { + city_positions[city] = i; + } + + let mut i = 0; + let mut first = true; + while i != 0 || first { + first = false; + + let city = parent1[i]; + + offspring[i] = city; + + let city = parent2[i]; + i = city_positions[city]; + } + + NodePermutation { permutation: offspring } + } +} + +impl Crossover<2> for CycleCrossover +where + D: Dim, + DefaultAllocator: Allocator, +{ + type Chromosome = NodePermutation; + type Out = f64; + + fn crossover( + &self, + parents: &eoa_lib::replacement::EvaluatedPopulation, + pairs: impl Iterator>, + rng: &mut dyn RngCore + ) -> Population { + let mut offsprings = vec![]; + + let permutation = &parents.population[0].chromosome.permutation; + let mut city_positions = + OVector::zeros_generic(permutation.shape_generic().0, U1); + + for pair in pairs { + let parent1 = &parents.population[pair.x].chromosome; + let parent2 = &parents.population[pair.y].chromosome; + + let (perm1, perm2) = ( + &parent1.permutation, + &parent2.permutation + ); + + offsprings.push(self.perform_crossover(perm1, perm2, city_positions.as_mut_slice())); + offsprings.push(self.perform_crossover(perm2, perm1, city_positions.as_mut_slice())); + } + + Population::from_vec(offsprings) + } +} + +pub struct PartiallyMappedCrossover { + _phantom: PhantomData +} + +impl PartiallyMappedCrossover +where + D: Dim, + DefaultAllocator: Allocator, +{ + pub fn new() -> Self { + Self { _phantom: PhantomData } + } + + fn find_cross_points(&self, chromosome: &NodePermutation, rng: &mut dyn RngCore) -> [usize; 2] { + let (min, max) = (0, chromosome.permutation.len()); + let first = rng.random_range(min..max); + let second = rng.random_range(min..max); + + [ first.min(second), first.max(second) ] + } + + fn perform_crossover( + &self, + parent1: &OVector, + parent2: &OVector, + crossover_points: &[usize; 2], + city_positions: &mut [usize] + ) -> NodePermutation { + let mut offspring = parent1.clone(); + + for (i, &city) in parent1.iter().enumerate() { + city_positions[city] = i; + } + + for i in crossover_points[0]..crossover_points[1] { + let city = parent2[i]; + + offspring.swap_rows( + i, + city_positions[city] + ); + } + + NodePermutation { permutation: offspring } + } +} + +impl Crossover<2> for PartiallyMappedCrossover +where + D: Dim, + DefaultAllocator: Allocator, +{ + type Chromosome = NodePermutation; + type Out = f64; + + fn crossover( + &self, + parents: &eoa_lib::replacement::EvaluatedPopulation, + pairs: impl Iterator>, + rng: &mut dyn RngCore + ) -> Population { + let mut offsprings = vec![]; + + let permutation = &parents.population[0].chromosome.permutation; + let mut city_positions = + OVector::zeros_generic(permutation.shape_generic().0, U1); + + for pair in pairs { + let parent1 = &parents.population[pair.x].chromosome; + let parent2 = &parents.population[pair.y].chromosome; + + let (perm1, perm2) = ( + &parent1.permutation, + &parent2.permutation + ); + + let cross_points = self.find_cross_points(parent1, rng); + offsprings.push(self.perform_crossover(perm1, perm2, &cross_points, city_positions.as_mut_slice())); + offsprings.push(self.perform_crossover(perm2, perm1, &cross_points, city_positions.as_mut_slice())); + } + + Population::from_vec(offsprings) + } +}