From be955686286443cb9a4dd270d844b9875b62a9e9 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 1 Nov 2025 19:41:55 +0100 Subject: [PATCH] feat(tsp): add MinimalSpanningTree initializer --- codes/tsp_hw01/src/initializers.rs | 131 ++++++++++++++++++++++++++++- 1 file changed, 127 insertions(+), 4 deletions(-) diff --git a/codes/tsp_hw01/src/initializers.rs b/codes/tsp_hw01/src/initializers.rs index 995af405ba297369e24878550732806e8ce5152d..2cc6ddc44786971f039e01bbcdd4c3d3566c86bc 100644 --- a/codes/tsp_hw01/src/initializers.rs +++ b/codes/tsp_hw01/src/initializers.rs @@ -1,8 +1,8 @@ use std::{cmp::Ordering, marker::PhantomData}; -use nalgebra::{allocator::Allocator, DefaultAllocator, Dim, OVector, U1}; -use rand::{prelude::SliceRandom, Rng, RngCore}; +use nalgebra::{allocator::Allocator, Const, DefaultAllocator, Dim, OVector, U1, U2}; +use rand::{prelude::SliceRandom, seq::IteratorRandom, Rng, RngCore}; use eoa_lib::initializer::Initializer; -use crate::tsp::{NodePermutation, TSPInstance}; +use crate::{graph::{Graph, minimal_spanning_tree_kruskal, GenericGraph}, tsp::{NodePermutation, TSPCity, TSPEdge, TSPInstance}}; pub struct TSPRandomInitializer where @@ -169,10 +169,133 @@ where } // 3. Initialize randomly with random p_choose_second (call initialize_single) - for i in 0..count { + for _ in 0..count { population.push(self.initialize_single(size, rng)) } + population + } +} + +pub struct MinimumSpanningTreeInitializer<'a, D: Dim> +where + DefaultAllocator: nalgebra::allocator::Allocator, + DefaultAllocator: nalgebra::allocator::Allocator +{ + instance: &'a TSPInstance, + minimal_spanning_tree: GenericGraph, + _phantom: PhantomData, +} + +impl<'a, D: Dim> MinimumSpanningTreeInitializer<'a, D> +where + DefaultAllocator: nalgebra::allocator::Allocator, + DefaultAllocator: nalgebra::allocator::Allocator, + DefaultAllocator: nalgebra::allocator::Allocator, +{ + pub fn new(instance: &'a TSPInstance) -> Self { + let tsp_graph = instance.clone().to_graph(); + let minimal_spanning_tree = + minimal_spanning_tree_kruskal( + &tsp_graph, + None, + |_| true + ); + + assert_eq!(minimal_spanning_tree.components_count(), 1); + let minimal_spanning_tree = tsp_graph + .filter_edges(|i, _| + minimal_spanning_tree.edges.contains(&i)); + + Self { + instance, + minimal_spanning_tree, + _phantom: PhantomData + } + } + + fn initialize_from( + &self, + node: usize, + size: D, + rng: &mut dyn RngCore + ) -> NodePermutation { + let mut used_nodes = vec![false; size.value()]; + let mut individual = + OVector::zeros_generic(size, U1); + + let mut current_node = node; + for i in 0..size.value()-1 { + individual[i] = current_node; + used_nodes[current_node] = true; + + let usable_neighbors = self.minimal_spanning_tree + .neighbor_idxs(current_node) + .unwrap() + .filter(|&neighbor| !used_nodes[neighbor]); + + let chosen = usable_neighbors.choose(rng); + + current_node = if let Some(next_node) = chosen { + next_node + } else { + // Choose closest node + (0..size.value()) + .filter(|&node| !used_nodes[node]) + .min_by(|&next_node1, &next_node2| + self.instance.distances(current_node, next_node1) + .partial_cmp(&self.instance.distances(current_node, next_node2)) + .unwrap_or(Ordering::Less)) + // There has to be unused node since we haven't yet used size.value() nodes. + .unwrap() + } + } + + // The last node + individual[size.value() - 1] = current_node; + + NodePermutation { permutation: individual } + } +} + +impl<'a, D: Dim> Initializer> for MinimumSpanningTreeInitializer<'a, D> +where + DefaultAllocator: nalgebra::allocator::Allocator, + DefaultAllocator: nalgebra::allocator::Allocator, + DefaultAllocator: nalgebra::allocator::Allocator, +{ + fn initialize_single(&self, size: D, rng: &mut dyn RngCore) -> NodePermutation { + let starting_node = rng.random_range(0..size.value()); + self.initialize_from(starting_node, size, rng) + } + + fn initialize( + &self, + size: D, + mut count: usize, + rng: &mut dyn RngCore + ) -> Vec> { + let mut population = Vec::with_capacity(count); + + // 1. Initialize from every node + for i in 0..size.value() { + population.push(self.initialize_from( + i, + size, + rng + )); + + count -= 1; + + if count == 0 { + return population; + } + } + + // 2. Initialize randomly with random p_choose_second (call initialize_single) + for _ in 0..count { + population.push(self.initialize_single(size, rng)); + } population }