~ruther/ctu-fee-eoa

3dccde3fe70937fab3dca13360e8e7ae22026a8e — Rutherther a month ago 48d61c1
feat(tsp): add BinaryString representation of TSP
1 files changed, 172 insertions(+), 5 deletions(-)

M codes/tsp_hw01/src/tsp.rs
M codes/tsp_hw01/src/tsp.rs => codes/tsp_hw01/src/tsp.rs +172 -5
@@ 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();