From 3dccde3fe70937fab3dca13360e8e7ae22026a8e Mon Sep 17 00:00:00 2001 From: Rutherther Date: Fri, 31 Oct 2025 11:18:45 +0100 Subject: [PATCH] feat(tsp): add BinaryString representation of TSP --- codes/tsp_hw01/src/tsp.rs | 177 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 172 insertions(+), 5 deletions(-) diff --git a/codes/tsp_hw01/src/tsp.rs b/codes/tsp_hw01/src/tsp.rs index ed0018ce3ccc2db3b1f2a38fff1de9b78f0f8dfd..35c2ad5cfcea3ecbabff1ad07446129cc97351e9 100644 --- a/codes/tsp_hw01/src/tsp.rs +++ b/codes/tsp_hw01/src/tsp.rs @@ -1,10 +1,11 @@ -use std::{convert::Infallible, marker::PhantomData}; +use std::{cmp::Ordering, convert::Infallible, error::Error, marker::PhantomData}; -use eoa_lib::{crossover::Crossover, fitness::FitnessFunction, initializer::Initializer, perturbation::PerturbationOperator, replacement::Population}; +use eoa_lib::{binary_string::BinaryString, crossover::Crossover, fitness::FitnessFunction, initializer::Initializer, perturbation::PerturbationOperator, replacement::Population}; use itertools::Itertools; use nalgebra::{allocator::Allocator, distance, Const, DefaultAllocator, Dim, Dyn, OMatrix, OVector, Point, U1}; use plotters::prelude::*; use rand::{seq::{IteratorRandom, SliceRandom}, Rng, RngCore}; +use thiserror::Error; use crate::graph::Edge; @@ -440,17 +441,90 @@ where } } +pub struct BinaryStringToNodePermutation { + dim_in: DIn, + dim_out: DOut, +} + +impl BinaryStringToNodePermutation { + pub fn new(dim_in: DIn, dim_out: DOut) -> Self { + Self { + dim_in, + dim_out + } + } +} + +#[derive(Error, Debug)] +pub enum DimensionMismatch { + #[error("The input dimension should be equal to half matrix NxN where the output is N")] + Mismatch +} + +impl FitnessFunction for BinaryStringToNodePermutation +where + DefaultAllocator: Allocator, + DefaultAllocator: Allocator, + DefaultAllocator: Allocator, +{ + type In = BinaryString; + type Out = NodePermutation; + type Err = DimensionMismatch; + + fn fit(self: &Self, inp: &Self::In) -> Result { + let nodes = self.dim_out.value(); + let mut orderings = + OMatrix::::from_element_generic(self.dim_out, self.dim_out, Ordering::Equal); + + let mut in_index = 0usize; + for i in 0..self.dim_out.value() { + for j in i+1..self.dim_out.value() { + orderings[(i, j)] = if inp.vec[in_index] == 1 { + Ordering::Greater + } else { + Ordering::Less + }; + + orderings[(j, i)] = if inp.vec[in_index] == 1 { + Ordering::Less + } else { + Ordering::Greater + }; + + in_index += 1; + } + } + + let mut result = + OVector::from_iterator_generic(self.dim_out, U1, 0..nodes); + + for i in 0..nodes { + for j in i+1..nodes { + let node1 = result[i]; + let node2 = result[j]; + + if orderings[(node1, node2)] == Ordering::Greater { + result.swap_rows(i, j); + } + } + } + + Ok(NodePermutation { permutation: result }) + } +} + #[cfg(test)] mod tests { use std::convert::Infallible; - use eoa_lib::{crossover::Crossover, fitness::FitnessFunction, initializer::Initializer, pairing::{AdjacentPairing, Pairing}, replacement::Population}; - use nalgebra::{Const, SVector, U2, U6}; + use eoa_lib::{binary_string::BinaryString, crossover::Crossover, fitness::FitnessFunction, initializer::Initializer, pairing::{AdjacentPairing, Pairing}, replacement::Population}; + use nalgebra::{Const, SVector, U15, U6}; use rand::{rngs::StdRng, RngCore, SeedableRng}; use crate::tsp::TSPInstance; - use super::{EdgeRecombinationCrossover, NodePermutation, TSPRandomInitializer}; + use super::{BinaryStringToNodePermutation, EdgeRecombinationCrossover, NodePermutation, ReverseSubsequencePerturbation, TSPRandomInitializer}; + use eoa_lib::perturbation::PerturbationOperator; struct MockRng; impl RngCore for MockRng { @@ -478,6 +552,99 @@ mod tests { } } + #[test] + fn test_binary_string_representation() { + // x 0 1 2 3 4 5 + // 0 0 0 0 0 0 0 + // 1 1 0 0 0 0 0 + // 2 1 1 0 0 0 0 + // 3 1 1 1 0 0 0 + // 4 1 1 1 1 0 0 + // 5 1 1 1 1 1 0 + + // x 0 1 2 3 4 5 + // 0 0 0 0 0 0 + // 1 0 0 0 0 + // 2 0 0 0 + // 3 0 0 + // 4 0 + // 5 + + // 6 nodes + // length of binary string: 5 + 4 + 3 + 2 + 1 = 15 + + let converter = BinaryStringToNodePermutation::new( + U15, + U6 + ); + + let binary_string_ordering = BinaryString::::new(vec![1; 15]); + + let mut expected_permutation = vec![0, 1, 2, 3, 4, 5]; + expected_permutation.reverse(); + + let mut permutation = converter.fit(&binary_string_ordering) + .unwrap(); + + assert_eq!( + expected_permutation, + permutation.permutation.as_mut_slice().to_vec() + ); + + let binary_string_ordering = BinaryString::::new(vec![0; 15]); + expected_permutation.reverse(); + + let mut permutation = converter.fit(&binary_string_ordering) + .unwrap(); + + assert_eq!( + expected_permutation, + permutation.permutation.as_mut_slice().to_vec() + ) + } + #[test] + fn test_nontrivial_binary_string_representation() { + // x 0 1 2 3 4 5 + // 0 0 1 0 0 0 0 + // 1 0 0 0 0 0 0 + // 2 1 1 0 0 0 1 + // 3 1 1 1 0 0 0 + // 4 1 1 1 1 0 0 + // 5 1 1 0 1 1 0 + + // x 0 1 2 3 4 5 + // 0 0 0 0 0 0 + // 1 0 0 0 0 + // 2 1 1 1 + // 3 0 0 + // 4 1 + // 5 + + // 6 nodes + // length of binary string: 5 + 4 + 3 + 2 + 1 = 15 + + let converter = BinaryStringToNodePermutation::new( + U15, + U6 + ); + + let mut binary_string_ordering = BinaryString::::new(vec![0; 15]); + binary_string_ordering.vec[9] = 1; + binary_string_ordering.vec[10] = 1; + binary_string_ordering.vec[11] = 1; + binary_string_ordering.vec[14] = 1; + + let expected_permutation = vec![0, 1, 3, 5, 4, 2]; + + let mut permutation = converter.fit(&binary_string_ordering) + .unwrap(); + + assert_eq!( + expected_permutation, + permutation.permutation.as_mut_slice().to_vec() + ); + } + #[test] fn test_edge_recombination_properties() { let crossover = EdgeRecombinationCrossover::>::new();