~ruther/ctu-fee-eoa

8eaf8145d149098a0ef70811ce8ea9c98447eb11 — Rutherther a month ago ec7345a
fix(tsp): properly implement binary string -> node permutation fitness
1 files changed, 77 insertions(+), 46 deletions(-)

M codes/tsp_hw01/src/tsp.rs
M codes/tsp_hw01/src/tsp.rs => codes/tsp_hw01/src/tsp.rs +77 -46
@@ 452,89 452,120 @@ where
    }
}

pub struct BinaryStringToNodePermutation<DIn: Dim, DOut: Dim> {
pub struct TSPBinaryStringWrapper<'a, DIn: Dim, DOut: Dim>
where
    DOut: Dim,
    DefaultAllocator: Allocator<DOut, DOut>
{
    instance: &'a TSPInstance<DOut>,
    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 {
impl<'a, DIn: Dim, DOut: Dim> TSPBinaryStringWrapper<'a, DIn, DOut>
where
    DOut: Dim,
    DefaultAllocator: Allocator<DOut, DOut>,
    DefaultAllocator: Allocator<DIn>,
    DefaultAllocator: Allocator<DOut>,
{
    pub fn new(
        instance: &'a TSPInstance<DOut>,
        dim_in: DIn,
        dim_out: DOut
    ) -> Result<Self, DimensionMismatch> {
        let res = Self {
            instance,
            dim_in,
            dim_out
        };

        if dim_out.value() * (dim_out.value() - 1) / 2 != dim_in.value() {
            Err(DimensionMismatch::Mismatch)
        } else {
            Ok(res)
        }
    }
}

#[derive(Error, Debug)]
pub enum DimensionMismatch {
    #[error("The input dimension should be equal to half matrix NxN where the output is N")]
    Mismatch
}
    pub fn to_permutation(&self, inp: &BinaryString<DIn>) -> Result<NodePermutation<DOut>, DimensionMismatch> {
        let nodes = self.dim_out.value();

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;
        if inp.vec().shape_generic().0.value() != self.dim_in.value() {
            return Err(DimensionMismatch::Mismatch);
        }

    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);
        // Count how many nodes each node comes after (precedence count)
        let mut precedence_count = OVector::<usize, DOut>::zeros_generic(self.dim_out, U1);

        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
                };
            for j in i+1..nodes {
                if in_index >= inp.vec.len() {
                    return Err(DimensionMismatch::Mismatch);
                }

                orderings[(j, i)] = if inp.vec[in_index] == 1 {
                    Ordering::Less
                if inp.vec[in_index] == 1 {
                    // i comes before j, so j has one more predecessor
                    precedence_count[j] += 1;
                } else {
                    Ordering::Greater
                };
                    // j comes before i, so i has one more predecessor
                    precedence_count[i] += 1;
                }

                in_index += 1;
            }
        }

        let mut result =
            OVector::from_iterator_generic(self.dim_out, U1, 0..nodes);
        if in_index != inp.vec.len() {
            return Err(DimensionMismatch::Mismatch);
        }

        for i in 0..nodes {
            for j in i+1..nodes {
                let node1 = result[i];
                let node2 = result[j];
        let mut result = OVector::from_iterator_generic(
            self.dim_out,
            U1,
            0..nodes
        );

                if orderings[(node1, node2)] == Ordering::Greater {
                    result.swap_rows(i, j);
                }
            }
        }
        result
            .as_mut_slice()
            .sort_by_key(|&node| precedence_count[node]);

        Ok(NodePermutation { permutation: result })
    }
}

#[derive(Error, Debug)]
pub enum DimensionMismatch {
    #[error("The input dimension should be equal to half matrix NxN where the output is N")]
    Mismatch
}

impl<'a, DIn: Dim, DOut: Dim> FitnessFunction for TSPBinaryStringWrapper<'a, DIn, DOut>
where
    DefaultAllocator: Allocator<DIn>,
    DefaultAllocator: Allocator<DOut>,
    DefaultAllocator: Allocator<DOut, DOut>,
{
    type In = BinaryString<DIn>;
    type Out = f64;
    type Err = DimensionMismatch;

    fn fit(self: &Self, inp: &Self::In) -> Result<Self::Out, Self::Err> {
        Ok(self.instance.fit(&self.to_permutation(inp)?).unwrap())
    }
}

#[cfg(test)]
mod tests {
    use std::convert::Infallible;

    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 rand::{rngs::StdRng, seq::SliceRandom, RngCore, SeedableRng};

    use crate::tsp::TSPInstance;

    use super::{BinaryStringToNodePermutation, EdgeRecombinationCrossover, NodePermutation, ReverseSubsequencePerturbation, TSPRandomInitializer};
    use super::{TSPBinaryStringWrapper, EdgeRecombinationCrossover, NodePermutation, ReverseSubsequencePerturbation, TSPRandomInitializer};
    use eoa_lib::perturbation::PerturbationOperator;

    struct MockRng;