vrp_core/construction/heuristics/
insertions.rs1#[cfg(test)]
2#[path = "../../../tests/unit/construction/heuristics/insertions_test.rs"]
3mod insertions_test;
4
5use crate::construction::heuristics::*;
6use crate::models::common::Cost;
7use crate::models::problem::{Actor, Job, JobIdDimension};
8use crate::models::solution::Activity;
9use crate::models::ViolationCode;
10use lazy_static::lazy_static;
11use rosomaxa::prelude::*;
12use std::borrow::Borrow;
13use std::cmp::Ordering;
14use std::fmt::{Debug, Formatter};
15use std::ops::{Add, ControlFlow, Index, Sub};
16use std::sync::Arc;
17use tinyvec::{TinyVec, TinyVecIterator};
18
19#[derive(Debug)]
21pub enum InsertionResult {
22 Success(InsertionSuccess),
24 Failure(InsertionFailure),
26}
27
28pub struct InsertionSuccess {
30 pub cost: InsertionCost,
32
33 pub job: Job,
35
36 pub activities: Vec<(Activity, usize)>,
38
39 pub actor: Arc<Actor>,
41}
42
43impl Debug for InsertionSuccess {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 f.debug_struct(short_type_name::<Self>())
46 .field("cost", &self.cost)
47 .field("job", &self.job)
48 .field(
49 "activities",
50 &self
51 .activities
52 .iter()
53 .map(|(a, idx)| {
54 (
55 a.retrieve_job()
56 .and_then(|job| job.dimens().get_job_id().cloned())
57 .unwrap_or("undef".to_string()),
58 *idx,
59 )
60 })
61 .collect::<Vec<_>>(),
62 )
63 .field("actor", self.actor.as_ref())
64 .finish()
65 }
66}
67
68#[derive(Debug)]
70pub struct InsertionFailure {
71 pub constraint: ViolationCode,
73 pub stopped: bool,
75 pub job: Option<Job>,
77}
78
79const COST_DIMENSION: usize = 6;
82
83type CostArray = [Cost; COST_DIMENSION];
85
86lazy_static! {
87 static ref MAX_INSERTION_COST: InsertionCost = InsertionCost::new(&[Cost::MAX]);
88}
89
90#[derive(Clone, Default)]
92pub struct InsertionCost {
93 data: TinyVec<CostArray>,
94}
95
96impl InsertionCost {
97 pub fn new(data: &[Cost]) -> Self {
99 Self { data: data.into() }
100 }
101
102 pub fn iter(&self) -> impl Iterator<Item = Cost> + '_ {
104 self.data.iter().cloned()
105 }
106
107 pub fn max_value() -> &'static Self {
109 &MAX_INSERTION_COST
110 }
111}
112
113impl FromIterator<Cost> for InsertionCost {
114 fn from_iter<T: IntoIterator<Item = Cost>>(iter: T) -> Self {
115 Self { data: TinyVec::<CostArray>::from_iter(iter) }
116 }
117}
118
119impl IntoIterator for InsertionCost {
120 type Item = Cost;
121 type IntoIter = TinyVecIterator<CostArray>;
122
123 fn into_iter(self) -> Self::IntoIter {
124 self.data.into_iter()
125 }
126}
127
128impl Eq for InsertionCost {}
129
130impl PartialEq for InsertionCost {
131 fn eq(&self, other: &Self) -> bool {
132 self.cmp(other) == Ordering::Equal
133 }
134}
135
136impl PartialOrd for InsertionCost {
137 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
138 Some(self.cmp(other))
139 }
140}
141
142impl Ord for InsertionCost {
143 fn cmp(&self, other: &Self) -> Ordering {
144 let size = self.data.len().max(other.data.len());
145 (0..size)
146 .try_fold(Ordering::Equal, |acc, idx| {
147 let left = self.data.get(idx).cloned().unwrap_or_default();
148 let right = other.data.get(idx).cloned().unwrap_or_default();
149
150 let result = left.total_cmp(&right);
151 match result {
152 Ordering::Equal => ControlFlow::Continue(acc),
153 _ => ControlFlow::Break(result),
154 }
155 })
156 .unwrap_value()
157 }
158}
159
160impl<'a, B> Add<B> for &'a InsertionCost
161where
162 B: Borrow<InsertionCost>,
163{
164 type Output = InsertionCost;
165
166 fn add(self, rhs: B) -> Self::Output {
167 let rhs = rhs.borrow();
168 let size = self.data.len().max(rhs.data.len());
169
170 (0..size)
171 .map(|idx| {
172 self.data.get(idx).copied().unwrap_or(Cost::default())
173 + rhs.data.get(idx).copied().unwrap_or(Cost::default())
174 })
175 .collect()
176 }
177}
178
179impl<B> Add<B> for InsertionCost
180where
181 B: Borrow<InsertionCost>,
182{
183 type Output = InsertionCost;
184
185 fn add(self, rhs: B) -> Self::Output {
186 &self + rhs
187 }
188}
189
190impl<'a, B> Sub<B> for &'a InsertionCost
191where
192 B: Borrow<InsertionCost>,
193{
194 type Output = InsertionCost;
195
196 fn sub(self, rhs: B) -> Self::Output {
197 let rhs = rhs.borrow();
198 let size = self.data.len().max(rhs.data.len());
199
200 (0..size)
201 .map(|idx| {
202 self.data.get(idx).copied().unwrap_or(Cost::default())
203 - rhs.data.get(idx).copied().unwrap_or(Cost::default())
204 })
205 .collect()
206 }
207}
208
209impl<B> Sub<B> for InsertionCost
210where
211 B: Borrow<InsertionCost>,
212{
213 type Output = InsertionCost;
214
215 fn sub(self, rhs: B) -> Self::Output {
216 &self - rhs
217 }
218}
219
220impl Debug for InsertionCost {
221 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
222 f.debug_list().entries(&self.data).finish()
223 }
224}
225
226impl Index<usize> for InsertionCost {
227 type Output = Cost;
228
229 fn index(&self, index: usize) -> &Self::Output {
230 if index < self.data.len() {
231 &self.data[index]
232 } else {
233 panic!("index out of range: {index}, size is {}", self.data.len())
234 }
235 }
236}
237
238pub struct InsertionHeuristic {
243 insertion_evaluator: Box<dyn InsertionEvaluator + Send + Sync>,
244}
245
246impl Default for InsertionHeuristic {
247 fn default() -> Self {
248 InsertionHeuristic::new(Box::<PositionInsertionEvaluator>::default())
249 }
250}
251
252impl InsertionHeuristic {
253 pub fn new(insertion_evaluator: Box<dyn InsertionEvaluator + Send + Sync>) -> Self {
255 Self { insertion_evaluator }
256 }
257}
258
259impl InsertionHeuristic {
260 pub fn process(
262 &self,
263 mut insertion_ctx: InsertionContext,
264 job_selector: &(dyn JobSelector),
265 route_selector: &(dyn RouteSelector),
266 leg_selection: &LegSelection,
267 result_selector: &(dyn ResultSelector),
268 ) -> InsertionContext {
269 prepare_insertion_ctx(&mut insertion_ctx);
270
271 while !insertion_ctx.solution.required.is_empty()
272 && !insertion_ctx.environment.quota.as_ref().map_or(false, |q| q.is_reached())
273 {
274 job_selector.prepare(&mut insertion_ctx);
275 route_selector.prepare(&mut insertion_ctx);
276
277 let jobs = job_selector.select(&insertion_ctx).collect::<Vec<_>>();
278 let routes = route_selector.select(&insertion_ctx, jobs.as_slice()).collect::<Vec<_>>();
279
280 let result =
281 self.insertion_evaluator.evaluate_all(&insertion_ctx, &jobs, &routes, leg_selection, result_selector);
282
283 match result {
284 InsertionResult::Success(success) => {
285 apply_insertion_success(&mut insertion_ctx, success);
286 }
287 InsertionResult::Failure(failure) => {
288 let (route_indices, jobs) = copy_selection_data(&insertion_ctx, routes.as_slice(), jobs.as_slice());
290 apply_insertion_failure(&mut insertion_ctx, failure, &route_indices, &jobs);
291 }
292 }
293 }
294
295 finalize_insertion_ctx(&mut insertion_ctx);
296
297 insertion_ctx
298 }
299}
300
301impl InsertionResult {
302 pub fn make_success(
304 cost: InsertionCost,
305 job: Job,
306 activities: Vec<(Activity, usize)>,
307 route_ctx: &RouteContext,
308 ) -> Self {
309 Self::Success(InsertionSuccess { cost, job, activities, actor: route_ctx.route().actor.clone() })
310 }
311
312 pub fn make_failure() -> Self {
314 Self::make_failure_with_code(ViolationCode::unknown(), false, None)
315 }
316
317 pub fn make_failure_with_code(code: ViolationCode, stopped: bool, job: Option<Job>) -> Self {
319 Self::Failure(InsertionFailure { constraint: code, stopped, job })
320 }
321
322 pub fn choose_best_result(left: Self, right: Self) -> Self {
324 match (&left, &right) {
325 (Self::Success(_), Self::Failure(_)) => left,
326 (Self::Failure(_), Self::Success(_)) => right,
327 (Self::Success(lhs), Self::Success(rhs)) => {
328 if lhs.cost > rhs.cost {
329 right
330 } else {
331 left
332 }
333 }
334 (Self::Failure(_), Self::Failure(rhs)) => {
335 if rhs.constraint == ViolationCode::unknown() {
336 left
337 } else {
338 right
339 }
340 }
341 }
342 }
343
344 pub fn as_success(&self) -> Option<&InsertionSuccess> {
346 match self {
347 Self::Success(success) => Some(success),
348 Self::Failure(_) => None,
349 }
350 }
351}
352
353impl TryFrom<InsertionResult> for InsertionSuccess {
354 type Error = InsertionFailure;
355
356 fn try_from(value: InsertionResult) -> Result<Self, Self::Error> {
357 match value {
358 InsertionResult::Success(success) => Ok(success),
359 InsertionResult::Failure(failure) => Err(failure),
360 }
361 }
362}
363
364pub(crate) fn prepare_insertion_ctx(insertion_ctx: &mut InsertionContext) {
365 insertion_ctx.solution.required.extend(insertion_ctx.solution.unassigned.keys().cloned());
366 insertion_ctx.problem.goal.accept_solution_state(&mut insertion_ctx.solution);
367}
368
369pub(crate) fn finalize_insertion_ctx(insertion_ctx: &mut InsertionContext) {
370 finalize_unassigned(insertion_ctx, UnassignmentInfo::Unknown);
371
372 insertion_ctx.problem.goal.accept_solution_state(&mut insertion_ctx.solution);
373}
374
375pub(crate) fn apply_insertion_success(insertion_ctx: &mut InsertionContext, success: InsertionSuccess) {
376 let route_index = if let Some(new_route_ctx) = insertion_ctx.solution.registry.get_route(&success.actor) {
377 insertion_ctx.solution.routes.push(new_route_ctx);
378 insertion_ctx.solution.routes.len() - 1
379 } else {
380 insertion_ctx
381 .solution
382 .routes
383 .iter()
384 .position(|route_ctx| route_ctx.route().actor == success.actor)
385 .expect("registry is out of sync with used routes")
386 };
387
388 let route_ctx = insertion_ctx.solution.routes.get_mut(route_index).unwrap();
389 let route = route_ctx.route_mut();
390 success.activities.into_iter().for_each(|(a, index)| {
391 route.tour.insert_at(a, index + 1);
392 });
393
394 let job = success.job;
395 insertion_ctx.solution.required.retain(|j| *j != job);
396 insertion_ctx.solution.unassigned.remove(&job);
397 insertion_ctx.problem.goal.accept_insertion(&mut insertion_ctx.solution, route_index, &job);
398}
399
400fn apply_insertion_failure(
401 insertion_ctx: &mut InsertionContext,
402 failure: InsertionFailure,
403 route_indices: &[usize],
404 jobs: &[Job],
405) {
406 let selected_routes = route_indices.len();
407 let selected_jobs = jobs.len();
408
409 let all_unassignable = selected_jobs == insertion_ctx.solution.required.len()
411 && selected_routes == insertion_ctx.solution.routes.len();
412
413 let failure_handled = insertion_ctx.problem.goal.notify_failure(&mut insertion_ctx.solution, route_indices, jobs);
416 if failure_handled {
417 return;
418 }
419
420 let no_routes_available = failure.job.is_none();
423
424 if let Some(job) = failure.job {
425 insertion_ctx.solution.unassigned.insert(job.clone(), UnassignmentInfo::Simple(failure.constraint));
426 insertion_ctx.solution.required.retain(|j| *j != job);
427 }
428
429 if all_unassignable || no_routes_available {
430 let code =
431 if all_unassignable { UnassignmentInfo::Unknown } else { UnassignmentInfo::Simple(failure.constraint) };
432 finalize_unassigned(insertion_ctx, code);
433 }
434}
435
436fn finalize_unassigned(insertion_ctx: &mut InsertionContext, code: UnassignmentInfo) {
437 let unassigned = &insertion_ctx.solution.unassigned;
438 insertion_ctx.solution.required.retain(|job| !unassigned.contains_key(job));
439 insertion_ctx.solution.unassigned.extend(insertion_ctx.solution.required.drain(0..).map(|job| (job, code.clone())));
440}
441
442fn copy_selection_data(
443 insertion_ctx: &InsertionContext,
444 routes: &[&RouteContext],
445 jobs: &[&Job],
446) -> (Vec<usize>, Vec<Job>) {
447 let route_indices = routes
448 .iter()
449 .filter_map(|route| insertion_ctx.solution.routes.iter().position(|r| r == *route))
450 .collect::<Vec<_>>();
451 let jobs = jobs.iter().map(|&job| job.clone()).collect::<Vec<_>>();
452
453 (route_indices, jobs)
454}