diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/README.md b/tig-algorithms/src/vehicle_routing/cw_heuristic/README.md new file mode 100644 index 00000000..b79a5a2d --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/README.md @@ -0,0 +1,23 @@ +# TIG Code Submission + +## Submission Details + +* **Challenge Name:** vehicle_routing +* **Algorithm Name:** cw_heuristic +* **Copyright:** 2024 Just +* **Identity of Submitter:** Just +* **Identity of Creator of Algorithmic Method:** null +* **Unique Algorithm Identifier (UAI):** null + +## License + +The files in this folder are under the following licenses: +* TIG Benchmarker Outbound License +* TIG Commercial License +* TIG Inbound Game License +* TIG Innovator Outbound Game License +* TIG Open Data License +* TIG THV Game License + +Copies of the licenses can be obtained at: +https://github.com/tig-foundation/tig-monorepo/tree/main/docs/licenses \ No newline at end of file diff --git a/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs b/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs new file mode 100644 index 00000000..c0ad74e6 --- /dev/null +++ b/tig-algorithms/src/vehicle_routing/cw_heuristic/mod.rs @@ -0,0 +1,131 @@ +use anyhow::{anyhow, Result}; +use serde_json::{Map, Value}; +use tig_challenges::vehicle_routing::*; + + +pub fn solve_challenge( + challenge: &Challenge, + save_solution: &dyn Fn(&Solution) -> Result<()>, + hyperparameters: &Option>, +) -> Result<()> { + Err(anyhow!("This algorithm is no longer compatible.")) +} + +// Old code that is no longer compatible +#[cfg(none)] +mod dead_code { + use tig_challenges::vehicle_routing::*; + + + pub fn solve_challenge(challenge: &Challenge) -> anyhow::Result> { + let mut solution = Solution { + sub_solutions: Vec::new(), + }; + for sub_instance in &challenge.sub_instances { + match solve_sub_instance(sub_instance)? { + Some(sub_solution) => solution.sub_solutions.push(sub_solution), + None => return Ok(None), + } + } + Ok(Some(solution)) + } + + pub fn solve_sub_instance(challenge: &SubInstance) -> anyhow::Result> { + let d = &challenge.distance_matrix; + let c = challenge.max_capacity; + let n = challenge.num_nodes; + + let max_dist: f32 = challenge.distance_matrix[0].iter().sum::() as f32; + let p = challenge.baseline_total_distance as f32 / max_dist; + if p < 0.57 { + return Ok(None) + } + + // Clarke-Wright heuristic for node pairs based on their distances to depot + // vs distance between each other + let mut scores: Vec<(i32, usize, usize)> = Vec::with_capacity((n-1)*(n-2)/2); + for i in 1..n { + for j in (i + 1)..n { + scores.push((d[i][0] + d[0][j] - d[i][j], i, j)); + } + } + + scores.sort_unstable_by(|a, b| b.0.cmp(&a.0)); + + // Create a route for every node + let mut routes: Vec>> = (0..n).map(|i| Some(vec![i])).collect(); + routes[0] = None; + let mut route_demands: Vec = challenge.demands.clone(); + + // Iterate through node pairs, starting from greatest score + for (s, i, j) in scores { + // Stop if score is negative + if s < 0 { + break; + } + + // Skip if joining the nodes is not possible + if routes[i].is_none() || routes[j].is_none() { + continue; + } + + let left_route = routes[i].as_ref().unwrap(); + let right_route = routes[j].as_ref().unwrap(); + let mut left_startnode = left_route[0]; + let right_startnode = right_route[0]; + let left_endnode = left_route[left_route.len() - 1]; + let mut right_endnode = right_route[right_route.len() - 1]; + let merged_demand = route_demands[left_startnode] + route_demands[right_startnode]; + + if left_startnode == right_startnode || merged_demand > c { + continue; + } + + let mut left_route = routes[i].take().unwrap(); + let mut right_route = routes[j].take().unwrap(); + routes[left_startnode] = None; + routes[right_startnode] = None; + routes[left_endnode] = None; + routes[right_endnode] = None; + + // reverse it + if left_startnode == i { + left_route.reverse(); + left_startnode = left_endnode; + } + if right_endnode == j { + right_route.reverse(); + right_endnode = right_startnode; + } + + let mut new_route = left_route; + new_route.extend(right_route); + + // Only the start and end nodes of routes are kept + routes[left_startnode] = Some(new_route.clone()); + routes[right_endnode] = Some(new_route); + route_demands[left_startnode] = merged_demand; + route_demands[right_endnode] = merged_demand; + } + + let routes = routes + .into_iter() + .enumerate() + .filter(|(i, x)| x.as_ref().is_some_and(|x| x[0] == *i)) + .map(|(_, mut x)| { + let mut route = vec![0]; + route.append(x.as_mut().unwrap()); + route.push(0); + route + }) + .collect(); + + Ok(Some(SubSolution { + routes + })) + } +} + +pub fn help() { + println!("No help information available."); +} diff --git a/tig-algorithms/src/vehicle_routing/mod.rs b/tig-algorithms/src/vehicle_routing/mod.rs index 930b4fb4..bbe2d901 100644 --- a/tig-algorithms/src/vehicle_routing/mod.rs +++ b/tig-algorithms/src/vehicle_routing/mod.rs @@ -66,7 +66,8 @@ // c002_a034 -// c002_a035 +pub mod cw_heuristic; +pub use cw_heuristic as c002_a035; // c002_a036