1use super::*;
2use crate::construction::heuristics::*;
3use crate::models::{Extras, GoalContext};
4use crate::rosomaxa::get_default_selection_size;
5use crate::solver::search::*;
6use rosomaxa::algorithms::gsom::Input;
7use rosomaxa::hyper::*;
8use rosomaxa::population::*;
9use rosomaxa::termination::*;
10use std::marker::PhantomData;
11
12pub type TargetEvolutionStrategy =
14 Box<dyn EvolutionStrategy<Context = RefinementContext, Objective = GoalContext, Solution = InsertionContext>>;
15pub type TargetPopulation =
17 Box<dyn HeuristicPopulation<Objective = GoalContext, Individual = InsertionContext> + Send + Sync>;
18pub type TargetHeuristic =
20 Box<dyn HyperHeuristic<Context = RefinementContext, Objective = GoalContext, Solution = InsertionContext>>;
21pub type TargetSearchOperator = Arc<
23 dyn HeuristicSearchOperator<Context = RefinementContext, Objective = GoalContext, Solution = InsertionContext>
24 + Send
25 + Sync,
26>;
27
28pub type GreedyPopulation = Greedy<GoalContext, InsertionContext>;
30pub type ElitismPopulation = Elitism<GoalContext, InsertionContext>;
32pub type RosomaxaPopulation = Rosomaxa<GoalContext, InsertionContext>;
34
35pub type DynTermination = dyn Termination<Context = RefinementContext, Objective = GoalContext> + Send + Sync;
37pub type TargetCompositeTermination = CompositeTermination<RefinementContext, GoalContext, InsertionContext>;
39pub type MaxTimeTermination = MaxTime<RefinementContext, GoalContext, InsertionContext>;
41pub type MaxGenerationTermination = MaxGeneration<RefinementContext, GoalContext, InsertionContext>;
43pub type MinVariationTermination = MinVariation<RefinementContext, GoalContext, InsertionContext, String>;
45
46pub type TargetHeuristicProbability = HeuristicProbability<RefinementContext, GoalContext, InsertionContext>;
48pub type TargetHeuristicGroup = HeuristicSearchGroup<RefinementContext, GoalContext, InsertionContext>;
50
51pub type ProblemConfigBuilder = EvolutionConfigBuilder<RefinementContext, GoalContext, InsertionContext, String>;
53
54pub type HeuristicFilterFn = Arc<dyn Fn(&str) -> bool + Send + Sync>;
56
57custom_extra_property!(HeuristicFilter typeof HeuristicFilterFn);
58
59pub struct VrpConfigBuilder {
61 problem: Arc<Problem>,
62 environment: Option<Arc<Environment>>,
63 heuristic: Option<TargetHeuristic>,
64 telemetry_mode: Option<TelemetryMode>,
65}
66
67impl VrpConfigBuilder {
68 pub fn new(problem: Arc<Problem>) -> Self {
70 Self { problem, environment: None, heuristic: None, telemetry_mode: None }
71 }
72
73 pub fn set_environment(mut self, environment: Arc<Environment>) -> Self {
75 self.environment = Some(environment);
76 self
77 }
78
79 pub fn set_telemetry_mode(mut self, mode: TelemetryMode) -> Self {
81 self.telemetry_mode = Some(mode);
82 self
83 }
84
85 pub fn set_heuristic(mut self, heuristic: TargetHeuristic) -> Self {
88 self.heuristic = Some(heuristic);
89 self
90 }
91
92 pub fn prebuild(self) -> GenericResult<ProblemConfigBuilder> {
94 let problem = self.problem;
95 let environment = self.environment.unwrap_or_else(|| Arc::new(Environment::default()));
96 let telemetry_mode =
97 self.telemetry_mode.unwrap_or_else(|| get_default_telemetry_mode(environment.logger.clone()));
98
99 let heuristic = self.heuristic.unwrap_or_else(|| get_default_heuristic(problem.clone(), environment.clone()));
100
101 let selection_size = get_default_selection_size(environment.as_ref());
102 let population = get_default_population(problem.goal.clone(), environment.clone(), selection_size);
103
104 Ok(ProblemConfigBuilder::default()
105 .with_heuristic(heuristic)
106 .with_context(RefinementContext::new(problem.clone(), population, telemetry_mode, environment.clone()))
107 .with_processing(create_default_processing())
108 .with_initial(4, 0.05, create_default_init_operators(problem, environment)))
109 }
110}
111
112pub fn get_default_telemetry_mode(logger: InfoLogger) -> TelemetryMode {
114 TelemetryMode::OnlyLogging { logger, log_best: 100, log_population: 1000 }
115}
116
117pub fn get_default_heuristic(problem: Arc<Problem>, environment: Arc<Environment>) -> TargetHeuristic {
119 Timer::measure_duration_with_callback(
120 || Box::new(get_dynamic_heuristic(problem, environment.clone())),
121 |duration| (environment.logger)(format!("getting default heuristic took: {}ms", duration.as_millis()).as_str()),
122 )
123}
124
125pub fn get_static_heuristic(
127 problem: Arc<Problem>,
128 environment: Arc<Environment>,
129) -> StaticSelective<RefinementContext, GoalContext, InsertionContext> {
130 let default_operator = statik::create_default_heuristic_operator(problem.clone(), environment.clone());
131 let local_search = statik::create_default_local_search(environment.random.clone());
132
133 let heuristic_group: TargetHeuristicGroup = vec![
134 (
135 Arc::new(DecomposeSearch::new(default_operator.clone(), (2, 4), 4, SINGLE_HEURISTIC_QUOTA_LIMIT)),
136 create_context_operator_probability(
137 300,
138 10,
139 vec![(SelectionPhase::Exploration, 0.05), (SelectionPhase::Exploitation, 0.05)],
140 environment.random.clone(),
141 ),
142 ),
143 (local_search.clone(), create_scalar_operator_probability(0.05, environment.random.clone())),
144 (default_operator.clone(), create_scalar_operator_probability(1., environment.random.clone())),
145 (local_search, create_scalar_operator_probability(0.05, environment.random.clone())),
146 ];
147
148 get_static_heuristic_from_heuristic_group(problem, environment, heuristic_group)
149}
150
151pub fn get_static_heuristic_from_heuristic_group(
153 problem: Arc<Problem>,
154 environment: Arc<Environment>,
155 heuristic_group: TargetHeuristicGroup,
156) -> StaticSelective<RefinementContext, GoalContext, InsertionContext> {
157 StaticSelective::<RefinementContext, GoalContext, InsertionContext>::new(
158 heuristic_group,
159 create_diversify_operators(problem, environment),
160 )
161}
162
163pub fn get_dynamic_heuristic(
165 problem: Arc<Problem>,
166 environment: Arc<Environment>,
167) -> DynamicSelective<RefinementContext, GoalContext, InsertionContext> {
168 let search_operators = dynamic::get_operators(problem.clone(), environment.clone());
169 let diversify_operators = create_diversify_operators(problem, environment.clone());
170
171 DynamicSelective::<RefinementContext, GoalContext, InsertionContext>::new(
172 search_operators,
173 diversify_operators,
174 environment.as_ref(),
175 )
176}
177
178pub fn create_elitism_population(
180 objective: Arc<GoalContext>,
181 environment: Arc<Environment>,
182) -> Elitism<GoalContext, InsertionContext> {
183 let selection_size = get_default_selection_size(environment.as_ref());
184 Elitism::new(objective, environment.random.clone(), 4, selection_size)
185}
186
187custom_solution_state!(SolutionWeights typeof Vec<Float>);
188
189impl RosomaxaWeighted for InsertionContext {
190 fn init_weights(&mut self) {
191 let weights = vec![
193 get_max_load_variance(self),
195 get_max_load_mean(self),
196 get_full_load_ratio(self),
197 get_duration_mean(self),
199 get_waiting_mean(self),
200 get_distance_mean(self),
202 get_longest_distance_between_customers_mean(self),
203 get_first_distance_customer_mean(self),
204 get_last_distance_customer_mean(self),
205 get_average_distance_between_depot_customer_mean(self),
207 get_longest_distance_between_depot_customer_mean(self),
208 get_customers_deviation(self),
210 self.solution.unassigned.len() as Float,
212 self.solution.routes.len() as Float,
213 self.get_total_cost().unwrap_or_default(),
214 ];
215
216 self.solution.state.set_solution_weights(weights);
217 }
218}
219
220impl Input for InsertionContext {
221 fn weights(&self) -> &[Float] {
222 self.solution.state.get_solution_weights().unwrap().as_slice()
223 }
224}
225
226pub fn create_scalar_operator_probability(
228 scalar_probability: Float,
229 random: Arc<dyn Random>,
230) -> TargetHeuristicProbability {
231 (Box::new(move |_, _| random.is_hit(scalar_probability)), PhantomData)
232}
233
234pub fn create_context_operator_probability(
236 jobs_threshold: usize,
237 routes_threshold: usize,
238 phases: Vec<(SelectionPhase, Float)>,
239 random: Arc<dyn Random>,
240) -> TargetHeuristicProbability {
241 let phases = phases.into_iter().collect::<HashMap<_, _>>();
242 (
243 Box::new(move |refinement_ctx, insertion_ctx| {
244 let below_thresholds = insertion_ctx.problem.jobs.size() < jobs_threshold
245 || insertion_ctx.solution.routes.len() < routes_threshold;
246
247 if below_thresholds {
248 return false;
249 }
250
251 let phase_probability = phases.get(&refinement_ctx.selection_phase()).cloned().unwrap_or(0.);
252 random.is_hit(phase_probability)
253 }),
254 PhantomData,
255 )
256}
257
258fn get_limits(problem: &Problem) -> (RemovalLimits, RemovalLimits) {
259 let normal_limits = RemovalLimits::new(problem);
260 let activities_range = normal_limits.removed_activities_range.clone();
261 let small_limits = RemovalLimits {
262 removed_activities_range: (activities_range.start / 3).max(2)..(activities_range.end / 3).max(8),
263 affected_routes_range: 1..2,
264 };
265
266 (normal_limits, small_limits)
267}
268
269const SINGLE_HEURISTIC_QUOTA_LIMIT: usize = 200;
270
271pub use self::builder::create_default_init_operators;
272pub use self::builder::create_default_processing;
273pub use self::statik::create_default_heuristic_operator;
274
275mod builder {
276 use super::*;
277 use crate::rosomaxa::evolution::InitialOperators;
278 use crate::solver::processing::*;
279 use crate::solver::RecreateInitialOperator;
280
281 pub fn create_default_init_operators(
283 problem: Arc<Problem>,
284 environment: Arc<Environment>,
285 ) -> InitialOperators<RefinementContext, GoalContext, InsertionContext> {
286 let random = environment.random.clone();
287 let wrap = |recreate: Arc<dyn Recreate>| Box::new(RecreateInitialOperator::new(recreate));
288
289 let mut main: InitialOperators<_, _, _> = vec![
290 (wrap(Arc::new(RecreateWithCheapest::new(random.clone()))), 1),
291 (wrap(Arc::new(RecreateWithFarthest::new(random.clone()))), 1),
292 (wrap(Arc::new(RecreateWithRegret::new(2, 3, random.clone()))), 1),
293 (wrap(Arc::new(RecreateWithGaps::new(1, (problem.jobs.size() / 10).max(1), random.clone()))), 1),
294 (wrap(Arc::new(RecreateWithSkipBest::new(1, 2, random.clone()))), 1),
295 (wrap(Arc::new(RecreateWithBlinks::new_with_defaults(random.clone()))), 1),
296 (wrap(Arc::new(RecreateWithPerturbation::new_with_defaults(random.clone()))), 1),
297 (wrap(Arc::new(RecreateWithNearestNeighbor::new(random.clone()))), 1),
298 ];
299
300 let alternatives = get_recreate_with_alternative_goal(problem.goal.as_ref(), {
301 move || RecreateWithCheapest::new(random.clone())
302 })
303 .map(|recreate| {
304 let init_operator: Box<
305 dyn InitialOperator<Context = RefinementContext, Objective = GoalContext, Solution = InsertionContext>
306 + Send
307 + Sync,
308 > = wrap(recreate);
309
310 (init_operator, 1)
311 })
312 .collect::<InitialOperators<_, _, _>>();
313
314 if alternatives.is_empty() {
315 main
316 } else {
317 main.splice(1..1, alternatives);
318 main
319 }
320 }
321
322 pub fn create_default_processing() -> ProcessingConfig<RefinementContext, GoalContext, InsertionContext> {
324 ProcessingConfig {
325 context: vec![Box::<VicinityClustering>::default()],
326 solution: vec![
327 Box::new(AdvanceDeparture::default()),
328 Box::<RescheduleReservedTime>::default(),
329 Box::<UnassignmentReason>::default(),
330 Box::<VicinityClustering>::default(),
331 ],
332 }
333 }
334}
335
336fn create_diversify_operators(
337 problem: Arc<Problem>,
338 environment: Arc<Environment>,
339) -> HeuristicDiversifyOperators<RefinementContext, GoalContext, InsertionContext> {
340 let random = environment.random.clone();
341
342 let recreates: Vec<(Arc<dyn Recreate>, usize)> = vec![
343 (Arc::new(RecreateWithSkipBest::new(1, 2, random.clone())), 1),
344 (Arc::new(RecreateWithRegret::new(1, 3, random.clone())), 1),
345 (Arc::new(RecreateWithPerturbation::new_with_defaults(random.clone())), 1),
346 (Arc::new(RecreateWithGaps::new(2, 20, random.clone())), 1),
347 (Arc::new(RecreateWithNearestNeighbor::new(random.clone())), 1),
348 (Arc::new(RecreateWithSlice::new(random.clone())), 1),
349 ];
350
351 let redistribute_search = Arc::new(RedistributeSearch::new(Arc::new(WeightedRecreate::new(recreates))));
352 let infeasible_search = Arc::new(InfeasibleSearch::new(
353 Arc::new(WeightedHeuristicOperator::new(
354 vec![
355 dynamic::create_default_inner_ruin_recreate(problem, environment.clone()),
356 dynamic::create_default_local_search(random.clone()),
357 ],
358 vec![10, 1],
359 )),
360 Arc::new(RecreateWithCheapest::new(random)),
361 4,
362 (0.05, 0.2),
363 (0.33, 0.75),
364 ));
365 let local_search = Arc::new(LocalSearch::new(Arc::new(CompositeLocalOperator::new(
366 vec![(Arc::new(ExchangeSequence::new(8, 0.5, 0.1)), 1)],
367 2,
368 4,
369 ))));
370
371 vec![Arc::new(WeightedHeuristicOperator::new(
372 vec![redistribute_search, local_search, infeasible_search],
373 vec![10, 2, 1],
374 ))]
375}
376
377mod statik {
378 use super::*;
379
380 pub fn create_default_heuristic_operator(
382 problem: Arc<Problem>,
383 environment: Arc<Environment>,
384 ) -> TargetSearchOperator {
385 let (normal_limits, small_limits) = get_limits(problem.as_ref());
386 let random = environment.random.clone();
387
388 let recreate = Arc::new(WeightedRecreate::new(vec![
390 (Arc::new(RecreateWithSkipBest::new(1, 2, random.clone())), 50),
391 (Arc::new(RecreateWithRegret::new(2, 3, random.clone())), 20),
392 (Arc::new(RecreateWithCheapest::new(random.clone())), 20),
393 (Arc::new(RecreateWithPerturbation::new_with_defaults(random.clone())), 10),
394 (Arc::new(RecreateWithSkipBest::new(3, 4, random.clone())), 5),
395 (Arc::new(RecreateWithGaps::new(2, 20, random.clone())), 5),
396 (Arc::new(RecreateWithBlinks::new_with_defaults(random.clone())), 5),
397 (Arc::new(RecreateWithFarthest::new(random.clone())), 2),
398 (Arc::new(RecreateWithSkipBest::new(4, 8, random.clone())), 2),
399 (Arc::new(RecreateWithNearestNeighbor::new(random.clone())), 1),
400 (Arc::new(RecreateWithSlice::new(random.clone())), 1),
401 (
402 Arc::new(RecreateWithSkipRandom::default_explorative_phased(
403 Arc::new(RecreateWithCheapest::new(random.clone())),
404 random.clone(),
405 )),
406 1,
407 ),
408 ]));
409
410 let close_route = Arc::new(CloseRouteRemoval::new(normal_limits.clone()));
412 let worst_route = Arc::new(WorstRouteRemoval::new(normal_limits.clone()));
413 let random_route = Arc::new(RandomRouteRemoval::new(normal_limits.clone()));
414
415 let random_job = Arc::new(RandomJobRemoval::new(normal_limits.clone()));
416 let extra_random_job = Arc::new(RandomJobRemoval::new(small_limits));
417
418 let ruin = Arc::new(WeightedRuin::new(vec![
419 (
420 vec![
421 (Arc::new(AdjustedStringRemoval::new_with_defaults(normal_limits.clone())), 1.),
422 (extra_random_job.clone(), 0.1),
423 ],
424 100,
425 ),
426 (vec![(Arc::new(NeighbourRemoval::new(normal_limits.clone())), 1.), (extra_random_job.clone(), 0.1)], 10),
427 (vec![(Arc::new(WorstJobRemoval::new(4, normal_limits)), 1.), (extra_random_job.clone(), 0.1)], 10),
428 (
429 vec![
430 (Arc::new(ClusterRemoval::new_with_defaults(problem.clone()).unwrap()), 1.),
432 (extra_random_job.clone(), 0.1),
433 ],
434 5,
435 ),
436 (vec![(close_route, 1.), (extra_random_job.clone(), 0.1)], 2),
437 (vec![(worst_route, 1.), (extra_random_job.clone(), 0.1)], 1),
438 (vec![(random_route, 1.), (extra_random_job.clone(), 0.1)], 1),
439 (vec![(random_job, 1.), (extra_random_job, 0.1)], 1),
440 ]));
441
442 Arc::new(WeightedHeuristicOperator::new(
443 vec![
444 Arc::new(RuinAndRecreate::new(ruin, recreate)),
445 create_default_local_search(environment.random.clone()),
446 ],
447 vec![100, 10],
448 ))
449 }
450
451 pub fn create_default_local_search(random: Arc<dyn Random>) -> TargetSearchOperator {
453 Arc::new(LocalSearch::new(Arc::new(CompositeLocalOperator::new(
454 vec![
455 (Arc::new(ExchangeSwapStar::new(random, SINGLE_HEURISTIC_QUOTA_LIMIT)), 200),
456 (Arc::new(ExchangeInterRouteBest::default()), 100),
457 (Arc::new(ExchangeSequence::default()), 100),
458 (Arc::new(ExchangeInterRouteRandom::default()), 30),
459 (Arc::new(ExchangeIntraRouteRandom::default()), 30),
460 (Arc::new(RescheduleDeparture::default()), 20),
461 ],
462 1,
463 2,
464 ))))
465 }
466}
467
468mod dynamic {
469 use super::*;
470
471 fn get_recreates(problem: &Problem, random: Arc<dyn Random>) -> Vec<(Arc<dyn Recreate>, String)> {
472 let cheapest: Arc<dyn Recreate> = Arc::new(RecreateWithCheapest::new(random.clone()));
473 vec![
474 (cheapest.clone(), "cheapest".to_string()),
475 (Arc::new(RecreateWithSkipBest::new(1, 2, random.clone())), "skip_best".to_string()),
476 (Arc::new(RecreateWithRegret::new(1, 3, random.clone())), "regret".to_string()),
477 (Arc::new(RecreateWithPerturbation::new_with_defaults(random.clone())), "perturbation".to_string()),
478 (Arc::new(RecreateWithGaps::new(2, 20, random.clone())), "gaps".to_string()),
479 (Arc::new(RecreateWithBlinks::new_with_defaults(random.clone())), "blinks".to_string()),
480 (Arc::new(RecreateWithFarthest::new(random.clone())), "farthest".to_string()),
481 (Arc::new(RecreateWithNearestNeighbor::new(random.clone())), "nearest".to_string()),
482 (
483 Arc::new(RecreateWithSkipRandom::default_explorative_phased(cheapest.clone(), random.clone())),
484 "skip_random".to_string(),
485 ),
486 (Arc::new(RecreateWithSlice::new(random.clone())), "slice".to_string()),
487 ]
488 .into_iter()
489 .chain(
490 get_recreate_with_alternative_goal(problem.goal.as_ref(), {
491 let random = random.clone();
492 move || RecreateWithCheapest::new(random.clone())
493 })
494 .enumerate()
495 .map(|(idx, recreate)| (recreate, format!("alternative_{idx}"))),
496 )
497 .collect()
498 }
499
500 fn get_ruins_with_limits(limits: RemovalLimits, prefix: &str) -> Vec<(Arc<dyn Ruin>, String, Float)> {
501 vec![
502 (Arc::new(AdjustedStringRemoval::new_with_defaults(limits.clone())), format!("{prefix}_asr"), 2.),
503 (Arc::new(NeighbourRemoval::new(limits.clone())), format!("{prefix}_neighbour_removal"), 5.),
504 (Arc::new(WorstJobRemoval::new(4, limits.clone())), format!("{prefix}_worst_job"), 4.),
505 (Arc::new(RandomJobRemoval::new(limits.clone())), format!("{prefix}_random_job_removal"), 4.),
506 (Arc::new(RandomRouteRemoval::new(limits.clone())), format!("{prefix}_random_route_removal"), 2.),
507 (Arc::new(CloseRouteRemoval::new(limits.clone())), format!("{prefix}_close_route_removal"), 4.),
508 (Arc::new(WorstRouteRemoval::new(limits)), format!("{prefix}_worst_route_removal"), 5.),
509 ]
510 }
511
512 fn get_ruins(problem: Arc<Problem>) -> Vec<(Arc<dyn Ruin>, String, Float)> {
513 vec![(
514 Arc::new(ClusterRemoval::new_with_defaults(problem.clone()).unwrap()),
516 "cluster_removal".to_string(),
517 4.,
518 )]
519 }
520
521 fn get_mutations(
522 problem: Arc<Problem>,
523 environment: Arc<Environment>,
524 ) -> Vec<(TargetSearchOperator, String, Float)> {
525 vec![
526 (
527 Arc::new(LocalSearch::new(Arc::new(ExchangeInterRouteBest::default()))),
528 "local_exch_inter_route_best".to_string(),
529 1.,
530 ),
531 (
532 Arc::new(LocalSearch::new(Arc::new(ExchangeInterRouteRandom::default()))),
533 "local_exch_inter_route_random".to_string(),
534 1.,
535 ),
536 (
537 Arc::new(LocalSearch::new(Arc::new(ExchangeIntraRouteRandom::default()))),
538 "local_exch_intra_route_random".to_string(),
539 1.,
540 ),
541 (
542 Arc::new(LocalSearch::new(Arc::new(RescheduleDeparture::default()))),
543 "local_reschedule_departure".to_string(),
544 1.,
545 ),
546 (
547 Arc::new(LocalSearch::new(Arc::new(ExchangeSwapStar::new(
548 environment.random.clone(),
549 SINGLE_HEURISTIC_QUOTA_LIMIT,
550 )))),
551 "local_swap_star".to_string(),
552 10.,
553 ),
554 (
556 create_variable_search_decompose_search(problem.clone(), environment.clone()),
557 "decompose_search_var".to_string(),
558 25.,
559 ),
560 (create_composite_decompose_search(problem, environment), "decompose_search_com".to_string(), 25.),
561 ]
562 }
563
564 pub fn get_operators(
565 problem: Arc<Problem>,
566 environment: Arc<Environment>,
567 ) -> Vec<(TargetSearchOperator, String, Float)> {
568 let (normal_limits, small_limits) = get_limits(problem.as_ref());
569 let random = environment.random.clone();
570
571 let recreates = get_recreates(problem.as_ref(), random.clone());
574
575 let ruins = get_ruins_with_limits(normal_limits.clone(), "normal")
576 .into_iter()
577 .chain(get_ruins_with_limits(small_limits.clone(), "small"))
578 .chain(get_ruins(problem.clone()))
579 .collect::<Vec<_>>();
580
581 let extra_random_job = Arc::new(RandomJobRemoval::new(small_limits));
582
583 let ruins = ruins
585 .into_iter()
586 .map::<(Arc<dyn Ruin>, String, Float), _>(|(ruin, name, weight)| {
587 (Arc::new(CompositeRuin::new(vec![(ruin, 1.), (extra_random_job.clone(), 0.1)])), name, weight)
588 })
589 .collect::<Vec<_>>();
590
591 let mutations = get_mutations(problem.clone(), environment.clone());
592
593 let heuristic_filter = problem.extras.get_heuristic_filter();
594
595 recreates
596 .iter()
597 .flat_map(|(recreate, recreate_name)| {
598 ruins.iter().map::<(TargetSearchOperator, String, Float), _>(move |(ruin, ruin_name, weight)| {
599 (
600 Arc::new(RuinAndRecreate::new(ruin.clone(), recreate.clone())),
601 format!("{ruin_name}+{recreate_name}"),
602 *weight,
603 )
604 })
605 })
606 .chain(mutations)
607 .filter(|(_, name, _)| heuristic_filter.as_ref().map_or(true, |filter| (filter)(name.as_str())))
608 .collect::<Vec<_>>()
609 }
610
611 pub fn create_default_inner_ruin_recreate(
612 problem: Arc<Problem>,
613 environment: Arc<Environment>,
614 ) -> Arc<RuinAndRecreate> {
615 let (_, small_limits) = get_limits(problem.as_ref());
616 let random = environment.random.clone();
617
618 let cheapest = Arc::new(RecreateWithCheapest::new(random.clone()));
620 let recreate = Arc::new(WeightedRecreate::new(vec![
621 (cheapest.clone(), 1),
622 (Arc::new(RecreateWithSkipBest::new(1, 2, random.clone())), 1),
623 (Arc::new(RecreateWithPerturbation::new_with_defaults(random.clone())), 1),
624 (Arc::new(RecreateWithSkipBest::new(3, 4, random.clone())), 1),
625 (Arc::new(RecreateWithGaps::new(2, 20, random.clone())), 1),
626 (Arc::new(RecreateWithBlinks::new_with_defaults(random.clone())), 1),
627 (Arc::new(RecreateWithFarthest::new(random.clone())), 1),
628 (Arc::new(RecreateWithSlice::new(random.clone())), 1),
629 (Arc::new(RecreateWithSkipRandom::default_explorative_phased(cheapest, random.clone())), 1),
630 ]));
631
632 let random_route = Arc::new(RandomRouteRemoval::new(small_limits.clone()));
634 let random_job = Arc::new(RandomJobRemoval::new(small_limits.clone()));
635 let random_ruin = Arc::new(WeightedRuin::new(vec![
636 (vec![(random_job.clone(), 1.)], 10),
637 (vec![(random_route.clone(), 1.)], 1),
638 ]));
639
640 let ruin = Arc::new(WeightedRuin::new(vec![
641 (
642 vec![
643 (Arc::new(AdjustedStringRemoval::new_with_defaults(small_limits.clone())), 1.),
644 (random_ruin.clone(), 0.1),
645 ],
646 1,
647 ),
648 (vec![(Arc::new(NeighbourRemoval::new(small_limits.clone())), 1.), (random_ruin.clone(), 0.1)], 1),
649 (vec![(Arc::new(WorstJobRemoval::new(4, small_limits)), 1.), (random_ruin, 0.1)], 1),
650 (vec![(random_job, 1.), (random_route, 0.1)], 1),
651 ]));
652
653 Arc::new(RuinAndRecreate::new(ruin, recreate))
654 }
655
656 pub fn create_default_local_search(random: Arc<dyn Random>) -> Arc<LocalSearch> {
657 Arc::new(LocalSearch::new(Arc::new(CompositeLocalOperator::new(
658 vec![
659 (Arc::new(ExchangeSwapStar::new(random, SINGLE_HEURISTIC_QUOTA_LIMIT / 4)), 2),
660 (Arc::new(ExchangeInterRouteBest::default()), 1),
661 (Arc::new(ExchangeInterRouteRandom::default()), 1),
662 (Arc::new(ExchangeIntraRouteRandom::default()), 1),
663 (Arc::new(ExchangeSequence::default()), 1),
664 ],
665 1,
666 1,
667 ))))
668 }
669
670 fn create_variable_search_decompose_search(
671 problem: Arc<Problem>,
672 environment: Arc<Environment>,
673 ) -> TargetSearchOperator {
674 Arc::new(DecomposeSearch::new(
675 Arc::new(WeightedHeuristicOperator::new(
676 vec![
677 create_default_inner_ruin_recreate(problem.clone(), environment.clone()),
678 create_default_local_search(environment.random.clone()),
679 ],
680 vec![10, 1],
681 )),
682 (2, 4),
683 2,
684 SINGLE_HEURISTIC_QUOTA_LIMIT,
685 ))
686 }
687
688 fn create_composite_decompose_search(problem: Arc<Problem>, environment: Arc<Environment>) -> TargetSearchOperator {
689 let limits = RemovalLimits { removed_activities_range: (10..100), affected_routes_range: 1..1 };
690 let route_removal_operator =
691 Arc::new(RuinAndRecreate::new(Arc::new(RandomRouteRemoval::new(limits)), Arc::new(DummyRecreate)));
692
693 Arc::new(DecomposeSearch::new(
694 Arc::new(CompositeHeuristicOperator::new(vec![
695 (route_removal_operator, 1.),
696 (create_default_inner_ruin_recreate(problem.clone(), environment.clone()), 1.),
697 ])),
698 (2, 4),
699 2,
700 SINGLE_HEURISTIC_QUOTA_LIMIT,
701 ))
702 }
703}
704
705fn get_recreate_with_alternative_goal<T, F>(
706 original_goal: &GoalContext,
707 recreate_fn: F,
708) -> impl Iterator<Item = Arc<dyn Recreate>> + '_
709where
710 T: Recreate + Send + Sync + 'static,
711 F: Fn() -> T + 'static,
712{
713 original_goal
714 .get_alternatives()
715 .map::<Arc<dyn Recreate>, _>(move |goal| Arc::new(RecreateWithGoal::new(Arc::new(goal), recreate_fn())))
716}