Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
routing_ils.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cmath>
18#include <cstddef>
19#include <cstdint>
20#include <functional>
21#include <limits>
22#include <memory>
23#include <optional>
24#include <random>
25#include <utility>
26#include <vector>
27
28#include "absl/functional/bind_front.h"
29#include "absl/log/check.h"
30#include "absl/log/log.h"
31#include "absl/time/time.h"
32#include "absl/types/span.h"
33#include "google/protobuf/repeated_ptr_field.h"
40
41namespace operations_research {
42namespace {
43
44// Returns global cheapest insertion parameters based on the given recreate
45// strategy if available. Returns default parameters otherwise.
47GetGlobalCheapestInsertionParametersForRecreateStrategy(
48 const RecreateStrategy& recreate_strategy,
49 const GlobalCheapestInsertionParameters& default_parameters) {
50 return recreate_strategy.has_parameters() &&
51 recreate_strategy.parameters().has_global_cheapest_insertion()
52 ? recreate_strategy.parameters().global_cheapest_insertion()
53 : default_parameters;
54}
55
56// Returns local cheapest insertion parameters based on the given recreate
57// strategy if available. Returns default parameters otherwise.
59GetLocalCheapestInsertionParametersForRecreateStrategy(
60 const RecreateStrategy& recreate_strategy,
61 const LocalCheapestInsertionParameters& default_parameters) {
62 return recreate_strategy.has_parameters() &&
63 recreate_strategy.parameters().has_local_cheapest_insertion()
64 ? recreate_strategy.parameters().local_cheapest_insertion()
65 : default_parameters;
66}
67
68SavingsParameters GetSavingsParametersForRecreateStrategy(
69 const RecreateStrategy& recreate_strategy,
70 const SavingsParameters& default_parameters) {
71 return recreate_strategy.has_parameters() &&
72 recreate_strategy.parameters().has_savings()
73 ? recreate_strategy.parameters().savings()
74 : default_parameters;
75}
76
77// Returns a ruin procedure based on the given ruin strategy.
78std::unique_ptr<RuinProcedure> MakeRuinProcedure(
79 RoutingModel* model, std::mt19937* rnd, RuinStrategy ruin,
80 int num_neighbors_for_route_selection) {
81 switch (ruin.strategy_case()) {
83 return std::make_unique<CloseRoutesRemovalRuinProcedure>(
84 model, rnd, ruin.spatially_close_routes().num_ruined_routes(),
85 num_neighbors_for_route_selection);
86 break;
88 return std::make_unique<RandomWalkRemovalRuinProcedure>(
89 model, rnd, ruin.random_walk().num_removed_visits(),
90 num_neighbors_for_route_selection);
92 return std::make_unique<SISRRuinProcedure>(
93 model, rnd, ruin.sisr().max_removed_sequence_size(),
94 ruin.sisr().avg_num_removed_visits(), ruin.sisr().bypass_factor(),
95 num_neighbors_for_route_selection);
96 default:
97 LOG(DFATAL) << "Unsupported ruin procedure.";
98 return nullptr;
99 }
100}
101
102// Returns the ruin procedures associated with the given ruin strategies.
103std::vector<std::unique_ptr<RuinProcedure>> MakeRuinProcedures(
104 RoutingModel* model, std::mt19937* rnd,
105 const google::protobuf::RepeatedPtrField<
106 ::operations_research::RuinStrategy>& ruin_strategies,
107 int num_neighbors_for_route_selection) {
108 std::vector<std::unique_ptr<RuinProcedure>> ruin_procedures;
109 for (const RuinStrategy& ruin : ruin_strategies) {
110 ruin_procedures.push_back(
111 MakeRuinProcedure(model, rnd, ruin, num_neighbors_for_route_selection));
112 }
113 return ruin_procedures;
114}
115
116class SequentialCompositionStrategy
118 public:
119 explicit SequentialCompositionStrategy(
120 std::vector<RuinProcedure*> ruin_procedures)
121 : CompositeRuinProcedure::CompositionStrategy(
122 std::move(ruin_procedures)) {}
123 const std::vector<RuinProcedure*>& Select() override { return ruins_; }
124};
125
126class SequentialRandomizedCompositionStrategy
128 public:
129 SequentialRandomizedCompositionStrategy(
130 std::vector<RuinProcedure*> ruin_procedures, std::mt19937* rnd)
131 : CompositeRuinProcedure::CompositionStrategy(std::move(ruin_procedures)),
132 rnd_(*rnd) {}
133 const std::vector<RuinProcedure*>& Select() override {
134 std::shuffle(ruins_.begin(), ruins_.end(), rnd_);
135 return ruins_;
136 }
137
138 private:
139 std::mt19937& rnd_;
140};
141
142class SingleRandomCompositionStrategy
144 public:
145 SingleRandomCompositionStrategy(std::vector<RuinProcedure*> ruin_procedures,
146 std::mt19937* rnd)
147 : CompositeRuinProcedure::CompositionStrategy(std::move(ruin_procedures)),
148 rnd_(*rnd) {
149 single_ruin_.resize(1);
150 }
151 const std::vector<RuinProcedure*>& Select() override {
152 single_ruin_[0] = ruins_[rnd_() % ruins_.size()];
153 return single_ruin_;
154 }
155
156 private:
157 std::mt19937& rnd_;
158
159 // Stores the single ruin that will be returned.
160 std::vector<RuinProcedure*> single_ruin_;
161};
162
163// Returns a composition strategy based on the input parameters.
164std::unique_ptr<CompositeRuinProcedure::CompositionStrategy>
165MakeRuinCompositionStrategy(
166 absl::Span<const std::unique_ptr<RuinProcedure>> ruins,
167 RuinCompositionStrategy::Value composition_strategy, std::mt19937* rnd) {
168 std::vector<RuinProcedure*> ruin_ptrs;
169 ruin_ptrs.reserve(ruins.size());
170 for (const auto& ruin : ruins) {
171 ruin_ptrs.push_back(ruin.get());
172 }
173
174 switch (composition_strategy) {
176 return std::make_unique<SequentialCompositionStrategy>(
177 std::move(ruin_ptrs));
179 return std::make_unique<SequentialRandomizedCompositionStrategy>(
180 std::move(ruin_ptrs), rnd);
182 return std::make_unique<SingleRandomCompositionStrategy>(
183 std::move(ruin_ptrs), rnd);
184 default:
185 LOG(DFATAL) << "Unsupported composition strategy.";
186 return nullptr;
187 }
188}
189
190// Returns a ruin procedure based on the given ruin and recreate parameters.
191std::unique_ptr<RuinProcedure> MakeRuinProcedure(
192 const RuinRecreateParameters& parameters, RoutingModel* model,
193 std::mt19937* rnd) {
194 const int num_non_start_end_nodes = model->Size() - model->vehicles();
195 const uint32_t preferred_num_neighbors =
196 parameters.route_selection_neighbors_ratio() * num_non_start_end_nodes;
197
198 // TODO(user): rename parameters.route_selection_max_neighbors to something
199 // more general that can be used by multiple ruin procedures.
200 const uint32_t num_neighbors_for_route_selection =
201 std::min(parameters.route_selection_max_neighbors(),
202 std::max(parameters.route_selection_min_neighbors(),
203 preferred_num_neighbors));
204
205 if (parameters.ruin_strategies().size() == 1) {
206 return MakeRuinProcedure(model, rnd, *parameters.ruin_strategies().begin(),
207 num_neighbors_for_route_selection);
208 }
209
210 return std::make_unique<CompositeRuinProcedure>(
211 model,
212 MakeRuinProcedures(model, rnd, parameters.ruin_strategies(),
213 num_neighbors_for_route_selection),
214 parameters.ruin_composition_strategy(), rnd);
215}
216
217// Returns a recreate procedure based on the given parameters.
218std::unique_ptr<RoutingFilteredHeuristic> MakeRecreateProcedure(
219 const RoutingSearchParameters& parameters, RoutingModel* model,
220 std::function<bool()> stop_search,
221 LocalSearchFilterManager* filter_manager) {
222 const RecreateStrategy& recreate_strategy =
223 parameters.iterated_local_search_parameters()
224 .ruin_recreate_parameters()
225 .recreate_strategy();
226 switch (recreate_strategy.heuristic()) {
228 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
229 model, std::move(stop_search),
230 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
231 GetLocalCheapestInsertionParametersForRecreateStrategy(
232 recreate_strategy,
233 parameters.local_cheapest_insertion_parameters()),
234 filter_manager, model->GetBinCapacities());
236 return std::make_unique<LocalCheapestInsertionFilteredHeuristic>(
237 model, std::move(stop_search),
238 /*evaluator=*/nullptr,
239 GetLocalCheapestInsertionParametersForRecreateStrategy(
240 recreate_strategy,
241 parameters.local_cheapest_cost_insertion_parameters()),
242 filter_manager, model->GetBinCapacities());
244 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
245 model, std::move(stop_search),
246 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
247 [model](int64_t i) { return model->UnperformedPenaltyOrValue(0, i); },
248 filter_manager,
249 GetGlobalCheapestInsertionParametersForRecreateStrategy(
250 recreate_strategy,
251 parameters.global_cheapest_insertion_first_solution_parameters()),
252 /*is_sequential=*/true);
254 return std::make_unique<GlobalCheapestInsertionFilteredHeuristic>(
255 model, std::move(stop_search),
256 absl::bind_front(&RoutingModel::GetArcCostForVehicle, model),
257 [model](int64_t i) { return model->UnperformedPenaltyOrValue(0, i); },
258 filter_manager,
259 GetGlobalCheapestInsertionParametersForRecreateStrategy(
260 recreate_strategy,
261 parameters.global_cheapest_insertion_first_solution_parameters()),
262 /*is_sequential=*/false);
264 return std::make_unique<SequentialSavingsFilteredHeuristic>(
265 model, std::move(stop_search),
266 GetSavingsParametersForRecreateStrategy(
267 recreate_strategy, parameters.savings_parameters()),
268 filter_manager);
270 return std::make_unique<ParallelSavingsFilteredHeuristic>(
271 model, std::move(stop_search),
272 GetSavingsParametersForRecreateStrategy(
273 recreate_strategy, parameters.savings_parameters()),
274 filter_manager);
275 default:
276 LOG(DFATAL) << "Unsupported recreate procedure.";
277 return nullptr;
278 }
279
280 return nullptr;
281}
282
283// Greedy criterion in which the reference assignment is only replaced by an
284// improving candidate assignment.
285class GreedyDescentAcceptanceCriterion : public NeighborAcceptanceCriterion {
286 public:
287 bool Accept([[maybe_unused]] const SearchState& search_state,
288 const Assignment* candidate,
289 const Assignment* reference) override {
290 return candidate->ObjectiveValue() < reference->ObjectiveValue();
291 }
292};
293
294// Simulated annealing cooling schedule interface.
295class CoolingSchedule {
296 public:
297 CoolingSchedule(NeighborAcceptanceCriterion::SearchState final_search_state,
298 double initial_temperature, double final_temperature)
299 : final_search_state_(std::move(final_search_state)),
300 initial_temperature_(initial_temperature),
301 final_temperature_(final_temperature) {
302 DCHECK_GE(initial_temperature_, final_temperature_);
303 }
304 virtual ~CoolingSchedule() = default;
305
306 // Returns the temperature according to given search state.
307 virtual double GetTemperature(
308 const NeighborAcceptanceCriterion::SearchState& search_state) const = 0;
309
310 protected:
311 // Returns the progress of the given search state with respect to the final
312 // search state.
313 double GetProgress(
314 const NeighborAcceptanceCriterion::SearchState& search_state) const {
315 const double duration_progress =
316 absl::FDivDuration(search_state.duration, final_search_state_.duration);
317 const double solutions_progress =
318 static_cast<double>(search_state.solutions) /
319 final_search_state_.solutions;
320 const double progress = std::max(duration_progress, solutions_progress);
321 // We take the min with 1 as at the end of the search we may go a bit above
322 // 1 with duration_progress depending on when we check the time limit.
323 return std::min(1.0, progress);
324 }
325
326 const NeighborAcceptanceCriterion::SearchState final_search_state_;
327 const double initial_temperature_;
328 const double final_temperature_;
329};
330
331// A cooling schedule that lowers the temperature in an exponential way.
332class ExponentialCoolingSchedule : public CoolingSchedule {
333 public:
334 ExponentialCoolingSchedule(
335 NeighborAcceptanceCriterion::SearchState final_search_state,
336 double initial_temperature, double final_temperature)
337 : CoolingSchedule(std::move(final_search_state), initial_temperature,
338 final_temperature),
339 temperature_ratio_(final_temperature / initial_temperature) {}
340
341 double GetTemperature(const NeighborAcceptanceCriterion::SearchState&
342 search_state) const override {
343 const double progress = GetProgress(search_state);
344
345 return initial_temperature_ * std::pow(temperature_ratio_, progress);
346 }
347
348 private:
349 const double temperature_ratio_;
350};
351
352// A cooling schedule that lowers the temperature in a linear way.
353class LinearCoolingSchedule : public CoolingSchedule {
354 public:
355 LinearCoolingSchedule(
356 NeighborAcceptanceCriterion::SearchState final_search_state,
357 double initial_temperature, double final_temperature)
358 : CoolingSchedule(std::move(final_search_state), initial_temperature,
359 final_temperature) {}
360
361 double GetTemperature(const NeighborAcceptanceCriterion::SearchState&
362 search_state) const override {
363 const double progress = GetProgress(search_state);
364 return initial_temperature_ -
365 progress * (initial_temperature_ - final_temperature_);
366 }
367};
368
369// Returns a cooling schedule based on the given input parameters.
370std::unique_ptr<CoolingSchedule> MakeCoolingSchedule(
371 const RoutingModel& model,
373 const NeighborAcceptanceCriterion::SearchState& final_search_state,
374 std::mt19937* rnd) {
375 const auto [initial_temperature, final_temperature] =
376 GetSimulatedAnnealingTemperatures(model, sa_params, rnd);
377
378 switch (sa_params.cooling_schedule_strategy()) {
380 return std::make_unique<ExponentialCoolingSchedule>(
381 final_search_state, initial_temperature, final_temperature);
383 return std::make_unique<LinearCoolingSchedule>(
384 final_search_state, initial_temperature, final_temperature);
385 default:
386 LOG(DFATAL) << "Unsupported cooling schedule strategy.";
387 return nullptr;
388 }
389}
390
391// Simulated annealing acceptance criterion in which the reference assignment is
392// replaced with a probability given by the quality of the candidate solution,
393// the current search state and the chosen cooling schedule.
394class SimulatedAnnealingAcceptanceCriterion
396 public:
397 explicit SimulatedAnnealingAcceptanceCriterion(
398 std::unique_ptr<CoolingSchedule> cooling_schedule, std::mt19937* rnd)
399 : cooling_schedule_(std::move(cooling_schedule)),
400 rnd_(*rnd),
401 probability_distribution_(0.0, 1.0) {}
402
403 bool Accept(const SearchState& search_state, const Assignment* candidate,
404 const Assignment* reference) override {
405 double temperature = cooling_schedule_->GetTemperature(search_state);
406 return candidate->ObjectiveValue() +
407 temperature * std::log(probability_distribution_(rnd_)) <
408 reference->ObjectiveValue();
409 }
410
411 private:
412 std::unique_ptr<CoolingSchedule> cooling_schedule_;
413 std::mt19937 rnd_;
414 std::uniform_real_distribution<double> probability_distribution_;
415};
416
417// Acceptance criterion in which a candidate assignment is accepted when it has
418// all nodes performed.
419class AllNodesPerformedAcceptanceCriterion
421 public:
422 explicit AllNodesPerformedAcceptanceCriterion(const RoutingModel& model)
423 : model_(model) {}
424
425 bool Accept([[maybe_unused]] const SearchState& search_state,
426 const Assignment* candidate,
427 [[maybe_unused]] const Assignment* reference) override {
429 d < model_.GetNumberOfDisjunctions(); ++d) {
430 // This solution avoids counting non-fixed variables as inactive.
431 int num_possible_actives = model_.GetDisjunctionNodeIndices(d).size();
432 for (const int64_t node : model_.GetDisjunctionNodeIndices(d)) {
433 if (candidate->Value(model_.NextVar(node)) == node) {
434 --num_possible_actives;
435 }
436 }
437 if (num_possible_actives < model_.GetDisjunctionMaxCardinality(d)) {
438 return false;
439 }
440 }
441
442 for (int node = 0; node < model_.Size(); ++node) {
443 if (model_.IsStart(node) || model_.IsEnd(node)) continue;
444 if (!model_.GetDisjunctionIndices(node).empty()) continue;
445 if (candidate->Value(model_.NextVar(node)) == node) return false;
446 }
447 return true;
448 }
449
450 private:
451 const RoutingModel& model_;
452};
453
454// Returns the number of performed non-start/end nodes in the given assignment.
455int CountPerformedNodes(const RoutingModel& model,
456 const Assignment& assignment) {
457 int count = 0;
458 for (int v = 0; v < model.vehicles(); ++v) {
459 int64_t current_node_index = model.Start(v);
460 while (true) {
461 current_node_index = assignment.Value(model.NextVar(current_node_index));
462 if (model.IsEnd(current_node_index)) {
463 break;
464 }
465 count++;
466 }
467 }
468 return count;
469}
470
471// Acceptance criterion in which a candidate assignment is accepted when it
472// performs at least one more node than the reference assignment.
473class MoreNodesPerformedAcceptanceCriterion
475 public:
476 explicit MoreNodesPerformedAcceptanceCriterion(const RoutingModel& model)
477 : model_(model) {}
478
479 bool Accept([[maybe_unused]] const SearchState& search_state,
480 const Assignment* candidate,
481 const Assignment* reference) override {
482 return CountPerformedNodes(model_, *candidate) >
483 CountPerformedNodes(model_, *reference);
484 }
485
486 private:
487 const RoutingModel& model_;
488};
489
490class AbsencesBasedAcceptanceCriterion : public NeighborAcceptanceCriterion {
491 public:
492 explicit AbsencesBasedAcceptanceCriterion(
493 const RoutingModel& model, bool remove_route_with_lowest_absences)
494 : model_(model),
495 remove_route_with_lowest_absences_(remove_route_with_lowest_absences),
496 absences_(model.Size(), 0) {}
497
498 bool Accept([[maybe_unused]] const SearchState& search_state,
499 const Assignment* candidate,
500 const Assignment* reference) override {
501 int sum_candidate_absences = 0;
502 int sum_reference_absences = 0;
503 for (int node = 0; node < model_.Size(); ++node) {
504 if (model_.IsStart(node) || model_.IsEnd(node)) continue;
505 if (candidate->Value(model_.NextVar(node)) == node) {
506 sum_candidate_absences += absences_[node];
507 }
508 if (reference->Value(model_.NextVar(node)) == node) {
509 sum_reference_absences += absences_[node];
510 }
511 }
512 return sum_candidate_absences < sum_reference_absences;
513 }
514
515 void OnIterationEnd(const Assignment* reference) override {
516 for (int node = 0; node < model_.Size(); ++node) {
517 if (model_.IsStart(node) || model_.IsEnd(node)) continue;
518 if (reference->Value(model_.NextVar(node)) == node) {
519 ++absences_[node];
520 }
521 }
522 }
523
524 void OnBestSolutionFound(Assignment* reference) override {
525 if (!remove_route_with_lowest_absences_) return;
526
527 int candidate_route = -1;
528 int min_sum_absences = std::numeric_limits<int>::max();
529
530 for (int route = 0; route < model_.vehicles(); ++route) {
531 if (model_.Next(*reference, model_.Start(route)) == model_.End(route))
532 continue;
533 int sum_absences = 0;
534 for (int64_t node = reference->Value(model_.NextVar(model_.Start(route)));
535 node != model_.End(route);
536 node = reference->Value(model_.NextVar(node))) {
537 sum_absences += absences_[node];
538 }
539
540 if (sum_absences < min_sum_absences) {
541 candidate_route = route;
542 min_sum_absences = sum_absences;
543 }
544 }
545
546 // Remove the route with the lowest sum of absences.
547 if (candidate_route != -1) {
548 // Set next pointers for inner nodes.
549 int64_t node =
550 reference->Value(model_.NextVar(model_.Start(candidate_route)));
551 while (node != model_.End(candidate_route)) {
552 const int64_t next_node = reference->Value(model_.NextVar(node));
553 reference->SetValue(model_.NextVar(node), node);
554 reference->SetValue(model_.VehicleVar(node), -1);
555 node = next_node;
556 }
557 // Set next pointer for start node.
558 reference->SetValue(model_.NextVar(model_.Start(candidate_route)),
559 model_.End(candidate_route));
560 }
561 }
562
563 private:
564 const RoutingModel& model_;
565 bool remove_route_with_lowest_absences_;
566 std::vector<int> absences_;
567};
568
569// Returns whether the given assignment has at least one performed node.
570bool HasPerformedNodes(const RoutingModel& model,
571 const Assignment& assignment) {
572 for (int v = 0; v < model.vehicles(); ++v) {
573 if (model.Next(assignment, model.Start(v)) != model.End(v)) {
574 return true;
575 }
576 }
577 return false;
578}
579
580// Returns the number of used vehicles.
581int CountUsedVehicles(const RoutingModel& model, const Assignment& assignment) {
582 int count = 0;
583 for (int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
584 count += model.Next(assignment, model.Start(vehicle)) != model.End(vehicle);
585 }
586 return count;
587}
588
589// Returns the average route size of non empty routes.
590double ComputeAverageNonEmptyRouteSize(const RoutingModel& model,
591 const Assignment& assignment) {
592 const int num_used_vehicles = CountUsedVehicles(model, assignment);
593 if (num_used_vehicles == 0) return 0;
594
595 const double num_visits = model.Size() - model.vehicles();
596 return num_visits / num_used_vehicles;
597}
598
599// Returns a random performed visit for the given assignment. The procedure
600// requires a distribution including all visits. Returns -1 if there are no
601// performed visits.
602int64_t PickRandomPerformedVisit(
603 const RoutingModel& model, const Assignment& assignment, std::mt19937& rnd,
604 std::uniform_int_distribution<int64_t>& node_dist) {
605 DCHECK_EQ(node_dist.min(), 0);
606 DCHECK_EQ(node_dist.max(), model.Size() - model.vehicles());
607
608 if (!HasPerformedNodes(model, assignment)) {
609 return -1;
610 }
611
612 int64_t node;
613 do {
614 node = node_dist(rnd);
615 } while (model.IsStart(node) ||
616 assignment.Value(model.VehicleVar(node)) == -1);
617 DCHECK(!model.IsEnd(node));
618 return node;
619}
620} // namespace
621
622RoutingSolution::RoutingSolution(const RoutingModel& model) : model_(model) {
623 const int all_nodes = model.Size() + model.vehicles();
624
625 nexts_.resize(all_nodes, -1);
626 prevs_.resize(all_nodes, -1);
627 route_sizes_.resize(model.vehicles(), 0);
628}
629
630void RoutingSolution::Reset(const Assignment* assignment) {
631 assignment_ = assignment;
632
633 // TODO(user): consider resetting only previously set values.
634 nexts_.assign(nexts_.size(), -1);
635 // TODO(user): consider removing the resets below, and only rely on
636 // nexts_.
637 prevs_.assign(prevs_.size(), -1);
638 route_sizes_.assign(model_.vehicles(), -1);
639}
640
642 const int64_t start = model_.Start(vehicle);
643
644 if (BelongsToInitializedRoute(start)) {
645 return;
646 }
647
648 const int64_t end = model_.End(vehicle);
649
650 int64_t prev = end;
651 int64_t curr = start;
652
653 // Setup the start and inner nodes.
654 route_sizes_[vehicle] = -1;
655 while (curr != end) {
656 const int64_t next = assignment_->Value(model_.NextVar(curr));
657
658 nexts_[curr] = next;
659 prevs_[curr] = prev;
660 ++route_sizes_[vehicle];
661
662 prev = curr;
663 curr = next;
664 }
665
666 // Setup the end node.
667 nexts_[end] = start;
668 prevs_[end] = prev;
669}
670
672 DCHECK_EQ(nexts_[node] != -1, prevs_[node] != -1);
673 return nexts_[node] != -1;
674}
675
676int64_t RoutingSolution::GetNextNodeIndex(int64_t node) const {
677 return BelongsToInitializedRoute(node)
678 ? nexts_[node]
679 : assignment_->Value(model_.NextVar(node));
680}
681
683 DCHECK(BelongsToInitializedRoute(node));
684 return prevs_[node];
685}
686
687int RoutingSolution::GetRouteSize(int vehicle) const {
688 DCHECK(BelongsToInitializedRoute(model_.Start(vehicle)));
689 return route_sizes_[vehicle];
690}
691
692bool RoutingSolution::CanBeRemoved(int64_t node) const {
693 return !model_.IsStart(node) && !model_.IsEnd(node) &&
694 GetNextNodeIndex(node) != node;
695}
696
697void RoutingSolution::RemoveNode(int64_t node) {
698 DCHECK(BelongsToInitializedRoute(node));
699
700 DCHECK_NE(nexts_[node], node);
701 DCHECK_NE(prevs_[node], node);
702
703 const int64_t next = nexts_[node];
704 const int64_t prev = prevs_[node];
705
706 const int vehicle = assignment_->Value(model_.VehicleVar(node));
707 --route_sizes_[vehicle];
708 DCHECK_GE(route_sizes_[vehicle], 0);
709
710 nexts_[prev] = next;
711 prevs_[next] = prev;
712
713 nexts_[node] = node;
714 prevs_[node] = node;
715}
716
718 DCHECK(!model_.IsStart(node));
719 DCHECK(!model_.IsEnd(node));
720 if (const std::optional<int64_t> sibling_node =
721 model_.GetFirstMatchingPickupDeliverySibling(
722 node,
723 [this](int64_t candidate_node) {
724 return CanBeRemoved(candidate_node);
725 });
726 sibling_node.has_value()) {
727 const int sibling_vehicle =
728 assignment_->Value(model_.VehicleVar(sibling_node.value()));
729 DCHECK_NE(sibling_vehicle, -1);
730
731 InitializeRouteInfoIfNeeded(sibling_vehicle);
732 RemoveNode(sibling_node.value());
733 }
734}
735
737 int64_t visit, std::mt19937& rnd,
738 std::bernoulli_distribution& boolean_dist) const {
739 DCHECK(BelongsToInitializedRoute(visit));
740 DCHECK(!model_.IsStart(visit));
741 DCHECK(!model_.IsEnd(visit));
742 // The visit is performed.
743 DCHECK(CanBeRemoved(visit));
744
745 const int vehicle = assignment_->Value(model_.VehicleVar(visit));
746 if (GetRouteSize(vehicle) <= 1) {
747 return -1;
748 }
749
750 const bool move_forward = boolean_dist(rnd);
751 int64_t next_node = move_forward ? GetNextNodeIndex(visit)
753 if (model_.IsStart(next_node) || model_.IsEnd(next_node)) {
754 next_node = move_forward ? GetInitializedPrevNodeIndex(visit)
755 : GetNextNodeIndex(visit);
756 }
757 DCHECK(!model_.IsStart(next_node));
758 DCHECK(!model_.IsEnd(next_node));
759 return next_node;
760}
761
763 int64_t seed_visit, std::mt19937& rnd,
764 std::bernoulli_distribution& boolean_dist, int size) const {
765 DCHECK(BelongsToInitializedRoute(seed_visit));
766 DCHECK(!model_.IsStart(seed_visit));
767 DCHECK(!model_.IsEnd(seed_visit));
768 // The seed visit is actually performed.
769 DCHECK(CanBeRemoved(seed_visit));
770
771 // The seed visit is always included.
772 --size;
773
774 // Sequence's excluded boundaries.
775 int64_t left = GetInitializedPrevNodeIndex(seed_visit);
776 int64_t right = GetNextNodeIndex(seed_visit);
777
778 while (size-- > 0) {
779 if (model_.IsStart(left) && model_.IsEnd(right)) {
780 // We can no longer extend the sequence either way.
781 break;
782 }
783
784 // When left is at the start (resp. right is at the end), we can
785 // only extend right (resp. left), and if both ends are free to
786 // move we decide the direction at random.
787 if (model_.IsStart(left)) {
788 right = GetNextNodeIndex(right);
789 } else if (model_.IsEnd(right)) {
790 left = GetInitializedPrevNodeIndex(left);
791 } else {
792 const bool move_forward = boolean_dist(rnd);
793 if (move_forward) {
794 right = GetNextNodeIndex(right);
795 } else {
796 left = GetInitializedPrevNodeIndex(left);
797 }
798 }
799 }
800
801 // TODO(user): consider taking the container in input to avoid multiple
802 // memory allocations.
803 std::vector<int64_t> sequence;
804 int64_t curr = GetNextNodeIndex(left);
805 while (curr != right) {
806 sequence.push_back(curr);
807 curr = GetNextNodeIndex(curr);
808 }
809 return sequence;
810}
811
813 std::vector<RuinProcedure*> ruin_procedures)
814 : ruins_(std::move(ruin_procedures)) {}
815
817 RoutingModel* model,
818 std::vector<std::unique_ptr<RuinProcedure>> ruin_procedures,
819 RuinCompositionStrategy::Value composition_strategy, std::mt19937* rnd)
820 : model_(*model),
821 ruin_procedures_(std::move(ruin_procedures)),
822 composition_strategy_(MakeRuinCompositionStrategy(
823 ruin_procedures_, composition_strategy, rnd)),
824 ruined_assignment_(model_.solver()->MakeAssignment()),
825 next_assignment_(model_.solver()->MakeAssignment()) {}
826
827std::function<int64_t(int64_t)> CompositeRuinProcedure::Ruin(
828 const Assignment* assignment) {
829 const std::vector<RuinProcedure*>& ruins = composition_strategy_->Select();
830
831 auto next_accessors = ruins[0]->Ruin(assignment);
832 for (int i = 1; i < ruins.size(); ++i) {
833 const Assignment* next_assignment =
834 BuildAssignmentFromNextAccessor(next_accessors);
835 ruined_assignment_->Copy(next_assignment);
836 next_accessors = ruins[i]->Ruin(ruined_assignment_);
837 }
838
839 return next_accessors;
840}
841
842const Assignment* CompositeRuinProcedure::BuildAssignmentFromNextAccessor(
843 const std::function<int64_t(int64_t)>& next_accessors) {
844 next_assignment_->Clear();
845
846 // Setup next variables for nodes and vehicle variables for unperformed nodes.
847 for (int node = 0; node < model_.Size(); node++) {
848 const int64_t next = next_accessors(node);
849 next_assignment_->Add(model_.NextVar(node))->SetValue(next);
850 if (next == node) {
851 // Node is unperformed, we set its vehicle var accordingly.
852 next_assignment_->Add(model_.VehicleVar(node))->SetValue(-1);
853 }
854 }
855
856 // Setup vehicle variables for performed nodes.
857 for (int vehicle = 0; vehicle < model_.vehicles(); ++vehicle) {
858 int64_t node = model_.Start(vehicle);
859 while (!model_.IsEnd(node)) {
860 next_assignment_->Add(model_.VehicleVar(node))->SetValue(vehicle);
861 node = next_accessors(node);
862 }
863 // Also set the vehicle var for the vehicle end.
864 next_assignment_->Add(model_.VehicleVar(node))->SetValue(vehicle);
865 }
866
867 return next_assignment_;
868}
869
871 RoutingModel* model, std::mt19937* rnd, size_t num_routes,
872 int num_neighbors_for_route_selection)
873 : model_(*model),
874 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
875 {num_neighbors_for_route_selection,
876 /*add_vehicle_starts_to_neighbors=*/false,
877 /*add_vehicle_ends_to_neighbors=*/false,
878 /*only_sort_neighbors_for_partial_neighborhoods=*/false})),
879 num_routes_(num_routes),
880 rnd_(*rnd),
881 node_dist_(0, model->Size() - model->vehicles()),
882 removed_routes_(model->vehicles()) {}
883
884std::function<int64_t(int64_t)> CloseRoutesRemovalRuinProcedure::Ruin(
885 const Assignment* assignment) {
886 if (num_routes_ == 0) {
887 return [this, assignment](int64_t node) {
888 return assignment->Value(model_.NextVar(node));
889 };
890 }
891
892 const int64_t seed_node =
893 PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
894 if (seed_node == -1) {
895 return [this, assignment](int64_t node) {
896 return assignment->Value(model_.NextVar(node));
897 };
898 }
899
900 removed_routes_.ResetAllToFalse();
901
902 const int seed_route = assignment->Value(model_.VehicleVar(seed_node));
903 DCHECK_GE(seed_route, 0);
904
905 removed_routes_.Set(seed_route);
906
907 const RoutingCostClassIndex cost_class_index =
908 model_.GetCostClassIndexOfVehicle(seed_route);
909
910 const std::vector<int>& neighbors =
911 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
912 cost_class_index.value(), seed_node);
913
914 for (int neighbor : neighbors) {
915 if (removed_routes_.NumberOfSetCallsWithDifferentArguments() ==
916 num_routes_) {
917 break;
918 }
919 const int64_t route = assignment->Value(model_.VehicleVar(neighbor));
920 if (route < 0 || removed_routes_[route]) {
921 continue;
922 }
923 removed_routes_.Set(route);
924 }
925
926 return [this, assignment](int64_t node) {
927 // Shortcut removed routes to remove associated nodes.
928 if (model_.IsStart(node)) {
929 const int route = assignment->Value(model_.VehicleVar(node));
930 if (removed_routes_[route]) {
931 return model_.End(route);
932 }
933 }
934 return assignment->Value(model_.NextVar(node));
935 };
936}
937
939 RoutingModel* model, std::mt19937* rnd, int walk_length,
940 int num_neighbors_for_route_selection)
941 : model_(*model),
942 routing_solution_(*model),
943 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
944 {num_neighbors_for_route_selection,
945 /*add_vehicle_starts_to_neighbors=*/false,
946 /*add_vehicle_ends_to_neighbors=*/false,
947 /*only_sort_neighbors_for_partial_neighborhoods=*/false})),
948 rnd_(*rnd),
949 walk_length_(walk_length),
950 node_dist_(0, model->Size() - model->vehicles()) {}
951
952std::function<int64_t(int64_t)> RandomWalkRemovalRuinProcedure::Ruin(
953 const Assignment* assignment) {
954 if (walk_length_ == 0) {
955 return [this, assignment](int64_t node) {
956 return assignment->Value(model_.NextVar(node));
957 };
958 }
959
960 int64_t curr_node =
961 PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
962 if (curr_node == -1) {
963 return [this, assignment](int64_t node) {
964 return assignment->Value(model_.NextVar(node));
965 };
966 }
967
968 routing_solution_.Reset(assignment);
969
970 int walk_length = walk_length_;
971
972 while (walk_length-- > 0) {
973 // Remove the active siblings node of curr before selecting next, so that
974 // we do not accidentally end up with next being one of these sibling
975 // nodes.
976 routing_solution_.RemovePerformedPickupDeliverySibling(curr_node);
977
978 const int64_t next_node = GetNextNodeToRemove(assignment, curr_node);
979
980 routing_solution_.RemoveNode(curr_node);
981
982 if (next_node == -1) {
983 // We were not able to find a vertex where to move next.
984 // We thus prematurely abort the ruin.
985 break;
986 }
987
988 curr_node = next_node;
989 }
990
991 return
992 [this](int64_t node) { return routing_solution_.GetNextNodeIndex(node); };
993}
994
995int64_t RandomWalkRemovalRuinProcedure::GetNextNodeToRemove(
996 const Assignment* assignment, int node) {
997 const int curr_vehicle = assignment->Value(model_.VehicleVar(node));
998 routing_solution_.InitializeRouteInfoIfNeeded(curr_vehicle);
999
1000 if (boolean_dist_(rnd_)) {
1001 const int64_t next_node =
1002 routing_solution_.GetRandomAdjacentVisit(node, rnd_, boolean_dist_);
1003 if (next_node != -1) {
1004 return next_node;
1005 }
1006 }
1007
1008 // Pick the next node by jumping to a neighboring (non empty) route,
1009 // otherwise.
1010 const RoutingCostClassIndex cost_class_index =
1011 model_.GetCostClassIndexOfVehicle(curr_vehicle);
1012
1013 const std::vector<int>& neighbors =
1014 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
1015 cost_class_index.value(), node);
1016
1017 int64_t same_route_closest_neighbor = -1;
1018
1019 for (int neighbor : neighbors) {
1020 const int neighbor_vehicle = assignment->Value(model_.VehicleVar(neighbor));
1021
1022 if (!routing_solution_.CanBeRemoved(neighbor)) {
1023 continue;
1024 }
1025
1026 if (neighbor_vehicle == curr_vehicle) {
1027 if (same_route_closest_neighbor == -1) {
1028 same_route_closest_neighbor = neighbor;
1029 }
1030 continue;
1031 }
1032
1033 return neighbor;
1034 }
1035
1036 // If we are not able to find a node in another route, we are ok
1037 // with taking a node from the current one.
1038 // Note that it can be -1 if no removable neighbor was found for the input
1039 // node.
1040 return same_route_closest_neighbor;
1041}
1042
1044 int max_removed_sequence_size,
1045 int avg_num_removed_visits,
1046 double bypass_factor, int num_neighbors)
1047 : model_(*model),
1048 rnd_(*rnd),
1049 max_removed_sequence_size_(max_removed_sequence_size),
1050 avg_num_removed_visits_(avg_num_removed_visits),
1051 bypass_factor_(bypass_factor),
1052 neighbors_manager_(model->GetOrCreateNodeNeighborsByCostClass(
1053 {num_neighbors,
1054 /*add_vehicle_starts_to_neighbors=*/false,
1055 /*add_vehicle_ends_to_neighbors=*/false,
1056 /*only_sort_neighbors_for_partial_neighborhoods=*/false})),
1057 node_dist_(0, model->Size() - model->vehicles()),
1058 probability_dist_(0.0, 1.0),
1059 ruined_routes_(model->vehicles()),
1060 routing_solution_(*model) {}
1061
1062std::function<int64_t(int64_t)> SISRRuinProcedure::Ruin(
1063 const Assignment* assignment) {
1064 const int64_t seed_node =
1065 PickRandomPerformedVisit(model_, *assignment, rnd_, node_dist_);
1066 if (seed_node == -1) {
1067 return [this, assignment](int64_t node) {
1068 return assignment->Value(model_.NextVar(node));
1069 };
1070 }
1071
1072 routing_solution_.Reset(assignment);
1073 ruined_routes_.ResetAllToFalse();
1074
1075 const double max_sequence_size =
1076 std::min<double>(max_removed_sequence_size_,
1077 ComputeAverageNonEmptyRouteSize(model_, *assignment));
1078
1079 const double max_num_removed_sequences =
1080 (4 * avg_num_removed_visits_) / (1 + max_sequence_size) - 1;
1081 DCHECK_GE(max_num_removed_sequences, 1);
1082
1083 const int num_sequences_to_remove =
1084 std::floor(std::uniform_real_distribution<double>(
1085 1.0, max_num_removed_sequences + 1)(rnd_));
1086
1087 // We start by disrupting the route where the seed visit is served.
1088 const int seed_route = RuinRoute(*assignment, seed_node, max_sequence_size);
1089 DCHECK_NE(seed_route, -1);
1090
1091 const RoutingCostClassIndex cost_class_index =
1092 model_.GetCostClassIndexOfVehicle(seed_route);
1093
1094 for (const int neighbor :
1095 neighbors_manager_->GetOutgoingNeighborsOfNodeForCostClass(
1096 cost_class_index.value(), seed_node)) {
1097 if (ruined_routes_.NumberOfSetCallsWithDifferentArguments() ==
1098 num_sequences_to_remove) {
1099 break;
1100 }
1101
1102 if (!routing_solution_.CanBeRemoved(neighbor)) {
1103 continue;
1104 }
1105
1106 RuinRoute(*assignment, neighbor, max_sequence_size);
1107 }
1108
1109 return
1110 [this](int64_t node) { return routing_solution_.GetNextNodeIndex(node); };
1111}
1112
1113int SISRRuinProcedure::RuinRoute(const Assignment& assignment,
1114 int64_t seed_visit,
1115 double global_max_sequence_size) {
1116 const int route = assignment.Value(model_.VehicleVar(seed_visit));
1117 DCHECK_GE(route, 0);
1118 if (ruined_routes_[route]) return -1;
1119
1120 routing_solution_.InitializeRouteInfoIfNeeded(route);
1121 ruined_routes_.Set(route);
1122
1123 const double max_sequence_size = std::min<double>(
1124 routing_solution_.GetRouteSize(route), global_max_sequence_size);
1125
1126 int sequence_size = std::floor(
1127 std::uniform_real_distribution<double>(1.0, max_sequence_size + 1)(rnd_));
1128
1129 if (sequence_size == 1 || sequence_size == max_sequence_size ||
1130 boolean_dist_(rnd_)) {
1131 RuinRouteWithSequenceProcedure(seed_visit, sequence_size);
1132 } else {
1133 RuinRouteWithSplitSequenceProcedure(route, seed_visit, sequence_size);
1134 }
1135
1136 return route;
1137}
1138
1139void SISRRuinProcedure::RuinRouteWithSequenceProcedure(int64_t seed_visit,
1140 int sequence_size) {
1141 const std::vector<int64_t> sequence =
1142 routing_solution_.GetRandomSequenceOfVisits(seed_visit, rnd_,
1143 boolean_dist_, sequence_size);
1144
1145 // Remove the selected visits.
1146 for (const int64_t visit : sequence) {
1147 routing_solution_.RemoveNode(visit);
1148 }
1149
1150 // Remove any still performed pickup or delivery siblings.
1151 for (const int64_t visit : sequence) {
1152 routing_solution_.RemovePerformedPickupDeliverySibling(visit);
1153 }
1154}
1155
1156void SISRRuinProcedure::RuinRouteWithSplitSequenceProcedure(int64_t route,
1157 int64_t seed_visit,
1158 int sequence_size) {
1159 const int max_num_bypassed_visits =
1160 routing_solution_.GetRouteSize(route) - sequence_size;
1161 int num_bypassed_visits = 1;
1162 while (num_bypassed_visits < max_num_bypassed_visits &&
1163 probability_dist_(rnd_) >= bypass_factor_ * probability_dist_(rnd_)) {
1164 ++num_bypassed_visits;
1165 }
1166
1167 const std::vector<int64_t> sequence =
1168 routing_solution_.GetRandomSequenceOfVisits(
1169 seed_visit, rnd_, boolean_dist_, sequence_size + num_bypassed_visits);
1170
1171 const int start_bypassed_visits = rnd_() % (sequence_size + 1);
1172 const int end_bypassed_visits = start_bypassed_visits + num_bypassed_visits;
1173
1174 // Remove the selected visits.
1175 for (int i = 0; i < start_bypassed_visits; ++i) {
1176 routing_solution_.RemoveNode(sequence[i]);
1177 }
1178 for (int i = end_bypassed_visits; i < sequence.size(); ++i) {
1179 routing_solution_.RemoveNode(sequence[i]);
1180 }
1181
1182 // Remove any still performed pickup or delivery siblings.
1183 for (int i = 0; i < start_bypassed_visits; ++i) {
1184 routing_solution_.RemovePerformedPickupDeliverySibling(sequence[i]);
1185 }
1186 for (int i = end_bypassed_visits; i < sequence.size(); ++i) {
1187 routing_solution_.RemovePerformedPickupDeliverySibling(sequence[i]);
1188 }
1189}
1190
1192 public:
1194 const Assignment* assignment, std::unique_ptr<RuinProcedure> ruin,
1195 std::unique_ptr<RoutingFilteredHeuristic> recreate)
1196 : assignment_(*assignment),
1197 ruin_(std::move(ruin)),
1198 recreate_(std::move(recreate)) {}
1199
1200 Decision* Next(Solver* const solver) override {
1201 Assignment* const new_assignment = Recreate(ruin_->Ruin(&assignment_));
1202 if (new_assignment) {
1203 new_assignment->Restore();
1204 } else {
1205 solver->Fail();
1206 }
1207 return nullptr;
1208 }
1209
1210 private:
1211 Assignment* Recreate(const std::function<int64_t(int64_t)>& next_accessor) {
1212 return recreate_->BuildSolutionFromRoutes(next_accessor);
1213 }
1214
1215 const Assignment& assignment_;
1216 std::unique_ptr<RuinProcedure> ruin_;
1217 std::unique_ptr<RoutingFilteredHeuristic> recreate_;
1218};
1219
1221 const RoutingSearchParameters& parameters, RoutingModel* model,
1222 std::mt19937* rnd, const Assignment* assignment,
1223 std::function<bool()> stop_search,
1224 LocalSearchFilterManager* filter_manager) {
1225 std::unique_ptr<RuinProcedure> ruin = MakeRuinProcedure(
1227 model, rnd);
1228
1229 std::unique_ptr<RoutingFilteredHeuristic> recreate = MakeRecreateProcedure(
1230 parameters, model, std::move(stop_search), filter_manager);
1231
1232 return model->solver()->RevAlloc(new RuinAndRecreateDecisionBuilder(
1233 assignment, std::move(ruin), std::move(recreate)));
1234}
1235
1237 const RoutingSearchParameters& parameters, RoutingModel* model,
1238 std::mt19937* rnd, const Assignment* assignment,
1239 std::function<bool()> stop_search,
1240 LocalSearchFilterManager* filter_manager) {
1241 switch (
1245 parameters, model, rnd, assignment, std::move(stop_search),
1246 filter_manager);
1247 default:
1248 LOG(DFATAL) << "Unsupported perturbation strategy.";
1249 return nullptr;
1250 }
1251}
1252
1253std::unique_ptr<NeighborAcceptanceCriterion> MakeNeighborAcceptanceCriterion(
1254 const RoutingModel& model, const AcceptanceStrategy& acceptance_strategy,
1255 const NeighborAcceptanceCriterion::SearchState& final_search_state,
1256 std::mt19937* rnd) {
1257 switch (acceptance_strategy.strategy_case()) {
1259 return std::make_unique<GreedyDescentAcceptanceCriterion>();
1261 return std::make_unique<SimulatedAnnealingAcceptanceCriterion>(
1262 MakeCoolingSchedule(model, acceptance_strategy.simulated_annealing(),
1263 final_search_state, rnd),
1264 rnd);
1266 return std::make_unique<AllNodesPerformedAcceptanceCriterion>(model);
1268 return std::make_unique<MoreNodesPerformedAcceptanceCriterion>(model);
1270 return std::make_unique<AbsencesBasedAcceptanceCriterion>(
1271 model, acceptance_strategy.absences_based()
1273 default:
1274 LOG(DFATAL) << "Unsupported acceptance strategy.";
1275 return nullptr;
1276 }
1277}
1278
1279std::pair<double, double> GetSimulatedAnnealingTemperatures(
1280 const RoutingModel& model,
1281 const SimulatedAnnealingAcceptanceStrategy& sa_params, std::mt19937* rnd) {
1282 if (!sa_params.automatic_temperatures()) {
1283 return {sa_params.initial_temperature(), sa_params.final_temperature()};
1284 }
1285
1286 // In the unlikely case there are no vehicles (i.e., we will end up with an
1287 // "all unperformed" solution), we simply return 0.0 as initial and final
1288 // temperatures.
1289 if (model.vehicles() == 0) {
1290 return {0.0, 0.0};
1291 }
1292
1293 std::vector<int> num_vehicles_of_class(model.GetCostClassesCount(), 0);
1294 for (int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
1295 const RoutingCostClassIndex cost_class =
1296 model.GetCostClassIndexOfVehicle(vehicle);
1297 ++num_vehicles_of_class[cost_class.value()];
1298 }
1299
1300 std::uniform_int_distribution<int64_t> node_dist(0, model.nodes() - 1);
1301
1302 const int sample_size = model.nodes();
1303 DCHECK_GT(sample_size, 0);
1304
1305 std::vector<double> mean_arc_cost_for_class(model.GetCostClassesCount(), 0.0);
1306 for (int cost_class = 0; cost_class < model.GetCostClassesCount();
1307 ++cost_class) {
1308 if (num_vehicles_of_class[cost_class] == 0) {
1309 continue;
1310 }
1311
1312 for (int i = 0; i < sample_size; ++i) {
1313 mean_arc_cost_for_class[cost_class] += model.GetArcCostForClass(
1314 node_dist(*rnd), node_dist(*rnd), cost_class);
1315 }
1316 mean_arc_cost_for_class[cost_class] /= sample_size;
1317 }
1318
1319 double reference_temperature = 0;
1320 DCHECK_GT(model.vehicles(), 0);
1321 for (int cost_class = 0; cost_class < model.GetCostClassesCount();
1322 ++cost_class) {
1323 reference_temperature += mean_arc_cost_for_class[cost_class] *
1324 num_vehicles_of_class[cost_class] /
1325 model.vehicles();
1326 }
1327
1328 return {reference_temperature * 0.1, reference_temperature * 0.001};
1329}
1330
1331} // namespace operations_research
const ::operations_research::AbsencesBasedAcceptanceStrategy & absences_based() const
const ::operations_research::SimulatedAnnealingAcceptanceStrategy & simulated_annealing() const
int64_t Value(const IntVar *var) const
IntVarElement * Add(IntVar *var)
CloseRoutesRemovalRuinProcedure(RoutingModel *model, std::mt19937 *rnd, size_t num_routes, int num_neighbors_for_route_selection)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
CompositionStrategy(std::vector< RuinProcedure * > ruin_procedures)
CompositeRuinProcedure(RoutingModel *model, std::vector< std::unique_ptr< RuinProcedure > > ruin_procedures, RuinCompositionStrategy::Value composition_strategy, std::mt19937 *rnd)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
const ::operations_research::RuinRecreateParameters & ruin_recreate_parameters() const
::operations_research::PerturbationStrategy_Value perturbation_strategy() const
RandomWalkRemovalRuinProcedure(RoutingModel *model, std::mt19937 *rnd, int walk_length, int num_neighbors_for_route_selection)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
int64_t Size() const
Returns the number of next variables in the model.
Definition routing.h:2104
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
Definition routing.h:1920
RoutingDisjunctionIndex DisjunctionIndex
Definition routing.h:271
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Definition routing.cc:4308
operations_research::IntVar * NextVar(int64_t index) const
Definition routing.h:1859
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition routing.h:1937
int vehicles() const
Returns the number of vehicle routes in the model.
Definition routing.h:2102
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
Definition routing.cc:4298
operations_research::IntVar * VehicleVar(int64_t index) const
Definition routing.h:1879
const ::operations_research::IteratedLocalSearchParameters & iterated_local_search_parameters() const
void RemovePerformedPickupDeliverySibling(int64_t node)
bool BelongsToInitializedRoute(int64_t node) const
void Reset(const Assignment *assignment)
int64_t GetRandomAdjacentVisit(int64_t visit, std::mt19937 &rnd, std::bernoulli_distribution &boolean_dist) const
void InitializeRouteInfoIfNeeded(int vehicle)
std::vector< int64_t > GetRandomSequenceOfVisits(int64_t seed_visit, std::mt19937 &rnd, std::bernoulli_distribution &boolean_dist, int size) const
RoutingSolution(const RoutingModel &model)
bool CanBeRemoved(int64_t node) const
int64_t GetInitializedPrevNodeIndex(int64_t node) const
int GetRouteSize(int vehicle) const
int64_t GetNextNodeIndex(int64_t node) const
Decision * Next(Solver *const solver) override
RuinAndRecreateDecisionBuilder(const Assignment *assignment, std::unique_ptr< RuinProcedure > ruin, std::unique_ptr< RoutingFilteredHeuristic > recreate)
SISRRuinProcedure(RoutingModel *model, std::mt19937 *rnd, int max_removed_sequence_size, int avg_num_removed_visits, double bypass_factor, int num_neighbors)
std::function< int64_t(int64_t)> Ruin(const Assignment *assignment) override
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
void Set(IntegerType index)
Definition bitset.h:878
OR-Tools root namespace.
DecisionBuilder * MakeRuinAndRecreateDecisionBuilder(const RoutingSearchParameters &parameters, RoutingModel *model, std::mt19937 *rnd, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
ClosedInterval::Iterator end(ClosedInterval interval)
std::pair< double, double > GetSimulatedAnnealingTemperatures(const RoutingModel &model, const SimulatedAnnealingAcceptanceStrategy &sa_params, std::mt19937 *rnd)
std::unique_ptr< NeighborAcceptanceCriterion > MakeNeighborAcceptanceCriterion(const RoutingModel &model, const AcceptanceStrategy &acceptance_strategy, const NeighborAcceptanceCriterion::SearchState &final_search_state, std::mt19937 *rnd)
DecisionBuilder * MakePerturbationDecisionBuilder(const RoutingSearchParameters &parameters, RoutingModel *model, std::mt19937 *rnd, const Assignment *assignment, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
STL namespace.
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...