1use crate::instance::SolomonInstance;
4
5#[derive(Debug, Clone, PartialEq, Eq, Default)]
10pub struct Route {
11 pub nodes: Vec<usize>,
13}
14
15impl Route {
16 #[inline]
18 pub fn new() -> Self {
19 Self { nodes: Vec::new() }
20 }
21
22 #[inline]
24 pub fn from_nodes(nodes: Vec<usize>) -> Self {
25 Self { nodes }
26 }
27
28 #[inline]
30 pub fn len(&self) -> usize {
31 self.nodes.len()
32 }
33
34 #[inline]
36 pub fn is_empty(&self) -> bool {
37 self.nodes.is_empty()
38 }
39
40 pub fn distance(&self, instance: &SolomonInstance) -> f32 {
44 if self.nodes.is_empty() {
45 return 0.0;
46 }
47
48 let mut total_dist = instance.distance(0, self.nodes[0]);
49 for window in self.nodes.windows(2) {
50 total_dist += instance.distance(window[0], window[1]);
51 }
52 total_dist += instance.distance(*self.nodes.last().unwrap(), 0);
53
54 total_dist
55 }
56
57 pub fn total_demand(&self, instance: &SolomonInstance) -> f32 {
59 self.nodes.iter().map(|&node| instance.demand(node)).sum()
60 }
61
62 #[inline]
64 pub fn is_capacity_feasible(&self, instance: &SolomonInstance) -> bool {
65 self.total_demand(instance) <= instance.vehicle.capacity
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Default)]
71pub struct Solution {
72 pub routes: Vec<Route>,
74}
75
76impl Solution {
77 #[inline]
79 pub fn new(routes: Vec<Route>) -> Self {
80 Self { routes }
81 }
82
83 #[inline]
85 pub fn empty() -> Self {
86 Self { routes: Vec::new() }
87 }
88
89 pub fn total_distance(&self, instance: &SolomonInstance) -> f32 {
91 self.routes
92 .iter()
93 .map(|route| route.distance(instance))
94 .sum()
95 }
96
97 pub fn total_customers_visited(&self) -> usize {
99 self.routes.iter().map(Route::len).sum()
100 }
101
102 pub fn is_feasible(&self, instance: &SolomonInstance) -> bool {
107 if instance.validate().is_err() {
108 return false;
109 }
110 let active_routes = self.routes.iter().filter(|r| !r.is_empty());
111
112 if active_routes.clone().count() > instance.vehicle.num_vehicles {
114 return false;
115 }
116
117 let mut visited = vec![false; instance.num_nodes];
119
120 for route in active_routes {
121 for &node in &route.nodes {
122 if node == 0 || node >= instance.num_nodes {
124 return false;
125 }
126 if visited[node] {
128 return false;
129 }
130 visited[node] = true;
131 }
132 if !route.is_capacity_feasible(instance) {
134 return false;
135 }
136 }
137
138 visited[1..].iter().all(|&v| v)
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146 use crate::instance::VehicleConfig;
147
148 #[test]
149 fn test_solution_rejects_invalid_ids_without_panicking() {
150 let instance = create_mock_instance();
151 for node in [instance.num_nodes, usize::MAX] {
152 let solution = Solution::new(vec![Route::from_nodes(vec![node])]);
153 assert!(!solution.is_feasible(&instance));
154 }
155 }
156
157 #[test]
158 fn test_solution_rejects_malformed_instance() {
159 let mut instance = create_mock_instance();
160 instance.demands.clear();
161 let solution = Solution::new(vec![Route::from_nodes(vec![1, 2, 3])]);
162 assert!(!solution.is_feasible(&instance));
163 }
164
165 #[test]
166 fn test_solution_ignores_empty_routes_in_fleet_count() {
167 let instance = create_mock_instance();
168 let solution = Solution::new(vec![
169 Route::new(),
170 Route::from_nodes(vec![1, 2]),
171 Route::from_nodes(vec![3]),
172 ]);
173 assert!(solution.is_feasible(&instance));
174 }
175
176 fn create_mock_instance() -> SolomonInstance {
177 let xs = vec![0.0, 3.0, 3.0, 0.0];
183 let ys = vec![0.0, 0.0, 4.0, 4.0];
184 let demands = vec![0.0, 10.0, 20.0, 15.0];
185 let ready_times = vec![0.0, 0.0, 0.0, 0.0];
186 let due_times = vec![1000.0, 1000.0, 1000.0, 1000.0];
187 let service_times = vec![0.0, 10.0, 10.0, 10.0];
188 let distance_matrix = crate::instance::compute_distance_matrix(&xs, &ys);
189
190 SolomonInstance {
191 name: "MockInstance".into(),
192 vehicle: VehicleConfig {
193 num_vehicles: 2,
194 capacity: 40.0,
195 },
196 num_nodes: 4,
197 xs,
198 ys,
199 demands,
200 ready_times,
201 due_times,
202 service_times,
203 distance_matrix,
204 }
205 }
206
207 #[test]
208 fn test_empty_route_and_solution() {
209 let instance = create_mock_instance();
210 let route = Route::new();
211 assert!(route.is_empty());
212 assert_eq!(route.len(), 0);
213 assert_eq!(route.distance(&instance), 0.0);
214 assert_eq!(route.total_demand(&instance), 0.0);
215 assert!(route.is_capacity_feasible(&instance));
216
217 let solution = Solution::empty();
218 assert_eq!(solution.total_distance(&instance), 0.0);
219 assert_eq!(solution.total_customers_visited(), 0);
220 }
221
222 #[test]
223 fn test_route_distance_and_demand() {
224 let instance = create_mock_instance();
225 let route = Route::from_nodes(vec![1, 2]);
232
233 assert_eq!(route.len(), 2);
234 assert!(!route.is_empty());
235 assert_eq!(route.total_demand(&instance), 30.0);
236 assert!(route.is_capacity_feasible(&instance));
237
238 let dist = route.distance(&instance);
239 assert!((dist - 12.0).abs() < 1e-5);
240 }
241
242 #[test]
243 fn test_solution_feasible_valid() {
244 let instance = create_mock_instance();
245 let r1 = Route::from_nodes(vec![1, 2]);
249 let r2 = Route::from_nodes(vec![3]);
250 let solution = Solution::new(vec![r1, r2]);
251
252 assert_eq!(solution.total_customers_visited(), 3);
253 assert!(solution.is_feasible(&instance));
254 }
255
256 #[test]
257 fn test_solution_infeasible_exceeds_capacity() {
258 let instance = create_mock_instance();
259 let r = Route::from_nodes(vec![1, 2, 3]);
261 let solution = Solution::new(vec![r]);
262
263 assert!(!solution.is_feasible(&instance));
264 }
265
266 #[test]
267 fn test_solution_infeasible_duplicate_customer() {
268 let instance = create_mock_instance();
269 let r1 = Route::from_nodes(vec![1, 2]);
271 let r2 = Route::from_nodes(vec![1, 3]);
272 let solution = Solution::new(vec![r1, r2]);
273
274 assert!(!solution.is_feasible(&instance));
275 }
276
277 #[test]
278 fn test_solution_infeasible_missing_customer() {
279 let instance = create_mock_instance();
280 let r1 = Route::from_nodes(vec![1, 2]);
282 let solution = Solution::new(vec![r1]);
283
284 assert!(!solution.is_feasible(&instance));
285 }
286
287 #[test]
288 fn test_solution_infeasible_too_many_vehicles() {
289 let instance = create_mock_instance();
290 let r1 = Route::from_nodes(vec![1]);
292 let r2 = Route::from_nodes(vec![2]);
293 let r3 = Route::from_nodes(vec![3]);
294 let solution = Solution::new(vec![r1, r2, r3]);
295
296 assert!(!solution.is_feasible(&instance));
297 }
298
299 #[test]
300 fn test_solution_infeasible_depot_in_route() {
301 let instance = create_mock_instance();
302 let r1 = Route::from_nodes(vec![0, 1, 2]);
304 let r2 = Route::from_nodes(vec![3]);
305 let solution = Solution::new(vec![r1, r2]);
306
307 assert!(!solution.is_feasible(&instance));
308 }
309}