@@ 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<DIn: Dim, DOut: Dim> {
+ dim_in: DIn,
+ dim_out: DOut,
+}
+
+impl<DIn: Dim, DOut: Dim> BinaryStringToNodePermutation<DIn, DOut> {
+ 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<DIn: Dim, DOut: Dim> FitnessFunction for BinaryStringToNodePermutation<DIn, DOut>
+where
+ DefaultAllocator: Allocator<DIn>,
+ DefaultAllocator: Allocator<DOut>,
+ DefaultAllocator: Allocator<DOut, DOut>,
+{
+ type In = BinaryString<DIn>;
+ type Out = NodePermutation<DOut>;
+ type Err = DimensionMismatch;
+
+ fn fit(self: &Self, inp: &Self::In) -> Result<Self::Out, Self::Err> {
+ let nodes = self.dim_out.value();
+ let mut orderings =
+ OMatrix::<Ordering, DOut, DOut>::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 {
@@ 479,6 553,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::<U15>::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::<U15>::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::<U15>::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::<Const<10>>::new();
let initializer = TSPRandomInitializer::<Const<10>>::new();