1use anyhow::{anyhow, Result};
4use include_dir::{include_dir, Dir};
5use mahf::{
6 problems::{ObjectiveFunction, Problem, VectorProblem},
7 SingleObjective,
8};
9use tspf::WeightKind;
10
11mod instances;
12mod opt;
13
14pub use instances::Instances;
15
16type Edge = (usize, usize);
17pub type Node = usize;
18pub type Route = Vec<Node>;
19
20static FILES: Dir = include_dir!("$CARGO_MANIFEST_DIR/src/tsplib");
21
22#[derive(Debug, Clone, serde::Serialize)]
24pub struct TspOptimum {
25 pub objective: SingleObjective,
26 pub solution: Option<Route>,
27}
28
29#[derive(serde::Serialize)]
31pub struct Tsp {
32 best_solution: Option<TspOptimum>,
33 #[serde(skip)]
34 inner: tspf::Tsp,
35}
36
37impl Tsp {
38 fn evaluate_solution(&self, solution: &Route) -> SingleObjective {
39 solution
40 .iter()
41 .copied()
42 .zip(solution.iter().copied().skip(1))
43 .map(|edge| self.distance(edge))
44 .sum::<f64>()
45 .try_into()
46 .unwrap()
47 }
48}
49
50impl From<Instances> for Tsp {
51 fn from(value: Instances) -> Self {
52 value.load()
53 }
54}
55
56impl Tsp {
57 pub fn name(&self) -> &str {
59 self.inner.name()
60 }
61
62 pub fn best_solution(&self) -> Option<&TspOptimum> {
64 self.best_solution.as_ref()
65 }
66
67 pub fn best_fitness(&self) -> Option<f64> {
69 self.best_solution.as_ref().map(|o| o.objective.value())
70 }
71
72 pub fn distance(&self, edge: Edge) -> f64 {
74 let (a, b) = edge;
75
76 if self.inner.weight_kind() == WeightKind::Explicit {
78 self.inner.weight(a, b)
79 } else {
80 self.inner.weight(a + 1, b + 1)
81 }
82 }
83
84 pub fn greedy_route(&self) -> Route {
86 let mut route = Vec::with_capacity(self.inner.dim());
87 let mut remaining = (1..self.inner.dim()).collect::<Vec<usize>>();
88 route.push(0);
89 while !remaining.is_empty() {
90 let last = *route.last().unwrap();
91 let next_index = remaining
92 .iter()
93 .enumerate()
94 .min_by_key(|(_, &r)| SingleObjective::try_from(self.distance((last, r))).unwrap())
95 .unwrap()
96 .0;
97 let next = remaining.remove(next_index);
98 route.push(next);
99 }
100 route
101 }
102
103 pub fn try_parse(data: &str, opt: Option<&str>) -> Result<Self> {
106 let tsp =
107 tspf::TspBuilder::parse_str(data).map_err(|err| anyhow!("parsing failed: {}", err))?;
108
109 let mut instance = Tsp {
110 best_solution: None,
111 inner: tsp,
112 };
113
114 if let Some(opt) = opt {
115 instance.best_solution = Some(opt::parse_opt_file(&instance, opt));
116 }
117
118 Ok(instance)
119 }
120}
121
122impl Problem for Tsp {
123 type Encoding = Route;
124 type Objective = SingleObjective;
125
126 fn name(&self) -> &str {
127 "Tsp"
128 }
129}
130
131impl ObjectiveFunction for Tsp {
132 fn objective(&self, solution: &Self::Encoding) -> Self::Objective {
133 self.evaluate_solution(solution)
134 }
135}
136
137impl VectorProblem for Tsp {
138 type Element = usize;
139
140 fn dimension(&self) -> usize {
141 self.inner.dim()
142 }
143}