1use solverforge_solver::CrossEntityDistanceMeter;
9
10#[derive(Clone, Debug)]
20pub struct ProblemData {
21 pub capacity: i64,
22 pub depot: usize,
23 pub demands: Vec<i32>,
24 pub distance_matrix: Vec<Vec<i64>>,
25 pub time_windows: Vec<(i64, i64)>,
26 pub service_durations: Vec<i64>,
27 pub travel_times: Vec<Vec<i64>>,
28 pub vehicle_departure_time: i64,
29}
30
31pub trait VrpSolution {
45 fn vehicle_data_ptr(&self, entity_idx: usize) -> *const ProblemData;
46 fn vehicle_visits(&self, entity_idx: usize) -> &[usize];
47 fn vehicle_visits_mut(&mut self, entity_idx: usize) -> &mut Vec<usize>;
48 fn vehicle_count(&self) -> usize;
49}
50
51#[inline]
57fn problem_data_for_entity<S: VrpSolution>(plan: &S, entity_idx: usize) -> Option<&ProblemData> {
58 let ptr = plan.vehicle_data_ptr(entity_idx);
59 assert!(
60 !ptr.is_null(),
61 "VrpSolution::vehicle_data_ptr({entity_idx}) returned null for a non-empty fleet"
62 );
63 unsafe { ptr.as_ref() }
66}
67
68#[inline]
69fn first_problem_data<S: VrpSolution>(plan: &S) -> Option<&ProblemData> {
70 if plan.vehicle_count() == 0 {
71 return None;
72 }
73 problem_data_for_entity(plan, 0)
74}
75
76pub fn distance<S: VrpSolution>(plan: &S, i: usize, j: usize) -> i64 {
78 first_problem_data(plan).map_or(0, |data| data.distance_matrix[i][j])
79}
80
81pub fn depot_for_entity<S: VrpSolution>(plan: &S, _entity_idx: usize) -> usize {
82 first_problem_data(plan).map_or(0, |data| data.depot)
83}
84
85pub fn depot_for_cw<S: VrpSolution>(plan: &S) -> usize {
86 first_problem_data(plan).map_or(0, |data| data.depot)
87}
88
89pub fn element_load<S: VrpSolution>(plan: &S, elem: usize) -> i64 {
90 first_problem_data(plan).map_or(0, |data| data.demands[elem] as i64)
91}
92
93pub fn capacity<S: VrpSolution>(plan: &S) -> i64 {
94 first_problem_data(plan).map_or(i64::MAX, |data| data.capacity)
95}
96
97pub fn replace_route<S: VrpSolution>(plan: &mut S, entity_idx: usize, route: Vec<usize>) {
101 *plan.vehicle_visits_mut(entity_idx) = route;
102}
103
104pub fn get_route<S: VrpSolution>(plan: &S, entity_idx: usize) -> Vec<usize> {
108 plan.vehicle_visits(entity_idx).to_vec()
109}
110
111pub fn is_time_feasible<S: VrpSolution>(plan: &S, route: &[usize]) -> bool {
113 if route.is_empty() {
114 return true;
115 }
116 match first_problem_data(plan) {
117 Some(data) => check_time_feasible(route, data),
118 None => true,
119 }
120}
121
122pub fn is_kopt_feasible<S: VrpSolution>(plan: &S, _entity_idx: usize, route: &[usize]) -> bool {
125 is_time_feasible(plan, route)
126}
127
128fn check_time_feasible(route: &[usize], data: &ProblemData) -> bool {
129 let mut current_time = data.vehicle_departure_time;
130 let mut prev = data.depot;
131
132 for &visit in route {
133 current_time += data.travel_times[prev][visit];
134
135 let (min_start, max_end) = data.time_windows[visit];
136
137 if current_time < min_start {
138 current_time = min_start;
139 }
140
141 let service_end = current_time + data.service_durations[visit];
142
143 if service_end > max_end {
144 return false;
145 }
146
147 current_time = service_end;
148 prev = visit;
149 }
150
151 true
152}
153
154#[derive(Clone, Default)]
161pub struct MatrixDistanceMeter;
162
163impl<S: VrpSolution> CrossEntityDistanceMeter<S> for MatrixDistanceMeter {
164 fn distance(
165 &self,
166 solution: &S,
167 src_entity: usize,
168 src_pos: usize,
169 dst_entity: usize,
170 dst_pos: usize,
171 ) -> f64 {
172 let src_visits = solution.vehicle_visits(src_entity);
173 let dst_visits = solution.vehicle_visits(dst_entity);
174 if src_pos >= src_visits.len() || dst_pos >= dst_visits.len() {
175 return f64::INFINITY;
176 }
177 problem_data_for_entity(solution, src_entity).map_or(f64::INFINITY, |data| {
178 data.distance_matrix[src_visits[src_pos]][dst_visits[dst_pos]] as f64
179 })
180 }
181}
182
183#[derive(Clone, Default)]
185pub struct MatrixIntraDistanceMeter;
186
187impl<S: VrpSolution> CrossEntityDistanceMeter<S> for MatrixIntraDistanceMeter {
188 fn distance(
189 &self,
190 solution: &S,
191 src_entity: usize,
192 src_pos: usize,
193 _dst_entity: usize,
194 dst_pos: usize,
195 ) -> f64 {
196 let visits = solution.vehicle_visits(src_entity);
197 if src_pos >= visits.len() || dst_pos >= visits.len() {
198 return f64::INFINITY;
199 }
200 problem_data_for_entity(solution, src_entity).map_or(f64::INFINITY, |data| {
201 data.distance_matrix[visits[src_pos]][visits[dst_pos]] as f64
202 })
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 struct TestSolution {
211 data: Box<ProblemData>,
212 routes: Vec<Vec<usize>>,
213 }
214
215 impl TestSolution {
216 fn new(routes: Vec<Vec<usize>>) -> Self {
217 Self {
218 data: Box::new(ProblemData {
219 capacity: 10,
220 depot: 0,
221 demands: vec![0, 2, 3, 4],
222 distance_matrix: vec![
223 vec![0, 5, 7, 9],
224 vec![5, 0, 4, 6],
225 vec![7, 4, 0, 3],
226 vec![9, 6, 3, 0],
227 ],
228 time_windows: vec![(0, 100), (0, 10), (7, 14), (0, 12)],
229 service_durations: vec![0, 2, 2, 3],
230 travel_times: vec![
231 vec![0, 5, 7, 9],
232 vec![5, 0, 4, 6],
233 vec![7, 4, 0, 3],
234 vec![9, 6, 3, 0],
235 ],
236 vehicle_departure_time: 0,
237 }),
238 routes,
239 }
240 }
241 }
242
243 impl VrpSolution for TestSolution {
244 fn vehicle_data_ptr(&self, _entity_idx: usize) -> *const ProblemData {
245 self.data.as_ref() as *const ProblemData
246 }
247
248 fn vehicle_visits(&self, entity_idx: usize) -> &[usize] {
249 &self.routes[entity_idx]
250 }
251
252 fn vehicle_visits_mut(&mut self, entity_idx: usize) -> &mut Vec<usize> {
253 &mut self.routes[entity_idx]
254 }
255
256 fn vehicle_count(&self) -> usize {
257 self.routes.len()
258 }
259 }
260
261 struct NullDataSolution {
262 routes: Vec<Vec<usize>>,
263 }
264
265 impl VrpSolution for NullDataSolution {
266 fn vehicle_data_ptr(&self, _entity_idx: usize) -> *const ProblemData {
267 std::ptr::null()
268 }
269
270 fn vehicle_visits(&self, entity_idx: usize) -> &[usize] {
271 &self.routes[entity_idx]
272 }
273
274 fn vehicle_visits_mut(&mut self, entity_idx: usize) -> &mut Vec<usize> {
275 &mut self.routes[entity_idx]
276 }
277
278 fn vehicle_count(&self) -> usize {
279 self.routes.len()
280 }
281 }
282
283 #[test]
284 fn helpers_use_problem_data_from_first_vehicle() {
285 let solution = TestSolution::new(vec![vec![1, 2], vec![3]]);
286
287 assert_eq!(distance(&solution, 1, 3), 6);
288 assert_eq!(depot_for_entity(&solution, 1), 0);
289 assert_eq!(depot_for_cw(&solution), 0);
290 assert_eq!(element_load(&solution, 2), 3);
291 assert_eq!(capacity(&solution), 10);
292 }
293
294 #[test]
295 fn helpers_handle_empty_fleets() {
296 let solution = TestSolution::new(vec![]);
297
298 assert_eq!(distance(&solution, 1, 2), 0);
299 assert_eq!(depot_for_entity(&solution, 0), 0);
300 assert_eq!(depot_for_cw(&solution), 0);
301 assert_eq!(element_load(&solution, 1), 0);
302 assert_eq!(capacity(&solution), i64::MAX);
303 assert!(is_time_feasible(&solution, &[1, 2]));
304 assert!(is_kopt_feasible(&solution, 0, &[1, 2]));
305 }
306
307 #[test]
308 #[should_panic(expected = "vehicle_data_ptr(0) returned null")]
309 fn helpers_reject_missing_problem_data_for_non_empty_fleets() {
310 let solution = NullDataSolution {
311 routes: vec![vec![1, 2]],
312 };
313
314 let _ = distance(&solution, 1, 2);
315 }
316
317 #[test]
318 fn route_helpers_replace_and_clone_routes() {
319 let mut solution = TestSolution::new(vec![vec![1, 2], vec![3]]);
320
321 replace_route(&mut solution, 0, vec![2, 3]);
322 assert_eq!(solution.routes[0], vec![2, 3]);
323 assert_eq!(get_route(&solution, 0), vec![2, 3]);
324
325 replace_route(&mut solution, 1, vec![1]);
326 assert_eq!(solution.routes[1], vec![1]);
327 }
328
329 #[test]
330 fn time_feasibility_checks_waiting_and_deadlines() {
331 let solution = TestSolution::new(vec![vec![1, 2], vec![3]]);
332
333 assert!(
334 is_time_feasible(&solution, &[1, 2]),
335 "route should wait for customer 2 and still finish in time"
336 );
337 assert!(
338 !is_time_feasible(&solution, &[2, 3]),
339 "route should miss customer 3's latest end"
340 );
341 assert!(is_kopt_feasible(&solution, 1, &[1, 2]));
342 }
343
344 #[test]
345 fn distance_meters_cover_invalid_positions() {
346 let solution = TestSolution::new(vec![vec![1, 2], vec![3]]);
347
348 assert_eq!(MatrixDistanceMeter.distance(&solution, 0, 0, 1, 0), 6.0);
349 assert_eq!(
350 MatrixIntraDistanceMeter.distance(&solution, 0, 0, 0, 1),
351 4.0
352 );
353 assert!(MatrixDistanceMeter
354 .distance(&solution, 0, 4, 1, 0)
355 .is_infinite());
356 assert!(MatrixIntraDistanceMeter
357 .distance(&solution, 0, 0, 0, 4)
358 .is_infinite());
359 }
360}