From c90789c104b6d77738efdef0828a4d101b54c550 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Tue, 28 Oct 2025 18:20:03 +0100 Subject: [PATCH] feat(tsp): add data loading --- codes/tsp_hw01/Cargo.toml | 1 + codes/tsp_hw01/src/main.rs | 98 ++++++++++++++++++++++++++++++++++++-- codes/tsp_hw01/src/tsp.rs | 4 ++ 3 files changed, 100 insertions(+), 3 deletions(-) diff --git a/codes/tsp_hw01/Cargo.toml b/codes/tsp_hw01/Cargo.toml index ba5531cc3db55a2c6e330056c16db52fe8f5b3ab..654bcaabc7bae1a15fc5ad1658c4a14827f3e5f7 100644 --- a/codes/tsp_hw01/Cargo.toml +++ b/codes/tsp_hw01/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] eoa_lib = { path = "../eoa_lib" } +flate2 = "1.0" itertools = "0.14.0" nalgebra = "0.33.2" plotters = "0.3" diff --git a/codes/tsp_hw01/src/main.rs b/codes/tsp_hw01/src/main.rs index 50276aa691f7e04cd15afc6d298e49288f7f82c5..62984b3592da7bc81b4e950d8cc93da8529ceb89 100644 --- a/codes/tsp_hw01/src/main.rs +++ b/codes/tsp_hw01/src/main.rs @@ -1,8 +1,100 @@ pub mod tsp; pub mod graph; -use eoa_lib::local_search::local_search_first_improving; +use tsp::{TSPInstance, TSPRandomInitializer, SwapPerturbation}; +use nalgebra::{Const, Dyn}; +use eoa_lib::{ + initializer::Initializer, + local_search::local_search_first_improving, + terminating::NoBetterForCyclesTerminatingCondition, + comparison::MinimizingOperator, +}; +use rand::rng; +use std::fs::File; +use std::io::{BufRead, BufReader, Read}; +use flate2::read::GzDecoder; -fn main() { - println!("Hello, world!"); +fn load_tsp_instance(filename: &str) -> Result, Box> { + let file = File::open(filename)?; + let reader: Box = if filename.ends_with(".gz") { + Box::new(BufReader::new(GzDecoder::new(file))) + } else { + Box::new(BufReader::new(file)) + }; + + let mut cities = Vec::new(); + let mut in_coord_section = false; + let mut dimension = 0; + + for line in reader.lines() { + let line = line?.trim().to_string(); + + if line.starts_with("DIMENSION") { + dimension = line.split(':').nth(1) + .or_else(|| line.split_whitespace().nth(1)) + .ok_or("Could not parse dimension")? + .trim() + .parse::()?; + } else if line == "NODE_COORD_SECTION" { + in_coord_section = true; + } else if line == "EOF" { + break; + } else if in_coord_section && !line.is_empty() { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 3 { + let x: f64 = parts[1].parse()?; + let y: f64 = parts[2].parse()?; + cities.push((x, y)); + } + } + } + + if cities.len() != dimension { + return Err(format!("Expected {} cities, found {}", dimension, cities.len()).into()); + } + + Ok(TSPInstance::new_dyn(cities)) +} + +fn main() -> Result<(), Box> { + // Load a TSP instance from file + let instance = load_tsp_instance("instances/berlin52.tsp.gz")?; + let dimension = instance.dimension(); + + // Plot just the instance + instance.plot("tsp_instance.png")?; + println!("Created tsp_instance.png"); + + // Create a random solution + let initializer = TSPRandomInitializer::new(); + let mut rng = rng(); + let initial_solution = initializer.initialize_single(dimension, &mut rng); + + // Plot the initial solution + instance.draw_solution(&initial_solution, "tsp_initial_solution.png")?; + println!("Created tsp_initial_solution.png with cost: {:.2}", instance.solution_cost(&initial_solution)); + + // Run local search to improve the solution + let mut perturbation = SwapPerturbation::new(); + let mut terminating_condition = NoBetterForCyclesTerminatingCondition::new(100000); + let better_than_operator = MinimizingOperator::new(); + + let result = local_search_first_improving( + &instance, + &mut terminating_condition, + &mut perturbation, + &better_than_operator, + &initial_solution, + &mut rng, + )?; + + // Plot the improved solution + instance.draw_solution(&result.best_candidate.pos, "tsp_improved_solution.png")?; + println!("Created tsp_improved_solution.png with cost: {:.2}", result.best_candidate.fit); + println!("Improvement: {:.2} -> {:.2} (cycles: {})", + instance.solution_cost(&initial_solution), + result.best_candidate.fit, + result.cycles); + + Ok(()) } diff --git a/codes/tsp_hw01/src/tsp.rs b/codes/tsp_hw01/src/tsp.rs index 0c445532c0cdbc5bb2a35c199a96a296878b6b8d..c0cf6d86b69a7d264ef2418a2b1a7de79d95fbc1 100644 --- a/codes/tsp_hw01/src/tsp.rs +++ b/codes/tsp_hw01/src/tsp.rs @@ -87,6 +87,10 @@ where DefaultAllocator: Allocator, DefaultAllocator: Allocator, { + pub fn dimension(&self) -> D { + self.distances.shape_generic().0 + } + pub fn verify_solution(&self, solution: &NodePermutation) -> bool { let mut seen_vertices = OVector::from_element_generic( solution.permutation.shape_generic().0,