use std::{convert::Infallible, marker::PhantomData}; use eoa_lib::{fitness::FitnessFunction, initializer::Initializer, perturbation::PerturbationOperator}; use itertools::Itertools; use nalgebra::{allocator::Allocator, distance, Const, DefaultAllocator, Dim, Dyn, OMatrix, OVector, Point, U1}; use rand::{seq::SliceRandom, Rng, RngCore}; #[derive(PartialEq, Clone, Debug)] pub struct TSPCity { point: Point } #[derive(PartialEq, Clone, Debug)] pub struct NodePermutation where DefaultAllocator: Allocator { permutation: OVector } /// An instance of TSP, a fully connected graph /// with cities that connect to each other. /// The D parameter represents the number of cities. #[derive(PartialEq, Clone, Debug)] pub struct TSPInstance where D: Dim, DefaultAllocator: Allocator { cities: Vec, distances: OMatrix } impl TSPInstance where { pub fn new_dyn(cities: Vec<(f64, f64)>) -> Self { let dim = Dyn(cities.len()); let cities = OMatrix::>::from_fn_generic(dim, Const::<2>, |i, j| if j == 0 { cities[i].0 } else { cities[i].1 }); TSPInstance::new(cities) } } impl TSPInstance> where { pub fn new_const(cities: Vec<(f64, f64)>) -> Self { let cities = OMatrix::, Const<2>>::from_fn(|i, j| if j == 0 { cities[i].0 } else { cities[i].1 }); TSPInstance::new(cities) } } impl TSPInstance where D: Dim, DefaultAllocator: Allocator, DefaultAllocator: Allocator, DefaultAllocator: Allocator>, { pub fn new(cities: OMatrix>) -> Self { let dim = cities.shape_generic().0; let cities = cities.column_iter() .map(|position| TSPCity { point: Point::::new(position[0], position[1]) } ) .collect::>(); let distances = OMatrix::from_fn_generic( dim, dim, |i, j| distance(&cities[i].point, &cities[j].point) ); Self { cities, distances } } } impl TSPInstance where D: Dim, DefaultAllocator: Allocator, DefaultAllocator: Allocator, { pub fn verify_solution(&self, solution: &NodePermutation) -> bool { let mut seen_vertices = OVector::from_element_generic( solution.permutation.shape_generic().0, solution.permutation.shape_generic().1, false ); for &vertex in solution.permutation.iter() { // This vertex index is out of bounds if vertex >= self.cities.len() { return false; } // A node is repeating if seen_vertices[vertex] { return false; } seen_vertices[vertex] = true; } true } pub fn solution_cost(&self, solution: &NodePermutation) -> f64 { solution.permutation .iter() .circular_tuple_windows() .map(|(&node1, &node2): (&usize, &usize)| self.distances[(node1, node2)]) .sum() } } impl FitnessFunction for TSPInstance where D: Dim, DefaultAllocator: Allocator, DefaultAllocator: Allocator, { type In = NodePermutation; type Out = f64; type Err = Infallible; fn fit(self: &Self, inp: &Self::In) -> Result { Ok(self.solution_cost(inp)) } } pub struct TSPRandomInitializer where D: Dim, DefaultAllocator: Allocator, { _phantom: PhantomData, rng: Box } impl Initializer> for TSPRandomInitializer where D: Dim, DefaultAllocator: Allocator, DefaultAllocator: Allocator, { fn initialize_single(&mut self, size: D) -> NodePermutation { let len = size.value(); let mut indices = OVector::::from_iterator_generic(size, U1, 0..len); indices.as_mut_slice().shuffle(&mut self.rng); NodePermutation { permutation: indices } } } pub struct SwapPerturbation { _phantom: PhantomData, rng: Box, } impl PerturbationOperator for SwapPerturbation where D: Dim, DefaultAllocator: Allocator, DefaultAllocator: Allocator, { type Chromosome = NodePermutation; fn perturb(self: &mut Self, chromosome: &Self::Chromosome) -> Self::Chromosome { let first = self.rng.random_range(0..=chromosome.permutation.len()); let second = self.rng.random_range(0..=chromosome.permutation.len()); let mut new = chromosome.clone(); ( new.permutation[first], new.permutation[second] ) = ( new.permutation[second], new.permutation[first] ); new } }