OR-Tools  9.1
routing_search.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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
14// Implementation of all classes related to routing and search.
15// This includes decision builders, local search neighborhood operators
16// and local search filters.
17// TODO(user): Move all existing routing search code here.
18
20
21#include <algorithm>
22#include <cmath>
23#include <cstdint>
24#include <deque>
25#include <functional>
26#include <iterator>
27#include <limits>
28#include <memory>
29#include <numeric>
30#include <queue>
31#include <set>
32#include <string>
33#include <tuple>
34#include <utility>
35#include <vector>
36
37#include "absl/container/flat_hash_map.h"
38#include "absl/container/flat_hash_set.h"
39#include "absl/flags/flag.h"
40#include "absl/memory/memory.h"
41#include "absl/meta/type_traits.h"
42#include "absl/strings/str_cat.h"
43#include "absl/strings/string_view.h"
48#include "ortools/base/macros.h"
57#include "ortools/util/bitset.h"
60
61namespace operations_research {
62class LocalSearchPhaseParameters;
63} // namespace operations_research
64
65ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true,
66 "Shift insertion costs by the penalty of the inserted node(s).");
67
68ABSL_FLAG(int64_t, sweep_sectors, 1,
69 "The number of sectors the space is divided into before it is sweeped"
70 " by the ray.");
71
72namespace operations_research {
73
74// --- VehicleTypeCurator ---
75
76void VehicleTypeCurator::Reset(const std::function<bool(int)>& store_vehicle) {
77 const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
78 vehicle_type_container_->sorted_vehicle_classes_per_type;
79 sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
80 const std::vector<std::deque<int>>& all_vehicles_per_class =
81 vehicle_type_container_->vehicles_per_vehicle_class;
82 vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
83
84 for (int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
85 std::set<VehicleClassEntry>& stored_class_entries =
86 sorted_vehicle_classes_per_type_[type];
87 stored_class_entries.clear();
88 for (VehicleClassEntry class_entry : all_vehicle_classes_per_type[type]) {
89 const int vehicle_class = class_entry.vehicle_class;
90 std::vector<int>& stored_vehicles =
91 vehicles_per_vehicle_class_[vehicle_class];
92 stored_vehicles.clear();
93 for (int vehicle : all_vehicles_per_class[vehicle_class]) {
94 if (store_vehicle(vehicle)) {
95 stored_vehicles.push_back(vehicle);
96 }
97 }
98 if (!stored_vehicles.empty()) {
99 stored_class_entries.insert(class_entry);
100 }
101 }
102 }
103}
104
106 const std::function<bool(int)>& remove_vehicle) {
107 for (std::set<VehicleClassEntry>& class_entries :
108 sorted_vehicle_classes_per_type_) {
109 auto class_entry_it = class_entries.begin();
110 while (class_entry_it != class_entries.end()) {
111 const int vehicle_class = class_entry_it->vehicle_class;
112 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
113 vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
114 [&remove_vehicle](int vehicle) {
115 return remove_vehicle(vehicle);
116 }),
117 vehicles.end());
118 if (vehicles.empty()) {
119 class_entry_it = class_entries.erase(class_entry_it);
120 } else {
121 class_entry_it++;
122 }
123 }
124 }
125}
126
128 int type, std::function<bool(int)> vehicle_is_compatible,
129 std::function<bool(int)> stop_and_return_vehicle) {
130 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
131 sorted_vehicle_classes_per_type_[type];
132 auto vehicle_class_it = sorted_classes.begin();
133
134 while (vehicle_class_it != sorted_classes.end()) {
135 const int vehicle_class = vehicle_class_it->vehicle_class;
136 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
137 DCHECK(!vehicles.empty());
138
139 for (auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
140 vehicle_it++) {
141 const int vehicle = *vehicle_it;
142 if (vehicle_is_compatible(vehicle)) {
143 vehicles.erase(vehicle_it);
144 if (vehicles.empty()) {
145 sorted_classes.erase(vehicle_class_it);
146 }
147 return {vehicle, -1};
148 }
149 if (stop_and_return_vehicle(vehicle)) {
150 return {-1, vehicle};
151 }
152 }
153 // If no compatible vehicle was found in this class, move on to the next
154 // vehicle class.
155 vehicle_class_it++;
156 }
157 // No compatible vehicle of the given type was found and the stopping
158 // condition wasn't met.
159 return {-1, -1};
160}
161
162// - Models with pickup/deliveries or node precedences are best handled by
163// PARALLEL_CHEAPEST_INSERTION.
164// - As of January 2018, models with single nodes and at least one node with
165// only one allowed vehicle are better solved by PATH_MOST_CONSTRAINED_ARC.
166// - In all other cases, PATH_CHEAPEST_ARC is used.
167// TODO(user): Make this smarter.
169 bool has_pickup_deliveries, bool has_node_precedences,
170 bool has_single_vehicle_node) {
171 if (has_pickup_deliveries || has_node_precedences) {
173 }
174 if (has_single_vehicle_node) {
176 }
178}
179
180// --- First solution decision builder ---
181
182// IntVarFilteredDecisionBuilder
183
185 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
186 : heuristic_(std::move(heuristic)) {}
187
189 Assignment* const assignment = heuristic_->BuildSolution();
190 if (assignment != nullptr) {
191 VLOG(2) << "Number of decisions: " << heuristic_->number_of_decisions();
192 VLOG(2) << "Number of rejected decisions: "
193 << heuristic_->number_of_rejects();
194 assignment->Restore();
195 } else {
196 solver->Fail();
197 }
198 return nullptr;
199}
200
202 return heuristic_->number_of_decisions();
203}
204
206 return heuristic_->number_of_rejects();
207}
208
210 return absl::StrCat("IntVarFilteredDecisionBuilder(",
211 heuristic_->DebugString(), ")");
212}
213
214// --- First solution heuristics ---
215
216// IntVarFilteredHeuristic
217
219 Solver* solver, const std::vector<IntVar*>& vars,
220 LocalSearchFilterManager* filter_manager)
221 : assignment_(solver->MakeAssignment()),
222 solver_(solver),
223 vars_(vars),
224 delta_(solver->MakeAssignment()),
225 is_in_delta_(vars_.size(), false),
226 empty_(solver->MakeAssignment()),
227 filter_manager_(filter_manager),
228 number_of_decisions_(0),
229 number_of_rejects_(0) {
230 assignment_->MutableIntVarContainer()->Resize(vars_.size());
231 delta_indices_.reserve(vars_.size());
232}
233
235 number_of_decisions_ = 0;
236 number_of_rejects_ = 0;
237 // Wiping assignment when starting a new search.
239 assignment_->MutableIntVarContainer()->Resize(vars_.size());
240 delta_->MutableIntVarContainer()->Clear();
242}
243
246 if (!InitializeSolution()) {
247 return nullptr;
248 }
250 if (BuildSolutionInternal()) {
251 return assignment_;
252 }
253 return nullptr;
254}
255
257 const std::function<int64_t(int64_t)>& next_accessor) {
260 // NOTE: We don't need to clear or pre-set the two following vectors as the
261 // for loop below will set all elements.
262 start_chain_ends_.resize(model()->vehicles());
263 end_chain_starts_.resize(model()->vehicles());
264
265 for (int v = 0; v < model_->vehicles(); v++) {
266 int64_t node = model_->Start(v);
267 while (!model_->IsEnd(node)) {
268 const int64_t next = next_accessor(node);
269 DCHECK_NE(next, node);
270 SetValue(node, next);
271 SetVehicleIndex(node, v);
272 node = next;
273 }
274 // We relax all routes from start to end, so routes can now be extended
275 // by inserting nodes between the start and end.
276 start_chain_ends_[v] = model()->Start(v);
277 end_chain_starts_[v] = model()->End(v);
278 }
279 if (!Commit()) {
280 return nullptr;
281 }
283 if (BuildSolutionInternal()) {
284 return assignment_;
285 }
286 return nullptr;
287}
288
290 ++number_of_decisions_;
291 const bool accept = FilterAccept();
292 if (accept) {
293 const Assignment::IntContainer& delta_container = delta_->IntVarContainer();
294 const int delta_size = delta_container.Size();
295 Assignment::IntContainer* const container =
297 for (int i = 0; i < delta_size; ++i) {
298 const IntVarElement& delta_element = delta_container.Element(i);
299 IntVar* const var = delta_element.Var();
300 DCHECK_EQ(var, vars_[delta_indices_[i]]);
301 container->AddAtPosition(var, delta_indices_[i])
302 ->SetValue(delta_element.Value());
303 }
305 } else {
306 ++number_of_rejects_;
307 }
308 // Reset is_in_delta to all false.
309 for (const int delta_index : delta_indices_) {
310 is_in_delta_[delta_index] = false;
311 }
312 delta_->Clear();
313 delta_indices_.clear();
314 return accept;
315}
316
318 if (filter_manager_) filter_manager_->Synchronize(assignment_, delta_);
319}
320
321bool IntVarFilteredHeuristic::FilterAccept() {
322 if (!filter_manager_) return true;
323 LocalSearchMonitor* const monitor = solver_->GetLocalSearchMonitor();
324 return filter_manager_->Accept(monitor, delta_, empty_,
327}
328
329// RoutingFilteredHeuristic
330
333 : IntVarFilteredHeuristic(model->solver(), model->Nexts(), filter_manager),
334 model_(model) {}
335
336bool RoutingFilteredHeuristic::InitializeSolution() {
337 // Find the chains of nodes (when nodes have their "Next" value bound in the
338 // current solution, it forms a link in a chain). Eventually, starts[end]
339 // will contain the index of the first node of the chain ending at node 'end'
340 // and ends[start] will be the last node of the chain starting at node
341 // 'start'. Values of starts[node] and ends[node] for other nodes is used
342 // for intermediary computations and do not necessarily reflect actual chain
343 // starts and ends.
344
345 // Start by adding partial start chains to current assignment.
346 start_chain_ends_.clear();
347 start_chain_ends_.resize(model()->vehicles(), -1);
348 end_chain_starts_.clear();
349 end_chain_starts_.resize(model()->vehicles(), -1);
350
352 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
353 int64_t node = model()->Start(vehicle);
354 while (!model()->IsEnd(node) && Var(node)->Bound()) {
355 const int64_t next = Var(node)->Min();
356 SetValue(node, next);
357 SetVehicleIndex(node, vehicle);
358 node = next;
359 }
360 start_chain_ends_[vehicle] = node;
361 }
362
363 std::vector<int64_t> starts(Size() + model()->vehicles(), -1);
364 std::vector<int64_t> ends(Size() + model()->vehicles(), -1);
365 for (int node = 0; node < Size() + model()->vehicles(); ++node) {
366 // Each node starts as a singleton chain.
367 starts[node] = node;
368 ends[node] = node;
369 }
370 std::vector<bool> touched(Size(), false);
371 for (int node = 0; node < Size(); ++node) {
372 int current = node;
373 while (!model()->IsEnd(current) && !touched[current]) {
374 touched[current] = true;
375 IntVar* const next_var = Var(current);
376 if (next_var->Bound()) {
377 current = next_var->Value();
378 }
379 }
380 // Merge the sub-chain starting from 'node' and ending at 'current' with
381 // the existing sub-chain starting at 'current'.
382 starts[ends[current]] = starts[node];
383 ends[starts[node]] = ends[current];
384 }
385
386 // Set each route to be the concatenation of the chain at its starts and the
387 // chain at its end, without nodes in between.
388 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
389 end_chain_starts_[vehicle] = starts[model()->End(vehicle)];
390 int64_t node = start_chain_ends_[vehicle];
391 if (!model()->IsEnd(node)) {
392 int64_t next = starts[model()->End(vehicle)];
393 SetValue(node, next);
394 SetVehicleIndex(node, vehicle);
395 node = next;
396 while (!model()->IsEnd(node)) {
397 next = Var(node)->Min();
398 SetValue(node, next);
399 SetVehicleIndex(node, vehicle);
400 node = next;
401 }
402 }
403 }
404
405 if (!Commit()) {
407 return false;
408 }
409 return true;
410}
411
414 node, 1, [this, node](int alternate) {
415 if (node != alternate && !Contains(alternate)) {
416 SetValue(alternate, alternate);
417 }
418 });
419}
420
422 for (int index = 0; index < Size(); ++index) {
423 if (!Contains(index)) {
425 }
426 }
427}
428
430 std::vector<bool> to_make_unperformed(Size(), false);
431 for (const auto& [pickups, deliveries] :
432 model()->GetPickupAndDeliveryPairs()) {
433 int64_t performed_pickup = -1;
434 for (int64_t pickup : pickups) {
435 if (Contains(pickup) && Value(pickup) != pickup) {
436 performed_pickup = pickup;
437 break;
438 }
439 }
440 int64_t performed_delivery = -1;
441 for (int64_t delivery : deliveries) {
442 if (Contains(delivery) && Value(delivery) != delivery) {
443 performed_delivery = delivery;
444 break;
445 }
446 }
447 if ((performed_pickup == -1) != (performed_delivery == -1)) {
448 if (performed_pickup != -1) {
449 to_make_unperformed[performed_pickup] = true;
450 }
451 if (performed_delivery != -1) {
452 to_make_unperformed[performed_delivery] = true;
453 }
454 }
455 }
456 for (int index = 0; index < Size(); ++index) {
457 if (to_make_unperformed[index] || !Contains(index)) continue;
458 int64_t next = Value(index);
459 while (next < Size() && to_make_unperformed[next]) {
460 const int64_t next_of_next = Value(next);
461 SetValue(index, next_of_next);
463 next = next_of_next;
464 }
465 }
466}
467
468// CheapestInsertionFilteredHeuristic
469
472 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
473 std::function<int64_t(int64_t)> penalty_evaluator,
474 LocalSearchFilterManager* filter_manager)
475 : RoutingFilteredHeuristic(model, filter_manager),
476 evaluator_(std::move(evaluator)),
477 penalty_evaluator_(std::move(penalty_evaluator)) {}
478
479std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
481 const std::vector<int>& vehicles) {
482 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
483 model()->Size());
484
485 for (int node = 0; node < model()->Size(); node++) {
486 if (Contains(node)) continue;
487 std::vector<StartEndValue>& start_end_distances =
488 start_end_distances_per_node[node];
489
490 for (const int vehicle : vehicles) {
491 const int64_t start = model()->Start(vehicle);
492 const int64_t end = model()->End(vehicle);
493
494 // We compute the distance of node to the start/end nodes of the route.
495 const int64_t distance =
496 CapAdd(model()->GetArcCostForVehicle(start, node, vehicle),
497 model()->GetArcCostForVehicle(node, end, vehicle));
498 start_end_distances.push_back({distance, vehicle});
499 }
500 // Sort the distances for the node to all start/ends of available vehicles
501 // in decreasing order.
502 std::sort(start_end_distances.begin(), start_end_distances.end(),
503 [](const StartEndValue& first, const StartEndValue& second) {
504 return second < first;
505 });
506 }
507 return start_end_distances_per_node;
508}
509
510template <class Queue>
512 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
513 Queue* priority_queue) {
514 const int num_nodes = model()->Size();
515 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
516
517 for (int node = 0; node < num_nodes; node++) {
518 if (Contains(node)) continue;
519 std::vector<StartEndValue>& start_end_distances =
520 (*start_end_distances_per_node)[node];
521 if (start_end_distances.empty()) {
522 continue;
523 }
524 // Put the best StartEndValue for this node in the priority queue.
525 const StartEndValue& start_end_value = start_end_distances.back();
526 priority_queue->push(std::make_pair(start_end_value, node));
527 start_end_distances.pop_back();
528 }
529}
530
532 int64_t predecessor,
533 int64_t successor) {
534 SetValue(predecessor, node);
535 SetValue(node, successor);
537}
538
540 int64_t node_to_insert, int64_t start, int64_t next_after_start,
541 int64_t vehicle, std::vector<ValuedPosition>* valued_positions) {
542 CHECK(valued_positions != nullptr);
543 int64_t insert_after = start;
544 while (!model()->IsEnd(insert_after)) {
545 const int64_t insert_before =
546 (insert_after == start) ? next_after_start : Value(insert_after);
547 valued_positions->push_back(std::make_pair(
548 GetInsertionCostForNodeAtPosition(node_to_insert, insert_after,
549 insert_before, vehicle),
550 insert_after));
551 insert_after = insert_before;
552 }
553}
554
556 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
557 int vehicle) const {
558 return CapSub(CapAdd(evaluator_(insert_after, node_to_insert, vehicle),
559 evaluator_(node_to_insert, insert_before, vehicle)),
560 evaluator_(insert_after, insert_before, vehicle));
561}
562
564 int64_t node_to_insert) const {
565 if (penalty_evaluator_ != nullptr) {
566 return penalty_evaluator_(node_to_insert);
567 }
569}
570
571namespace {
572template <class T>
573void SortAndExtractPairSeconds(std::vector<std::pair<int64_t, T>>* pairs,
574 std::vector<T>* sorted_seconds) {
575 CHECK(pairs != nullptr);
576 CHECK(sorted_seconds != nullptr);
577 std::sort(pairs->begin(), pairs->end());
578 sorted_seconds->reserve(pairs->size());
579 for (const std::pair<int64_t, T>& p : *pairs) {
580 sorted_seconds->push_back(p.second);
581 }
582}
583} // namespace
584
585// Priority queue entries used by global cheapest insertion heuristic.
586
587// Entry in priority queue containing the insertion positions of a node pair.
589 public:
592 : heap_index_(-1),
593 value_(std::numeric_limits<int64_t>::max()),
594 pickup_to_insert_(pickup_to_insert),
595 pickup_insert_after_(pickup_insert_after),
596 delivery_to_insert_(delivery_to_insert),
597 delivery_insert_after_(delivery_insert_after),
598 vehicle_(vehicle) {}
599 // Note: for compatibility reasons, comparator follows tie-breaking rules used
600 // in the first version of GlobalCheapestInsertion.
601 bool operator<(const PairEntry& other) const {
602 // We first compare by value, then we favor insertions (vehicle != -1).
603 // The rest of the tie-breaking is done with std::tie.
604 if (value_ != other.value_) {
605 return value_ > other.value_;
606 }
607 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
608 return vehicle_ == -1;
609 }
610 return std::tie(pickup_insert_after_, pickup_to_insert_,
611 delivery_insert_after_, delivery_to_insert_, vehicle_) >
612 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
613 other.delivery_insert_after_, other.delivery_to_insert_,
614 other.vehicle_);
615 }
616 void SetHeapIndex(int h) { heap_index_ = h; }
617 int GetHeapIndex() const { return heap_index_; }
618 int64_t value() const { return value_; }
619 void set_value(int64_t value) { value_ = value; }
620 int pickup_to_insert() const { return pickup_to_insert_; }
621 int pickup_insert_after() const { return pickup_insert_after_; }
623 pickup_insert_after_ = pickup_insert_after;
624 }
625 int delivery_to_insert() const { return delivery_to_insert_; }
626 int delivery_insert_after() const { return delivery_insert_after_; }
627 int vehicle() const { return vehicle_; }
628 void set_vehicle(int vehicle) { vehicle_ = vehicle; }
629
630 private:
631 int heap_index_;
632 int64_t value_;
633 const int pickup_to_insert_;
634 int pickup_insert_after_;
635 const int delivery_to_insert_;
636 const int delivery_insert_after_;
637 int vehicle_;
638};
639
640// Entry in priority queue containing the insertion position of a node.
642 public:
644 : heap_index_(-1),
645 value_(std::numeric_limits<int64_t>::max()),
646 node_to_insert_(node_to_insert),
647 insert_after_(insert_after),
648 vehicle_(vehicle) {}
649 bool operator<(const NodeEntry& other) const {
650 // See PairEntry::operator<(), above. This one is similar.
651 if (value_ != other.value_) {
652 return value_ > other.value_;
653 }
654 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
655 return vehicle_ == -1;
656 }
657 return std::tie(insert_after_, node_to_insert_, vehicle_) >
658 std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
659 }
660 void SetHeapIndex(int h) { heap_index_ = h; }
661 int GetHeapIndex() const { return heap_index_; }
662 int64_t value() const { return value_; }
663 void set_value(int64_t value) { value_ = value; }
664 int node_to_insert() const { return node_to_insert_; }
665 int insert_after() const { return insert_after_; }
666 void set_insert_after(int insert_after) { insert_after_ = insert_after; }
667 int vehicle() const { return vehicle_; }
668 void set_vehicle(int vehicle) { vehicle_ = vehicle; }
669
670 private:
671 int heap_index_;
672 int64_t value_;
673 const int node_to_insert_;
674 int insert_after_;
675 int vehicle_;
676};
677
678// GlobalCheapestInsertionFilteredHeuristic
679
683 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
684 std::function<int64_t(int64_t)> penalty_evaluator,
685 LocalSearchFilterManager* filter_manager,
687 : CheapestInsertionFilteredHeuristic(model, std::move(evaluator),
688 std::move(penalty_evaluator),
689 filter_manager),
690 gci_params_(parameters),
691 node_index_to_vehicle_(model->Size(), -1),
692 empty_vehicle_type_curator_(nullptr) {
693 CHECK_GT(gci_params_.neighbors_ratio, 0);
694 CHECK_LE(gci_params_.neighbors_ratio, 1);
695 CHECK_GE(gci_params_.min_neighbors, 1);
696
697 if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
698 // All nodes are neighbors, so we set the neighbors_ratio to 1 to avoid
699 // unnecessary computations in the code.
700 gci_params_.neighbors_ratio = 1;
701 }
702
703 if (gci_params_.neighbors_ratio == 1) {
704 gci_params_.use_neighbors_ratio_for_initialization = false;
705 all_nodes_.resize(model->Size());
706 std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
707 }
708}
709
710void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
711 if (gci_params_.neighbors_ratio == 1 ||
712 !node_index_to_neighbors_by_cost_class_.empty()) {
713 // Neighborhood computations not needed or already done.
714 return;
715 }
716
717 // TODO(user): Refactor the neighborhood computations in RoutingModel.
718 const int64_t num_neighbors = NumNeighbors();
719 // If num_neighbors was greater or equal num_non_start_end_nodes - 1,
720 // gci_params_.neighbors_ratio should have been set to 1.
721 DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
722
723 const RoutingModel& routing_model = *model();
724 const int64_t size = routing_model.Size();
725 node_index_to_neighbors_by_cost_class_.resize(size);
726 const int num_cost_classes = routing_model.GetCostClassesCount();
727 for (int64_t node_index = 0; node_index < size; node_index++) {
728 node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
729 for (int cc = 0; cc < num_cost_classes; cc++) {
730 node_index_to_neighbors_by_cost_class_[node_index][cc] =
731 absl::make_unique<SparseBitset<int64_t>>(size);
732 }
733 }
734
735 for (int64_t node_index = 0; node_index < size; ++node_index) {
736 DCHECK(!routing_model.IsEnd(node_index));
737 if (routing_model.IsStart(node_index)) {
738 // We don't compute neighbors for vehicle starts: all nodes are considered
739 // neighbors for a vehicle start.
740 continue;
741 }
742
743 // TODO(user): Use the model's IndexNeighborFinder when available.
744 for (int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
745 if (!routing_model.HasVehicleWithCostClassIndex(
746 RoutingCostClassIndex(cost_class))) {
747 // No vehicle with this cost class, avoid unnecessary computations.
748 continue;
749 }
750 std::vector<std::pair</*cost*/ int64_t, /*node*/ int64_t>>
751 costed_after_nodes;
752 costed_after_nodes.reserve(size);
753 for (int after_node = 0; after_node < size; ++after_node) {
754 if (after_node != node_index && !routing_model.IsStart(after_node)) {
755 costed_after_nodes.push_back(
756 std::make_pair(routing_model.GetArcCostForClass(
757 node_index, after_node, cost_class),
758 after_node));
759 }
760 }
761 std::nth_element(costed_after_nodes.begin(),
762 costed_after_nodes.begin() + num_neighbors - 1,
763 costed_after_nodes.end());
764 costed_after_nodes.resize(num_neighbors);
765
766 for (auto [cost, neighbor] : costed_after_nodes) {
767 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
768 neighbor);
769
770 // Add reverse neighborhood.
771 DCHECK(!routing_model.IsEnd(neighbor) &&
772 !routing_model.IsStart(neighbor));
773 node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
774 node_index);
775 }
776 // Add all vehicle starts as neighbors to this node and vice-versa.
777 for (int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
778 const int64_t vehicle_start = routing_model.Start(vehicle);
779 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
780 vehicle_start);
781 node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
782 node_index);
783 }
784 }
785 }
786}
787
788bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
789 int cost_class, int64_t node_index, int64_t neighbor_index) const {
790 return gci_params_.neighbors_ratio == 1 ||
791 (*node_index_to_neighbors_by_cost_class_[node_index]
792 [cost_class])[neighbor_index];
793}
794
795bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices() const {
796 std::vector<bool> node_is_visited(model()->Size(), -1);
797 for (int v = 0; v < model()->vehicles(); v++) {
798 for (int node = model()->Start(v); !model()->IsEnd(node);
799 node = Value(node)) {
800 if (node_index_to_vehicle_[node] != v) {
801 return false;
802 }
803 node_is_visited[node] = true;
804 }
805 }
806
807 for (int node = 0; node < model()->Size(); node++) {
808 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
809 return false;
810 }
811 }
812
813 return true;
814}
815
817 ComputeNeighborhoods();
818 if (empty_vehicle_type_curator_ == nullptr) {
819 empty_vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
820 model()->GetVehicleTypeContainer());
821 }
822 // Store all empty vehicles in the empty_vehicle_type_curator_.
823 empty_vehicle_type_curator_->Reset(
824 [this](int vehicle) { return VehicleIsEmpty(vehicle); });
825 // Insert partially inserted pairs.
826 const RoutingModel::IndexPairs& pickup_delivery_pairs =
828 std::vector<int> pairs_to_insert;
829 absl::flat_hash_map<int, std::vector<int>> vehicle_to_pair_nodes;
830 for (int index = 0; index < pickup_delivery_pairs.size(); index++) {
831 const RoutingModel::IndexPair& index_pair = pickup_delivery_pairs[index];
832 int pickup_vehicle = -1;
833 for (int64_t pickup : index_pair.first) {
834 if (Contains(pickup)) {
835 pickup_vehicle = node_index_to_vehicle_[pickup];
836 break;
837 }
838 }
839 int delivery_vehicle = -1;
840 for (int64_t delivery : index_pair.second) {
841 if (Contains(delivery)) {
842 delivery_vehicle = node_index_to_vehicle_[delivery];
843 break;
844 }
845 }
846 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
847 pairs_to_insert.push_back(index);
848 }
849 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
850 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle];
851 for (int64_t delivery : index_pair.second) {
852 pair_nodes.push_back(delivery);
853 }
854 }
855 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
856 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle];
857 for (int64_t pickup : index_pair.first) {
858 pair_nodes.push_back(pickup);
859 }
860 }
861 }
862 for (const auto& vehicle_and_nodes : vehicle_to_pair_nodes) {
863 InsertNodesOnRoutes(vehicle_and_nodes.second, {vehicle_and_nodes.first});
864 }
865
866 InsertPairsAndNodesByRequirementTopologicalOrder();
867
868 // TODO(user): Adapt the pair insertions to also support seed and
869 // sequential insertion.
870 InsertPairs(pairs_to_insert);
871 std::vector<int> nodes;
872 for (int node = 0; node < model()->Size(); ++node) {
873 if (!Contains(node) && model()->GetPickupIndexPairs(node).empty() &&
874 model()->GetDeliveryIndexPairs(node).empty()) {
875 nodes.push_back(node);
876 }
877 }
878 InsertFarthestNodesAsSeeds();
879 if (gci_params_.is_sequential) {
880 SequentialInsertNodes(nodes);
881 } else {
882 InsertNodesOnRoutes(nodes, {});
883 }
885 DCHECK(CheckVehicleIndices());
886 return Commit();
887}
888
889void GlobalCheapestInsertionFilteredHeuristic::
890 InsertPairsAndNodesByRequirementTopologicalOrder() {
891 for (const std::vector<int>& types :
892 model()->GetTopologicallySortedVisitTypes()) {
893 for (int type : types) {
894 InsertPairs(model()->GetPairIndicesOfType(type));
895 InsertNodesOnRoutes(model()->GetSingleNodesOfType(type), {});
896 }
897 }
898}
899
900void GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
901 const std::vector<int>& pair_indices) {
903 std::vector<PairEntries> pickup_to_entries;
904 std::vector<PairEntries> delivery_to_entries;
905 InitializePairPositions(pair_indices, &priority_queue, &pickup_to_entries,
906 &delivery_to_entries);
907 while (!priority_queue.IsEmpty()) {
908 if (StopSearch()) {
909 for (PairEntry* const entry : *priority_queue.Raw()) {
910 delete entry;
911 }
912 return;
913 }
914 PairEntry* const entry = priority_queue.Top();
915 const int64_t pickup = entry->pickup_to_insert();
916 const int64_t delivery = entry->delivery_to_insert();
917 if (Contains(pickup) || Contains(delivery)) {
918 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
919 &delivery_to_entries);
920 continue;
921 }
922
923 const int entry_vehicle = entry->vehicle();
924 if (entry_vehicle == -1) {
925 // Pair is unperformed.
926 SetValue(pickup, pickup);
927 SetValue(delivery, delivery);
928 if (!Commit()) {
929 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
930 &delivery_to_entries);
931 }
932 continue;
933 }
934
935 // Pair is performed.
936 if (InsertPairEntryUsingEmptyVehicleTypeCurator(
937 pair_indices, entry, &priority_queue, &pickup_to_entries,
938 &delivery_to_entries)) {
939 // The entry corresponded to an insertion on an empty vehicle, which was
940 // handled by the method.
941 continue;
942 }
943
944 const int64_t pickup_insert_after = entry->pickup_insert_after();
945 const int64_t pickup_insert_before = Value(pickup_insert_after);
946 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
947
948 const int64_t delivery_insert_after = entry->delivery_insert_after();
949 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
950 ? pickup_insert_before
951 : Value(delivery_insert_after);
952 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
953 if (Commit()) {
954 UpdateAfterPairInsertion(pair_indices, entry_vehicle, pickup,
955 pickup_insert_after, delivery,
956 delivery_insert_after, &priority_queue,
957 &pickup_to_entries, &delivery_to_entries);
958 } else {
959 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
960 &delivery_to_entries);
961 }
962 }
963}
964
965bool GlobalCheapestInsertionFilteredHeuristic::
966 InsertPairEntryUsingEmptyVehicleTypeCurator(
967 const std::vector<int>& pair_indices,
968 GlobalCheapestInsertionFilteredHeuristic::PairEntry* const pair_entry,
970 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
971 priority_queue,
972 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
973 pickup_to_entries,
974 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
975 delivery_to_entries) {
976 const int entry_vehicle = pair_entry->vehicle();
977 if (entry_vehicle == -1 || !VehicleIsEmpty(entry_vehicle)) {
978 return false;
979 }
980
981 // Trying to insert on an empty vehicle.
982 // As we only have one pair_entry per empty vehicle type, we try inserting on
983 // all vehicles of this type with the same fixed cost, as they all have the
984 // same insertion value.
985 const int64_t pickup = pair_entry->pickup_to_insert();
986 const int64_t delivery = pair_entry->delivery_to_insert();
987 const int64_t entry_fixed_cost =
988 model()->GetFixedCostOfVehicle(entry_vehicle);
989 auto vehicle_is_compatible = [this, entry_fixed_cost, pickup,
990 delivery](int vehicle) {
991 if (model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
992 return false;
993 }
994 // NOTE: Only empty vehicles should be in the vehicle_curator_.
995 DCHECK(VehicleIsEmpty(vehicle));
996 const int64_t end = model()->End(vehicle);
997 InsertBetween(pickup, model()->Start(vehicle), end);
998 InsertBetween(delivery, pickup, end);
999 return Commit();
1000 };
1001 // Since the vehicles of the same type are sorted by increasing fixed
1002 // cost by the curator, we can stop as soon as a vehicle with a fixed cost
1003 // higher than the entry_fixed_cost is found which is empty, and adapt the
1004 // pair entry with this new vehicle.
1005 auto stop_and_return_vehicle = [this, entry_fixed_cost](int vehicle) {
1006 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1007 };
1008 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1009 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1010 empty_vehicle_type_curator_->Type(entry_vehicle),
1011 vehicle_is_compatible, stop_and_return_vehicle);
1012 if (compatible_vehicle >= 0) {
1013 // The pair was inserted on this vehicle.
1014 const int64_t vehicle_start = model()->Start(compatible_vehicle);
1015 const int num_previous_vehicle_entries =
1016 pickup_to_entries->at(vehicle_start).size() +
1017 delivery_to_entries->at(vehicle_start).size();
1018 UpdateAfterPairInsertion(pair_indices, compatible_vehicle, pickup,
1019 vehicle_start, delivery, pickup, priority_queue,
1020 pickup_to_entries, delivery_to_entries);
1021 if (compatible_vehicle != entry_vehicle) {
1022 // The pair was inserted on another empty vehicle of the same type
1023 // and same fixed cost as entry_vehicle.
1024 // Since this vehicle is empty and has the same fixed cost as the
1025 // entry_vehicle, it shouldn't be the representative of empty vehicles
1026 // for any pickup/delivery in the priority queue.
1027 DCHECK_EQ(num_previous_vehicle_entries, 0);
1028 return true;
1029 }
1030 // The previously unused entry_vehicle is now used, so we use the next
1031 // available vehicle of the same type to compute and store insertions on
1032 // empty vehicles.
1033 const int new_empty_vehicle =
1034 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1035 empty_vehicle_type_curator_->Type(compatible_vehicle));
1036
1037 if (new_empty_vehicle >= 0) {
1038 DCHECK(VehicleIsEmpty(new_empty_vehicle));
1039 // Add node entries after this vehicle start for uninserted pairs which
1040 // don't have entries on this empty vehicle.
1041 UpdatePairPositions(pair_indices, new_empty_vehicle,
1042 model()->Start(new_empty_vehicle), priority_queue,
1043 pickup_to_entries, delivery_to_entries);
1044 }
1045 } else if (next_fixed_cost_empty_vehicle >= 0) {
1046 // Could not insert on this vehicle or any other vehicle of the same type
1047 // with the same fixed cost, but found an empty vehicle of this type with
1048 // higher fixed cost.
1049 DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
1050 // Update the pair entry to correspond to an insertion on this
1051 // next_fixed_cost_empty_vehicle instead of the previous entry_vehicle.
1052 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1053 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1054 pair_entry->set_pickup_insert_after(
1055 model()->Start(next_fixed_cost_empty_vehicle));
1056 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1057 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1058 UpdatePairEntry(pair_entry, priority_queue);
1059 } else {
1060 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1061 delivery_to_entries);
1062 }
1063
1064 return true;
1065}
1066
1067void GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1068 const std::vector<int>& nodes, const absl::flat_hash_set<int>& vehicles) {
1070 std::vector<NodeEntries> position_to_node_entries;
1071 InitializePositions(nodes, vehicles, &priority_queue,
1072 &position_to_node_entries);
1073 // The following boolean indicates whether or not all vehicles are being
1074 // considered for insertion of the nodes simultaneously.
1075 // In the sequential version of the heuristic, as well as when inserting
1076 // single pickup or deliveries from pickup/delivery pairs, this will be false.
1077 // In the general parallel version of the heuristic, all_vehicles is true.
1078 const bool all_vehicles =
1079 vehicles.empty() || vehicles.size() == model()->vehicles();
1080
1081 while (!priority_queue.IsEmpty()) {
1082 NodeEntry* const node_entry = priority_queue.Top();
1083 if (StopSearch()) {
1084 for (NodeEntry* const entry : *priority_queue.Raw()) {
1085 delete entry;
1086 }
1087 return;
1088 }
1089 const int64_t node_to_insert = node_entry->node_to_insert();
1090 if (Contains(node_to_insert)) {
1091 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1092 continue;
1093 }
1094
1095 const int entry_vehicle = node_entry->vehicle();
1096 if (entry_vehicle == -1) {
1097 DCHECK(all_vehicles);
1098 // Make node unperformed.
1099 SetValue(node_to_insert, node_to_insert);
1100 if (!Commit()) {
1101 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1102 }
1103 continue;
1104 }
1105
1106 // Make node performed.
1107 if (InsertNodeEntryUsingEmptyVehicleTypeCurator(
1108 nodes, all_vehicles, node_entry, &priority_queue,
1109 &position_to_node_entries)) {
1110 DCHECK(all_vehicles);
1111 continue;
1112 }
1113
1114 const int64_t insert_after = node_entry->insert_after();
1115 InsertBetween(node_to_insert, insert_after, Value(insert_after));
1116 if (Commit()) {
1117 UpdatePositions(nodes, entry_vehicle, node_to_insert, all_vehicles,
1118 &priority_queue, &position_to_node_entries);
1119 UpdatePositions(nodes, entry_vehicle, insert_after, all_vehicles,
1120 &priority_queue, &position_to_node_entries);
1121 SetVehicleIndex(node_to_insert, entry_vehicle);
1122 } else {
1123 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1124 }
1125 }
1126}
1127
1128bool GlobalCheapestInsertionFilteredHeuristic::
1129 InsertNodeEntryUsingEmptyVehicleTypeCurator(
1130 const std::vector<int>& nodes, bool all_vehicles,
1131 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* const node_entry,
1133 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1134 priority_queue,
1135 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1136 position_to_node_entries) {
1137 const int entry_vehicle = node_entry->vehicle();
1138 if (entry_vehicle == -1 || !all_vehicles || !VehicleIsEmpty(entry_vehicle)) {
1139 return false;
1140 }
1141
1142 // Trying to insert on an empty vehicle, and all vehicles are being
1143 // considered simultaneously.
1144 // As we only have one node_entry per type, we try inserting on all vehicles
1145 // of this type with the same fixed cost as they all have the same insertion
1146 // value.
1147 const int64_t node_to_insert = node_entry->node_to_insert();
1148 const int64_t entry_fixed_cost =
1149 model()->GetFixedCostOfVehicle(entry_vehicle);
1150 auto vehicle_is_compatible = [this, entry_fixed_cost,
1151 node_to_insert](int vehicle) {
1152 if (model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1153 return false;
1154 }
1155 // NOTE: Only empty vehicles should be in the vehicle_curator_.
1156 DCHECK(VehicleIsEmpty(vehicle));
1157 InsertBetween(node_to_insert, model()->Start(vehicle),
1158 model()->End(vehicle));
1159 return Commit();
1160 };
1161 // Since the vehicles of the same type are sorted by increasing fixed
1162 // cost by the curator, we can stop as soon as an empty vehicle with a fixed
1163 // cost higher than the entry_fixed_cost is found, and add new entries for
1164 // this new vehicle.
1165 auto stop_and_return_vehicle = [this, entry_fixed_cost](int vehicle) {
1166 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1167 };
1168 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1169 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1170 empty_vehicle_type_curator_->Type(entry_vehicle),
1171 vehicle_is_compatible, stop_and_return_vehicle);
1172 if (compatible_vehicle >= 0) {
1173 // The node was inserted on this vehicle.
1174 UpdatePositions(nodes, compatible_vehicle, node_to_insert, all_vehicles,
1175 priority_queue, position_to_node_entries);
1176 const int64_t compatible_start = model()->Start(compatible_vehicle);
1177 const bool no_prior_entries_for_this_vehicle =
1178 position_to_node_entries->at(compatible_start).empty();
1179 UpdatePositions(nodes, compatible_vehicle, compatible_start, all_vehicles,
1180 priority_queue, position_to_node_entries);
1181 SetVehicleIndex(node_to_insert, compatible_vehicle);
1182 if (compatible_vehicle != entry_vehicle) {
1183 // The node was inserted on another empty vehicle of the same type
1184 // and same fixed cost as entry_vehicle.
1185 // Since this vehicle is empty and has the same fixed cost as the
1186 // entry_vehicle, it shouldn't be the representative of empty vehicles
1187 // for any node in the priority queue.
1188 DCHECK(no_prior_entries_for_this_vehicle);
1189 return true;
1190 }
1191 // The previously unused entry_vehicle is now used, so we use the next
1192 // available vehicle of the same type to compute and store insertions on
1193 // empty vehicles.
1194 const int new_empty_vehicle =
1195 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1196 empty_vehicle_type_curator_->Type(compatible_vehicle));
1197
1198 if (new_empty_vehicle >= 0) {
1199 DCHECK(VehicleIsEmpty(new_empty_vehicle));
1200 // Add node entries after this vehicle start for uninserted nodes which
1201 // don't have entries on this empty vehicle.
1202 UpdatePositions(nodes, new_empty_vehicle,
1203 model()->Start(new_empty_vehicle), all_vehicles,
1204 priority_queue, position_to_node_entries);
1205 }
1206 } else if (next_fixed_cost_empty_vehicle >= 0) {
1207 // Could not insert on this vehicle or any other vehicle of the same
1208 // type with the same fixed cost, but found an empty vehicle of this type
1209 // with higher fixed cost.
1210 DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
1211 // Update the insertion entry to be on next_empty_vehicle instead of the
1212 // previous entry_vehicle.
1213 position_to_node_entries->at(node_entry->insert_after()).erase(node_entry);
1214 node_entry->set_insert_after(model()->Start(next_fixed_cost_empty_vehicle));
1215 position_to_node_entries->at(node_entry->insert_after()).insert(node_entry);
1216 node_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1217 UpdateNodeEntry(node_entry, priority_queue);
1218 } else {
1219 DeleteNodeEntry(node_entry, priority_queue, position_to_node_entries);
1220 }
1221
1222 return true;
1223}
1224
1225void GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1226 const std::vector<int>& nodes) {
1227 std::vector<bool> is_vehicle_used;
1228 absl::flat_hash_set<int> used_vehicles;
1229 std::vector<int> unused_vehicles;
1230
1231 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1232 if (!used_vehicles.empty()) {
1233 InsertNodesOnRoutes(nodes, used_vehicles);
1234 }
1235
1236 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1237 ComputeStartEndDistanceForVehicles(unused_vehicles);
1238 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1239 first_node_queue;
1240 InitializePriorityQueue(&start_end_distances_per_node, &first_node_queue);
1241
1242 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1243 &is_vehicle_used);
1244
1245 while (vehicle >= 0) {
1246 InsertNodesOnRoutes(nodes, {vehicle});
1247 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1248 &is_vehicle_used);
1249 }
1250}
1251
1252void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1253 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1254 absl::flat_hash_set<int>* used_vehicles) {
1255 is_vehicle_used->clear();
1256 is_vehicle_used->resize(model()->vehicles());
1257
1258 used_vehicles->clear();
1259 used_vehicles->reserve(model()->vehicles());
1260
1261 unused_vehicles->clear();
1262 unused_vehicles->reserve(model()->vehicles());
1263
1264 for (int vehicle = 0; vehicle < model()->vehicles(); vehicle++) {
1265 if (!VehicleIsEmpty(vehicle)) {
1266 (*is_vehicle_used)[vehicle] = true;
1267 used_vehicles->insert(vehicle);
1268 } else {
1269 (*is_vehicle_used)[vehicle] = false;
1270 unused_vehicles->push_back(vehicle);
1271 }
1272 }
1273}
1274
1275void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1276 if (gci_params_.farthest_seeds_ratio <= 0) return;
1277 // Insert at least 1 farthest Seed if the parameter is positive.
1278 const int num_seeds = static_cast<int>(
1279 std::ceil(gci_params_.farthest_seeds_ratio * model()->vehicles()));
1280
1281 std::vector<bool> is_vehicle_used;
1282 absl::flat_hash_set<int> used_vehicles;
1283 std::vector<int> unused_vehicles;
1284 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1285 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1286 ComputeStartEndDistanceForVehicles(unused_vehicles);
1287
1288 // Priority queue where the Seeds with a larger distance are given higher
1289 // priority.
1290 std::priority_queue<Seed> farthest_node_queue;
1291 InitializePriorityQueue(&start_end_distances_per_node, &farthest_node_queue);
1292
1293 int inserted_seeds = 0;
1294 while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) {
1295 InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1296 &is_vehicle_used);
1297 inserted_seeds++;
1298 }
1299
1300 // NOTE: As we don't use the empty_vehicle_type_curator_ when inserting seed
1301 // nodes on routes, some previously empty vehicles may now be used, so we
1302 // update the curator accordingly to ensure it still only stores empty
1303 // vehicles.
1304 DCHECK(empty_vehicle_type_curator_ != nullptr);
1305 empty_vehicle_type_curator_->Update(
1306 [this](int vehicle) { return !VehicleIsEmpty(vehicle); });
1307}
1308
1309template <class Queue>
1310int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1311 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1312 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
1313 while (!priority_queue->empty()) {
1314 if (StopSearch()) break;
1315 const Seed& seed = priority_queue->top();
1316
1317 const int seed_node = seed.second;
1318 const int seed_vehicle = seed.first.vehicle;
1319
1320 std::vector<StartEndValue>& other_start_end_values =
1321 (*start_end_distances_per_node)[seed_node];
1322
1323 if (Contains(seed_node)) {
1324 // The node is already inserted, it is therefore no longer considered as
1325 // a potential seed.
1326 priority_queue->pop();
1327 other_start_end_values.clear();
1328 continue;
1329 }
1330 if (!(*is_vehicle_used)[seed_vehicle]) {
1331 // Try to insert this seed_node on this vehicle's route.
1332 const int64_t start = model()->Start(seed_vehicle);
1333 const int64_t end = model()->End(seed_vehicle);
1334 DCHECK_EQ(Value(start), end);
1335 InsertBetween(seed_node, start, end);
1336 if (Commit()) {
1337 priority_queue->pop();
1338 (*is_vehicle_used)[seed_vehicle] = true;
1339 other_start_end_values.clear();
1340 SetVehicleIndex(seed_node, seed_vehicle);
1341 return seed_vehicle;
1342 }
1343 }
1344 // Either the vehicle is already used, or the Commit() wasn't successful.
1345 // In both cases, we remove this Seed from the priority queue, and insert
1346 // the next StartEndValue from start_end_distances_per_node[seed_node]
1347 // in the priority queue.
1348 priority_queue->pop();
1349 if (!other_start_end_values.empty()) {
1350 const StartEndValue& next_seed_value = other_start_end_values.back();
1351 priority_queue->push(std::make_pair(next_seed_value, seed_node));
1352 other_start_end_values.pop_back();
1353 }
1354 }
1355 // No seed node was inserted.
1356 return -1;
1357}
1358
1359void GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1360 const std::vector<int>& pair_indices,
1362 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1363 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1364 pickup_to_entries,
1365 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1366 delivery_to_entries) {
1367 priority_queue->Clear();
1368 pickup_to_entries->clear();
1369 pickup_to_entries->resize(model()->Size());
1370 delivery_to_entries->clear();
1371 delivery_to_entries->resize(model()->Size());
1372 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1374 for (int index : pair_indices) {
1375 const RoutingModel::IndexPair& index_pair = pickup_delivery_pairs[index];
1376 for (int64_t pickup : index_pair.first) {
1377 if (Contains(pickup)) continue;
1378 for (int64_t delivery : index_pair.second) {
1379 if (Contains(delivery)) continue;
1380 // Add insertion entry making pair unperformed. When the pair is part
1381 // of a disjunction we do not try to make any of its pairs unperformed
1382 // as it requires having an entry with all pairs being unperformed.
1383 // TODO(user): Adapt the code to make pair disjunctions unperformed.
1384 if (gci_params_.add_unperformed_entries &&
1385 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1386 GetUnperformedValue(pickup) !=
1388 GetUnperformedValue(delivery) !=
1390 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue, nullptr,
1391 nullptr);
1392 }
1393 // Add all other insertion entries with pair performed.
1394 InitializeInsertionEntriesPerformingPair(
1395 pickup, delivery, priority_queue, pickup_to_entries,
1396 delivery_to_entries);
1397 }
1398 }
1399 }
1400}
1401
1402void GlobalCheapestInsertionFilteredHeuristic::
1403 InitializeInsertionEntriesPerformingPair(
1404 int64_t pickup, int64_t delivery,
1406 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1407 priority_queue,
1408 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1409 pickup_to_entries,
1410 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1411 delivery_to_entries) {
1412 if (!gci_params_.use_neighbors_ratio_for_initialization) {
1413 std::vector<std::pair<std::pair<int64_t, int>, std::pair<int64_t, int64_t>>>
1414 valued_positions;
1415 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
1416 if (VehicleIsEmpty(vehicle) &&
1417 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1418 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1419 // We only consider the least expensive empty vehicle of each type for
1420 // entries.
1421 continue;
1422 }
1423 const int64_t start = model()->Start(vehicle);
1424 std::vector<ValuedPosition> valued_pickup_positions;
1425 AppendEvaluatedPositionsAfter(pickup, start, Value(start), vehicle,
1426 &valued_pickup_positions);
1427 for (const ValuedPosition& valued_pickup_position :
1428 valued_pickup_positions) {
1429 const int64_t pickup_position = valued_pickup_position.second;
1430 CHECK(!model()->IsEnd(pickup_position));
1431 std::vector<ValuedPosition> valued_delivery_positions;
1432 AppendEvaluatedPositionsAfter(delivery, pickup, Value(pickup_position),
1433 vehicle, &valued_delivery_positions);
1434 for (const ValuedPosition& valued_delivery_position :
1435 valued_delivery_positions) {
1436 valued_positions.push_back(std::make_pair(
1437 std::make_pair(CapAdd(valued_pickup_position.first,
1438 valued_delivery_position.first),
1439 vehicle),
1440 std::make_pair(pickup_position,
1441 valued_delivery_position.second)));
1442 }
1443 }
1444 }
1445 for (const auto& [cost_for_vehicle, pair_positions] : valued_positions) {
1446 DCHECK_NE(pair_positions.first, pair_positions.second);
1447 AddPairEntry(pickup, pair_positions.first, delivery,
1448 pair_positions.second, cost_for_vehicle.second,
1449 priority_queue, pickup_to_entries, delivery_to_entries);
1450 }
1451 return;
1452 }
1453
1454 // We're only considering the closest neighbors as insertion positions for
1455 // the pickup/delivery pair.
1456 for (int cost_class = 0; cost_class < model()->GetCostClassesCount();
1457 cost_class++) {
1458 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1459 existing_insertion_positions;
1460 // Explore the neighborhood of the pickup.
1461 for (const int64_t pickup_insert_after :
1462 GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
1463 if (!Contains(pickup_insert_after)) {
1464 continue;
1465 }
1466 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1467 if (vehicle < 0 ||
1468 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1469 continue;
1470 }
1471
1472 if (VehicleIsEmpty(vehicle) &&
1473 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1474 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1475 // We only consider the least expensive empty vehicle of each type for
1476 // entries.
1477 continue;
1478 }
1479
1480 int64_t delivery_insert_after = pickup;
1481 while (!model()->IsEnd(delivery_insert_after)) {
1482 const std::pair<int64_t, int64_t> insertion_position = {
1483 pickup_insert_after, delivery_insert_after};
1484 DCHECK(!gtl::ContainsKey(existing_insertion_positions,
1485 insertion_position));
1486 existing_insertion_positions.insert(insertion_position);
1487
1488 AddPairEntry(pickup, pickup_insert_after, delivery,
1489 delivery_insert_after, vehicle, priority_queue,
1490 pickup_to_entries, delivery_to_entries);
1491 delivery_insert_after = (delivery_insert_after == pickup)
1492 ? Value(pickup_insert_after)
1493 : Value(delivery_insert_after);
1494 }
1495 }
1496
1497 // Explore the neighborhood of the delivery.
1498 for (const int64_t delivery_insert_after :
1499 GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
1500 if (!Contains(delivery_insert_after)) {
1501 continue;
1502 }
1503 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1504 if (vehicle < 0 ||
1505 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1506 continue;
1507 }
1508
1509 if (VehicleIsEmpty(vehicle)) {
1510 // Vehicle is empty.
1511 DCHECK_EQ(delivery_insert_after, model()->Start(vehicle));
1512 }
1513
1514 int64_t pickup_insert_after = model()->Start(vehicle);
1515 while (pickup_insert_after != delivery_insert_after) {
1516 if (!gtl::ContainsKey(
1517 existing_insertion_positions,
1518 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1519 AddPairEntry(pickup, pickup_insert_after, delivery,
1520 delivery_insert_after, vehicle, priority_queue,
1521 pickup_to_entries, delivery_to_entries);
1522 }
1523 pickup_insert_after = Value(pickup_insert_after);
1524 }
1525 }
1526 }
1527}
1528
1529void GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1530 const std::vector<int>& pair_indices, int vehicle, int64_t pickup,
1531 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1532 AdjustablePriorityQueue<PairEntry>* priority_queue,
1533 std::vector<PairEntries>* pickup_to_entries,
1534 std::vector<PairEntries>* delivery_to_entries) {
1535 UpdatePairPositions(pair_indices, vehicle, pickup_position, priority_queue,
1536 pickup_to_entries, delivery_to_entries);
1537 UpdatePairPositions(pair_indices, vehicle, pickup, priority_queue,
1538 pickup_to_entries, delivery_to_entries);
1539 UpdatePairPositions(pair_indices, vehicle, delivery, priority_queue,
1540 pickup_to_entries, delivery_to_entries);
1541 if (delivery_position != pickup) {
1542 UpdatePairPositions(pair_indices, vehicle, delivery_position,
1543 priority_queue, pickup_to_entries, delivery_to_entries);
1544 }
1545 SetVehicleIndex(pickup, vehicle);
1546 SetVehicleIndex(delivery, vehicle);
1547}
1548
1549void GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
1550 const std::vector<int>& pair_indices, int vehicle,
1551 int64_t pickup_insert_after,
1553 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1554 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1555 pickup_to_entries,
1556 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1557 delivery_to_entries) {
1558 // First, remove entries which have already been inserted and keep track of
1559 // the entries which are being kept and must be updated.
1560 using Pair = std::pair<int64_t, int64_t>;
1561 using Insertion = std::pair<Pair, /*delivery_insert_after*/ int64_t>;
1562 absl::flat_hash_set<Insertion> existing_insertions;
1563 std::vector<PairEntry*> to_remove;
1564 for (PairEntry* const pair_entry :
1565 pickup_to_entries->at(pickup_insert_after)) {
1566 DCHECK(priority_queue->Contains(pair_entry));
1567 DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
1568 if (Contains(pair_entry->pickup_to_insert()) ||
1569 Contains(pair_entry->delivery_to_insert())) {
1570 to_remove.push_back(pair_entry);
1571 } else {
1572 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1573 .contains(pair_entry));
1574 UpdatePairEntry(pair_entry, priority_queue);
1575 existing_insertions.insert(
1576 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1577 pair_entry->delivery_insert_after()});
1578 }
1579 }
1580 for (PairEntry* const pair_entry : to_remove) {
1581 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1582 delivery_to_entries);
1583 }
1584 // Create new entries for which the pickup is to be inserted after
1585 // pickup_insert_after.
1586 const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
1587 const int64_t pickup_insert_before = Value(pickup_insert_after);
1588 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1590 for (int pair_index : pair_indices) {
1591 const RoutingModel::IndexPair& index_pair =
1592 pickup_delivery_pairs[pair_index];
1593 for (int64_t pickup : index_pair.first) {
1594 if (Contains(pickup) ||
1595 !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
1596 continue;
1597 }
1598 for (int64_t delivery : index_pair.second) {
1599 if (Contains(delivery)) {
1600 continue;
1601 }
1602 int64_t delivery_insert_after = pickup;
1603 while (!model()->IsEnd(delivery_insert_after)) {
1604 const Insertion insertion = {{pickup, delivery},
1605 delivery_insert_after};
1606 if (!gtl::ContainsKey(existing_insertions, insertion)) {
1607 AddPairEntry(pickup, pickup_insert_after, delivery,
1608 delivery_insert_after, vehicle, priority_queue,
1609 pickup_to_entries, delivery_to_entries);
1610 }
1611 if (delivery_insert_after == pickup) {
1612 delivery_insert_after = pickup_insert_before;
1613 } else {
1614 delivery_insert_after = Value(delivery_insert_after);
1615 }
1616 }
1617 }
1618 }
1619 }
1620}
1621
1622void GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
1623 const std::vector<int>& pair_indices, int vehicle,
1624 int64_t delivery_insert_after,
1626 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1627 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1628 pickup_to_entries,
1629 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1630 delivery_to_entries) {
1631 // First, remove entries which have already been inserted and keep track of
1632 // the entries which are being kept and must be updated.
1633 using Pair = std::pair<int64_t, int64_t>;
1634 using Insertion = std::pair<Pair, /*pickup_insert_after*/ int64_t>;
1635 absl::flat_hash_set<Insertion> existing_insertions;
1636 std::vector<PairEntry*> to_remove;
1637 for (PairEntry* const pair_entry :
1638 delivery_to_entries->at(delivery_insert_after)) {
1639 DCHECK(priority_queue->Contains(pair_entry));
1640 DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
1641 if (Contains(pair_entry->pickup_to_insert()) ||
1642 Contains(pair_entry->delivery_to_insert())) {
1643 to_remove.push_back(pair_entry);
1644 } else {
1645 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1646 .contains(pair_entry));
1647 existing_insertions.insert(
1648 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1649 pair_entry->pickup_insert_after()});
1650 UpdatePairEntry(pair_entry, priority_queue);
1651 }
1652 }
1653 for (PairEntry* const pair_entry : to_remove) {
1654 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1655 delivery_to_entries);
1656 }
1657 // Create new entries for which the delivery is to be inserted after
1658 // delivery_insert_after.
1659 const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
1660 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1662 for (int pair_index : pair_indices) {
1663 const RoutingModel::IndexPair& index_pair =
1664 pickup_delivery_pairs[pair_index];
1665 for (int64_t delivery : index_pair.second) {
1666 if (Contains(delivery) ||
1667 !IsNeighborForCostClass(cost_class, delivery_insert_after,
1668 delivery)) {
1669 continue;
1670 }
1671 for (int64_t pickup : index_pair.first) {
1672 if (Contains(pickup)) {
1673 continue;
1674 }
1675 int64_t pickup_insert_after = model()->Start(vehicle);
1676 while (pickup_insert_after != delivery_insert_after) {
1677 const Insertion insertion = {{pickup, delivery}, pickup_insert_after};
1678 if (!gtl::ContainsKey(existing_insertions, insertion)) {
1679 AddPairEntry(pickup, pickup_insert_after, delivery,
1680 delivery_insert_after, vehicle, priority_queue,
1681 pickup_to_entries, delivery_to_entries);
1682 }
1683 pickup_insert_after = Value(pickup_insert_after);
1684 }
1685 }
1686 }
1687 }
1688}
1689
1690void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1691 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1693 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1694 std::vector<PairEntries>* pickup_to_entries,
1695 std::vector<PairEntries>* delivery_to_entries) {
1696 priority_queue->Remove(entry);
1697 if (entry->pickup_insert_after() != -1) {
1698 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1699 }
1700 if (entry->delivery_insert_after() != -1) {
1701 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1702 }
1703 delete entry;
1704}
1705
1706void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1707 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1708 int64_t delivery_insert_after, int vehicle,
1710 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1711 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1712 pickup_entries,
1713 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1714 delivery_entries) const {
1715 if (pickup_insert_after == -1) {
1716 DCHECK_EQ(delivery_insert_after, -1);
1717 DCHECK_EQ(vehicle, -1);
1718 PairEntry* pair_entry = new PairEntry(pickup, -1, delivery, -1, -1);
1719 pair_entry->set_value(
1720 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1721 ? 0
1722 : CapAdd(GetUnperformedValue(pickup),
1723 GetUnperformedValue(delivery)));
1724 priority_queue->Add(pair_entry);
1725 return;
1726 }
1727
1728 PairEntry* const pair_entry = new PairEntry(
1729 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle);
1730 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1731 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1732
1733 // Add entry to priority_queue and pickup_/delivery_entries.
1734 DCHECK(!priority_queue->Contains(pair_entry));
1735 pickup_entries->at(pickup_insert_after).insert(pair_entry);
1736 delivery_entries->at(delivery_insert_after).insert(pair_entry);
1737 priority_queue->Add(pair_entry);
1738}
1739
1740void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1741 GlobalCheapestInsertionFilteredHeuristic::PairEntry* const pair_entry,
1743 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1744 const {
1745 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1746 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1747 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1748 pair_entry->vehicle()));
1749
1750 // Update the priority_queue.
1751 DCHECK(priority_queue->Contains(pair_entry));
1752 priority_queue->NoteChangedPriority(pair_entry);
1753}
1754
1755int64_t
1756GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1757 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1758 int64_t delivery_insert_after, int vehicle) const {
1759 DCHECK_GE(pickup_insert_after, 0);
1760 const int64_t pickup_insert_before = Value(pickup_insert_after);
1761 const int64_t pickup_value = GetInsertionCostForNodeAtPosition(
1762 pickup, pickup_insert_after, pickup_insert_before, vehicle);
1763
1764 DCHECK_GE(delivery_insert_after, 0);
1765 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1766 ? pickup_insert_before
1767 : Value(delivery_insert_after);
1768 const int64_t delivery_value = GetInsertionCostForNodeAtPosition(
1769 delivery, delivery_insert_after, delivery_insert_before, vehicle);
1770
1771 const int64_t penalty_shift =
1772 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1773 ? CapAdd(GetUnperformedValue(pickup), GetUnperformedValue(delivery))
1774 : 0;
1775 return CapSub(CapAdd(pickup_value, delivery_value), penalty_shift);
1776}
1777
1778void GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1779 const std::vector<int>& nodes, const absl::flat_hash_set<int>& vehicles,
1781 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1782 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1783 position_to_node_entries) {
1784 priority_queue->Clear();
1785 position_to_node_entries->clear();
1786 position_to_node_entries->resize(model()->Size());
1787
1788 const int num_vehicles =
1789 vehicles.empty() ? model()->vehicles() : vehicles.size();
1790 const bool all_vehicles = (num_vehicles == model()->vehicles());
1791
1792 for (int node : nodes) {
1793 if (Contains(node)) {
1794 continue;
1795 }
1796 // Add insertion entry making node unperformed.
1797 if (gci_params_.add_unperformed_entries &&
1799 AddNodeEntry(node, -1, -1, all_vehicles, priority_queue, nullptr);
1800 }
1801 // Add all insertion entries making node performed.
1802 InitializeInsertionEntriesPerformingNode(node, vehicles, priority_queue,
1803 position_to_node_entries);
1804 }
1805}
1806
1807void GlobalCheapestInsertionFilteredHeuristic::
1808 InitializeInsertionEntriesPerformingNode(
1809 int64_t node, const absl::flat_hash_set<int>& vehicles,
1811 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1812 priority_queue,
1813 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1814 position_to_node_entries) {
1815 const int num_vehicles =
1816 vehicles.empty() ? model()->vehicles() : vehicles.size();
1817 const bool all_vehicles = (num_vehicles == model()->vehicles());
1818
1819 if (!gci_params_.use_neighbors_ratio_for_initialization) {
1820 auto vehicles_it = vehicles.begin();
1821 for (int v = 0; v < num_vehicles; v++) {
1822 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
1823
1824 const int64_t start = model()->Start(vehicle);
1825 if (all_vehicles && VehicleIsEmpty(vehicle) &&
1826 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1827 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1828 // We only consider the least expensive empty vehicle of each type for
1829 // entries.
1830 continue;
1831 }
1832 std::vector<ValuedPosition> valued_positions;
1833 AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle,
1834 &valued_positions);
1835 for (const std::pair<int64_t, int64_t>& valued_position :
1836 valued_positions) {
1837 AddNodeEntry(node, valued_position.second, vehicle, all_vehicles,
1838 priority_queue, position_to_node_entries);
1839 }
1840 }
1841 return;
1842 }
1843
1844 // We're only considering the closest neighbors as insertion positions for
1845 // the node.
1846 const auto insert_on_vehicle_for_cost_class = [this, &vehicles, all_vehicles](
1847 int v, int cost_class) {
1848 return (model()->GetCostClassIndexOfVehicle(v).value() == cost_class) &&
1849 (all_vehicles || vehicles.contains(v));
1850 };
1851 for (int cost_class = 0; cost_class < model()->GetCostClassesCount();
1852 cost_class++) {
1853 for (const int64_t insert_after :
1854 GetNeighborsOfNodeForCostClass(cost_class, node)) {
1855 if (!Contains(insert_after)) {
1856 continue;
1857 }
1858 const int vehicle = node_index_to_vehicle_[insert_after];
1859 if (vehicle == -1 ||
1860 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
1861 continue;
1862 }
1863 if (all_vehicles && VehicleIsEmpty(vehicle) &&
1864 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1865 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1866 // We only consider the least expensive empty vehicle of each type for
1867 // entries.
1868 continue;
1869 }
1870 AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
1871 position_to_node_entries);
1872 }
1873 }
1874}
1875
1876void GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
1877 const std::vector<int>& nodes, int vehicle, int64_t insert_after,
1878 bool all_vehicles,
1880 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1881 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1882 node_entries) {
1883 // Either create new entries if we are inserting after a newly inserted node
1884 // or remove entries which have already been inserted.
1885 std::vector<NodeEntry*> to_remove;
1886 absl::flat_hash_set<int> existing_insertions;
1887 for (NodeEntry* const node_entry : node_entries->at(insert_after)) {
1888 DCHECK_EQ(node_entry->insert_after(), insert_after);
1889 const int64_t node_to_insert = node_entry->node_to_insert();
1890 if (Contains(node_to_insert)) {
1891 to_remove.push_back(node_entry);
1892 } else {
1893 UpdateNodeEntry(node_entry, priority_queue);
1894 existing_insertions.insert(node_to_insert);
1895 }
1896 }
1897 for (NodeEntry* const node_entry : to_remove) {
1898 DeleteNodeEntry(node_entry, priority_queue, node_entries);
1899 }
1900 const int cost_class = model()->GetCostClassIndexOfVehicle(vehicle).value();
1901 for (int node_to_insert : nodes) {
1902 if (!Contains(node_to_insert) &&
1903 !existing_insertions.contains(node_to_insert) &&
1904 IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
1905 AddNodeEntry(node_to_insert, insert_after, vehicle, all_vehicles,
1906 priority_queue, node_entries);
1907 }
1908 }
1909}
1910
1911void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
1912 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
1914 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1915 std::vector<NodeEntries>* node_entries) {
1916 priority_queue->Remove(entry);
1917 if (entry->insert_after() != -1) {
1918 node_entries->at(entry->insert_after()).erase(entry);
1919 }
1920 delete entry;
1921}
1922
1923void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
1924 int64_t node, int64_t insert_after, int vehicle, bool all_vehicles,
1925 AdjustablePriorityQueue<NodeEntry>* priority_queue,
1926 std::vector<NodeEntries>* node_entries) const {
1927 const int64_t node_penalty = GetUnperformedValue(node);
1928 const int64_t penalty_shift =
1929 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1930 ? node_penalty
1931 : 0;
1932 if (insert_after == -1) {
1933 DCHECK_EQ(vehicle, -1);
1934 if (!all_vehicles) {
1935 // NOTE: In the case where we're not considering all routes
1936 // simultaneously, we don't add insertion entries making nodes
1937 // unperformed.
1938 return;
1939 }
1940 NodeEntry* const node_entry = new NodeEntry(node, -1, -1);
1941 node_entry->set_value(CapSub(node_penalty, penalty_shift));
1942 priority_queue->Add(node_entry);
1943 return;
1944 }
1945
1946 const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
1947 node, insert_after, Value(insert_after), vehicle);
1948 if (!all_vehicles && insertion_cost > node_penalty) {
1949 // NOTE: When all vehicles aren't considered for insertion, we don't
1950 // add entries making nodes unperformed, so we don't add insertions
1951 // which cost more than the node penalty either.
1952 return;
1953 }
1954
1955 NodeEntry* const node_entry = new NodeEntry(node, insert_after, vehicle);
1956 node_entry->set_value(CapSub(insertion_cost, penalty_shift));
1957 // Add entry to priority_queue and node_entries.
1958 DCHECK(!priority_queue->Contains(node_entry));
1959 node_entries->at(insert_after).insert(node_entry);
1960 priority_queue->Add(node_entry);
1961}
1962
1963void GlobalCheapestInsertionFilteredHeuristic::UpdateNodeEntry(
1964 NodeEntry* const node_entry,
1965 AdjustablePriorityQueue<NodeEntry>* priority_queue) const {
1966 const int64_t node = node_entry->node_to_insert();
1967 const int64_t insert_after = node_entry->insert_after();
1968 DCHECK_GE(insert_after, 0);
1969 const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
1970 node, insert_after, Value(insert_after), node_entry->vehicle());
1971 const int64_t penalty_shift =
1972 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1973 ? GetUnperformedValue(node)
1974 : 0;
1975
1976 node_entry->set_value(CapSub(insertion_cost, penalty_shift));
1977 // Update the priority_queue.
1978 DCHECK(priority_queue->Contains(node_entry));
1979 priority_queue->NoteChangedPriority(node_entry);
1980}
1981
1982// LocalCheapestInsertionFilteredHeuristic
1983// TODO(user): Add support for penalty costs.
1987 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
1988 LocalSearchFilterManager* filter_manager)
1989 : CheapestInsertionFilteredHeuristic(model, std::move(evaluator), nullptr,
1990 filter_manager) {
1991 std::vector<int> all_vehicles(model->vehicles());
1992 std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
1993
1994 start_end_distances_per_node_ =
1996}
1997
1999 // Marking if we've tried inserting a node.
2000 std::vector<bool> visited(model()->Size(), false);
2001 // Possible positions where the current node can inserted.
2002 std::vector<int64_t> insertion_positions;
2003 // Possible positions where its associated delivery node can inserted (if the
2004 // current node has one).
2005 std::vector<int64_t> delivery_insertion_positions;
2006 // Iterating on pickup and delivery pairs
2007 const RoutingModel::IndexPairs& index_pairs =
2009 for (const auto& index_pair : index_pairs) {
2010 for (int64_t pickup : index_pair.first) {
2011 if (Contains(pickup)) {
2012 continue;
2013 }
2014 for (int64_t delivery : index_pair.second) {
2015 // If either is already in the solution, let it be inserted in the
2016 // standard node insertion loop.
2017 if (Contains(delivery)) {
2018 continue;
2019 }
2020 if (StopSearch()) return false;
2021 visited[pickup] = true;
2022 visited[delivery] = true;
2023 ComputeEvaluatorSortedPositions(pickup, &insertion_positions);
2024 for (const int64_t pickup_insertion : insertion_positions) {
2025 const int pickup_insertion_next = Value(pickup_insertion);
2026 ComputeEvaluatorSortedPositionsOnRouteAfter(
2027 delivery, pickup, pickup_insertion_next,
2028 &delivery_insertion_positions);
2029 bool found = false;
2030 for (const int64_t delivery_insertion :
2031 delivery_insertion_positions) {
2032 InsertBetween(pickup, pickup_insertion, pickup_insertion_next);
2033 const int64_t delivery_insertion_next =
2034 (delivery_insertion == pickup_insertion) ? pickup
2035 : (delivery_insertion == pickup) ? pickup_insertion_next
2036 : Value(delivery_insertion);
2037 InsertBetween(delivery, delivery_insertion,
2038 delivery_insertion_next);
2039 if (Commit()) {
2040 found = true;
2041 break;
2042 }
2043 }
2044 if (found) {
2045 break;
2046 }
2047 }
2048 }
2049 }
2050 }
2051
2052 std::priority_queue<Seed> node_queue;
2053 InitializePriorityQueue(&start_end_distances_per_node_, &node_queue);
2054
2055 while (!node_queue.empty()) {
2056 const int node = node_queue.top().second;
2057 node_queue.pop();
2058 if (Contains(node) || visited[node]) continue;
2059 ComputeEvaluatorSortedPositions(node, &insertion_positions);
2060 for (const int64_t insertion : insertion_positions) {
2061 if (StopSearch()) return false;
2062 InsertBetween(node, insertion, Value(insertion));
2063 if (Commit()) {
2064 break;
2065 }
2066 }
2067 }
2069 return Commit();
2070}
2071
2072void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2073 int64_t node, std::vector<int64_t>* sorted_positions) {
2074 CHECK(sorted_positions != nullptr);
2075 CHECK(!Contains(node));
2076 sorted_positions->clear();
2077 const int size = model()->Size();
2078 if (node < size) {
2079 std::vector<std::pair<int64_t, int64_t>> valued_positions;
2080 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
2081 const int64_t start = model()->Start(vehicle);
2082 AppendEvaluatedPositionsAfter(node, start, Value(start), vehicle,
2083 &valued_positions);
2084 }
2085 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
2086 }
2087}
2088
2089void LocalCheapestInsertionFilteredHeuristic::
2090 ComputeEvaluatorSortedPositionsOnRouteAfter(
2091 int64_t node, int64_t start, int64_t next_after_start,
2092 std::vector<int64_t>* sorted_positions) {
2093 CHECK(sorted_positions != nullptr);
2094 CHECK(!Contains(node));
2095 sorted_positions->clear();
2096 const int size = model()->Size();
2097 if (node < size) {
2098 // TODO(user): Take vehicle into account.
2099 std::vector<std::pair<int64_t, int64_t>> valued_positions;
2100 AppendEvaluatedPositionsAfter(node, start, next_after_start, 0,
2101 &valued_positions);
2102 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
2103 }
2104}
2105
2106// CheapestAdditionFilteredHeuristic
2107
2110 : RoutingFilteredHeuristic(model, filter_manager) {}
2111
2113 const int kUnassigned = -1;
2115 std::vector<std::vector<int64_t>> deliveries(Size());
2116 std::vector<std::vector<int64_t>> pickups(Size());
2117 for (const RoutingModel::IndexPair& pair : pairs) {
2118 for (int first : pair.first) {
2119 for (int second : pair.second) {
2120 deliveries[first].push_back(second);
2121 pickups[second].push_back(first);
2122 }
2123 }
2124 }
2125 // To mimic the behavior of PathSelector (cf. search.cc), iterating on
2126 // routes with partial route at their start first then on routes with largest
2127 // index.
2128 std::vector<int> sorted_vehicles(model()->vehicles(), 0);
2129 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
2130 sorted_vehicles[vehicle] = vehicle;
2131 }
2132 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
2133 PartialRoutesAndLargeVehicleIndicesFirst(*this));
2134 // Neighbors of the node currently being extended.
2135 for (const int vehicle : sorted_vehicles) {
2136 int64_t last_node = GetStartChainEnd(vehicle);
2137 bool extend_route = true;
2138 // Extend the route of the current vehicle while it's possible. We can
2139 // iterate more than once if pickup and delivery pairs have been inserted
2140 // in the last iteration (see comment below); the new iteration will try to
2141 // extend the route after the last delivery on the route.
2142 while (extend_route) {
2143 extend_route = false;
2144 bool found = true;
2145 int64_t index = last_node;
2146 int64_t end = GetEndChainStart(vehicle);
2147 // Extend the route until either the end node of the vehicle is reached
2148 // or no node or node pair can be added. Deliveries in pickup and
2149 // delivery pairs are added at the same time as pickups, at the end of the
2150 // route, in reverse order of the pickups. Deliveries are never added
2151 // alone.
2152 while (found && !model()->IsEnd(index)) {
2153 found = false;
2154 std::vector<int64_t> neighbors;
2155 if (index < model()->Nexts().size()) {
2156 std::unique_ptr<IntVarIterator> it(
2157 model()->Nexts()[index]->MakeDomainIterator(false));
2158 auto next_values = InitAndGetValues(it.get());
2159 neighbors = GetPossibleNextsFromIterator(index, next_values.begin(),
2160 next_values.end());
2161 }
2162 for (int i = 0; !found && i < neighbors.size(); ++i) {
2163 int64_t next = -1;
2164 switch (i) {
2165 case 0:
2166 next = FindTopSuccessor(index, neighbors);
2167 break;
2168 case 1:
2169 SortSuccessors(index, &neighbors);
2170 ABSL_FALLTHROUGH_INTENDED;
2171 default:
2172 next = neighbors[i];
2173 }
2174 if (model()->IsEnd(next) && next != end) {
2175 continue;
2176 }
2177 // Only add a delivery if one of its pickups has been added already.
2178 if (!model()->IsEnd(next) && !pickups[next].empty()) {
2179 bool contains_pickups = false;
2180 for (int64_t pickup : pickups[next]) {
2181 if (Contains(pickup)) {
2182 contains_pickups = true;
2183 break;
2184 }
2185 }
2186 if (!contains_pickups) {
2187 continue;
2188 }
2189 }
2190 std::vector<int64_t> next_deliveries;
2191 if (next < deliveries.size()) {
2192 next_deliveries = GetPossibleNextsFromIterator(
2193 next, deliveries[next].begin(), deliveries[next].end());
2194 }
2195 if (next_deliveries.empty()) next_deliveries = {kUnassigned};
2196 for (int j = 0; !found && j < next_deliveries.size(); ++j) {
2197 if (StopSearch()) return false;
2198 int delivery = -1;
2199 switch (j) {
2200 case 0:
2201 delivery = FindTopSuccessor(next, next_deliveries);
2202 break;
2203 case 1:
2204 SortSuccessors(next, &next_deliveries);
2205 ABSL_FALLTHROUGH_INTENDED;
2206 default:
2207 delivery = next_deliveries[j];
2208 }
2209 // Insert "next" after "index", and before "end" if it is not the
2210 // end already.
2212 if (!model()->IsEnd(next)) {
2213 SetValue(next, end);
2215 if (delivery != kUnassigned) {
2216 SetValue(next, delivery);
2217 SetValue(delivery, end);
2219 }
2220 }
2221 if (Commit()) {
2222 index = next;
2223 found = true;
2224 if (delivery != kUnassigned) {
2225 if (model()->IsEnd(end) && last_node != delivery) {
2226 last_node = delivery;
2227 extend_route = true;
2228 }
2229 end = delivery;
2230 }
2231 break;
2232 }
2233 }
2234 }
2235 }
2236 }
2237 }
2239 return Commit();
2240}
2241
2242bool CheapestAdditionFilteredHeuristic::
2243 PartialRoutesAndLargeVehicleIndicesFirst::operator()(int vehicle1,
2244 int vehicle2) const {
2245 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2246 builder_.GetStartChainEnd(vehicle1));
2247 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
2248 builder_.GetStartChainEnd(vehicle2));
2249 if (has_partial_route1 == has_partial_route2) {
2250 return vehicle2 < vehicle1;
2251 } else {
2252 return has_partial_route2 < has_partial_route1;
2253 }
2254}
2255
2256// EvaluatorCheapestAdditionFilteredHeuristic
2257
2260 RoutingModel* model, std::function<int64_t(int64_t, int64_t)> evaluator,
2261 LocalSearchFilterManager* filter_manager)
2262 : CheapestAdditionFilteredHeuristic(model, filter_manager),
2263 evaluator_(std::move(evaluator)) {}
2264
2265int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2266 int64_t node, const std::vector<int64_t>& successors) {
2267 int64_t best_evaluation = std::numeric_limits<int64_t>::max();
2268 int64_t best_successor = -1;
2269 for (int64_t successor : successors) {
2270 const int64_t evaluation = (successor >= 0)
2271 ? evaluator_(node, successor)
2273 if (evaluation < best_evaluation ||
2274 (evaluation == best_evaluation && successor > best_successor)) {
2275 best_evaluation = evaluation;
2276 best_successor = successor;
2277 }
2278 }
2279 return best_successor;
2280}
2281
2282void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2283 int64_t node, std::vector<int64_t>* successors) {
2284 std::vector<std::pair<int64_t, int64_t>> values;
2285 values.reserve(successors->size());
2286 for (int64_t successor : *successors) {
2287 // Tie-breaking on largest node index to mimic the behavior of
2288 // CheapestValueSelector (search.cc).
2289 values.push_back({evaluator_(node, successor), -successor});
2290 }
2291 std::sort(values.begin(), values.end());
2292 successors->clear();
2293 for (auto value : values) {
2294 successors->push_back(-value.second);
2295 }
2296}
2297
2298// ComparatorCheapestAdditionFilteredHeuristic
2299
2303 LocalSearchFilterManager* filter_manager)
2304 : CheapestAdditionFilteredHeuristic(model, filter_manager),
2305 comparator_(std::move(comparator)) {}
2306
2307int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2308 int64_t node, const std::vector<int64_t>& successors) {
2309 return *std::min_element(successors.begin(), successors.end(),
2310 [this, node](int successor1, int successor2) {
2311 return comparator_(node, successor1, successor2);
2312 });
2313}
2314
2315void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2316 int64_t node, std::vector<int64_t>* successors) {
2317 std::sort(successors->begin(), successors->end(),
2318 [this, node](int successor1, int successor2) {
2319 return comparator_(node, successor1, successor2);
2320 });
2321}
2322
2323// Class storing and allowing access to the savings according to the number of
2324// vehicle types.
2325// The savings are stored and sorted in sorted_savings_per_vehicle_type_.
2326// Furthermore, when there is more than one vehicle type, the savings for a same
2327// before-->after arc are sorted in costs_and_savings_per_arc_[arc] by
2328// increasing cost(s-->before-->after-->e), where s and e are the start and end
2329// of the route, in order to make sure the arc is served by the route with the
2330// closest depot (start/end) possible.
2331// When there is only one vehicle "type" (i.e. all vehicles have the same
2332// start/end and cost class), each arc has a single saving value associated to
2333// it, so we ignore this last step to avoid unnecessary computations, and only
2334// work with sorted_savings_per_vehicle_type_[0].
2335// In case of multiple vehicle types, the best savings for each arc, i.e. the
2336// savings corresponding to the closest vehicle type, are inserted and sorted in
2337// sorted_savings_.
2338//
2339// This class also handles skipped Savings:
2340// The vectors skipped_savings_starting/ending_at_ contain all the Savings that
2341// weren't added to the model, which we want to consider for later:
2342// 1) When a Saving before-->after with both nodes uncontained cannot be used to
2343// start a new route (no more available vehicles or could not commit on any
2344// of those available).
2345// 2) When only one of the nodes of the Saving is contained but on a different
2346// vehicle type.
2347// In these cases, the Update() method is called with update_best_saving = true,
2348// which in turn calls SkipSavingForArc() (within
2349// UpdateNextAndSkippedSavingsForArcWithType()) to mark the Saving for this arc
2350// (with the correct type in the second case) as "skipped", by storing it in
2351// skipped_savings_starting_at_[before] and skipped_savings_ending_at_[after].
2352//
2353// UpdateNextAndSkippedSavingsForArcWithType() also updates the next_savings_
2354// vector, which stores the savings to go through once we've iterated through
2355// all sorted_savings_.
2356// In the first case above, where neither nodes are contained, we skip the
2357// current Saving (current_saving_), and add the next best Saving for this arc
2358// to next_savings_ (in case this skipped Saving is never considered).
2359// In the second case with a specific type, we search for the Saving with the
2360// correct type for this arc, and add it to both next_savings_ and the skipped
2361// Savings.
2362//
2363// The skipped Savings are then re-considered when one of their ends gets
2364// inserted:
2365// When another Saving other_node-->before (or after-->other_node) gets
2366// inserted, all skipped Savings in skipped_savings_starting_at_[before] (or
2367// skipped_savings_ending_at_[after]) are once again considered by calling
2368// ReinjectSkippedSavingsStartingAt() (or ReinjectSkippedSavingsEndingAt()).
2369// Then, when calling GetSaving(), we iterate through the reinjected Savings in
2370// order of insertion in the vectors while there are reinjected savings.
2371template <typename Saving>
2373 public:
2374 explicit SavingsContainer(const SavingsFilteredHeuristic* savings_db,
2375 int vehicle_types)
2376 : savings_db_(savings_db),
2377 vehicle_types_(vehicle_types),
2378 index_in_sorted_savings_(0),
2379 single_vehicle_type_(vehicle_types == 1),
2380 using_incoming_reinjected_saving_(false),
2381 sorted_(false),
2382 to_update_(true) {}
2383
2384 void InitializeContainer(int64_t size, int64_t saving_neighbors) {
2385 sorted_savings_per_vehicle_type_.clear();
2386 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
2387 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2388 savings.reserve(size * saving_neighbors);
2389 }
2390
2391 sorted_savings_.clear();
2392 costs_and_savings_per_arc_.clear();
2393 arc_indices_per_before_node_.clear();
2394
2395 if (!single_vehicle_type_) {
2396 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
2397 arc_indices_per_before_node_.resize(size);
2398 for (int before_node = 0; before_node < size; before_node++) {
2399 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
2400 }
2401 }
2402 skipped_savings_starting_at_.clear();
2403 skipped_savings_starting_at_.resize(size);
2404 skipped_savings_ending_at_.clear();
2405 skipped_savings_ending_at_.resize(size);
2406 incoming_reinjected_savings_ = nullptr;
2407 outgoing_reinjected_savings_ = nullptr;
2408 incoming_new_reinjected_savings_ = nullptr;
2409 outgoing_new_reinjected_savings_ = nullptr;
2410 }
2411
2412 void AddNewSaving(const Saving& saving, int64_t total_cost,
2413 int64_t before_node, int64_t after_node, int vehicle_type) {
2414 CHECK(!sorted_savings_per_vehicle_type_.empty())
2415 << "Container not initialized!";
2416 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
2417 UpdateArcIndicesCostsAndSavings(before_node, after_node,
2418 {total_cost, saving});
2419 }
2420
2421 void Sort() {
2422 CHECK(!sorted_) << "Container already sorted!";
2423
2424 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2425 std::sort(savings.begin(), savings.end());
2426 }
2427
2428 if (single_vehicle_type_) {
2429 const auto& savings = sorted_savings_per_vehicle_type_[0];
2430 sorted_savings_.resize(savings.size());
2431 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
2432 [](const Saving& saving) {
2433 return SavingAndArc({saving, /*arc_index*/ -1});
2434 });
2435 } else {
2436 // For each arc, sort the savings by decreasing total cost
2437 // start-->a-->b-->end.
2438 // The best saving for each arc is therefore the last of its vector.
2439 sorted_savings_.reserve(vehicle_types_ *
2440 costs_and_savings_per_arc_.size());
2441
2442 for (int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
2443 arc_index++) {
2444 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2445 costs_and_savings_per_arc_[arc_index];
2446 DCHECK(!costs_and_savings.empty());
2447
2448 std::sort(
2449 costs_and_savings.begin(), costs_and_savings.end(),
2450 [](const std::pair<int64_t, Saving>& cs1,
2451 const std::pair<int64_t, Saving>& cs2) { return cs1 > cs2; });
2452
2453 // Insert all Savings for this arc with the lowest cost into
2454 // sorted_savings_.
2455 // TODO(user): Also do this when reiterating on next_savings_.
2456 const int64_t cost = costs_and_savings.back().first;
2457 while (!costs_and_savings.empty() &&
2458 costs_and_savings.back().first == cost) {
2459 sorted_savings_.push_back(
2460 {costs_and_savings.back().second, arc_index});
2461 costs_and_savings.pop_back();
2462 }
2463 }
2464 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2465 next_saving_type_and_index_for_arc_.clear();
2466 next_saving_type_and_index_for_arc_.resize(
2467 costs_and_savings_per_arc_.size(), {-1, -1});
2468 }
2469 sorted_ = true;
2470 index_in_sorted_savings_ = 0;
2471 to_update_ = false;
2472 }
2473
2474 bool HasSaving() {
2475 return index_in_sorted_savings_ < sorted_savings_.size() ||
2476 HasReinjectedSavings();
2477 }
2478
2480 CHECK(sorted_) << "Calling GetSaving() before Sort() !";
2481 CHECK(!to_update_)
2482 << "Update() should be called between two calls to GetSaving() !";
2483
2484 to_update_ = true;
2485
2486 if (HasReinjectedSavings()) {
2487 if (incoming_reinjected_savings_ != nullptr &&
2488 outgoing_reinjected_savings_ != nullptr) {
2489 // Get the best Saving among the two.
2490 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
2491 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
2492 if (incoming_saving < outgoing_saving) {
2493 current_saving_ = incoming_saving;
2494 using_incoming_reinjected_saving_ = true;
2495 } else {
2496 current_saving_ = outgoing_saving;
2497 using_incoming_reinjected_saving_ = false;
2498 }
2499 } else {
2500 if (incoming_reinjected_savings_ != nullptr) {
2501 current_saving_ = incoming_reinjected_savings_->front();
2502 using_incoming_reinjected_saving_ = true;
2503 }
2504 if (outgoing_reinjected_savings_ != nullptr) {
2505 current_saving_ = outgoing_reinjected_savings_->front();
2506 using_incoming_reinjected_saving_ = false;
2507 }
2508 }
2509 } else {
2510 current_saving_ = sorted_savings_[index_in_sorted_savings_];
2511 }
2512 return current_saving_.saving;
2513 }
2514
2515 void Update(bool update_best_saving, int type = -1) {
2516 CHECK(to_update_) << "Container already up to date!";
2517 if (update_best_saving) {
2518 const int64_t arc_index = current_saving_.arc_index;
2519 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
2520 }
2521 if (!HasReinjectedSavings()) {
2522 index_in_sorted_savings_++;
2523
2524 if (index_in_sorted_savings_ == sorted_savings_.size()) {
2525 sorted_savings_.swap(next_savings_);
2526 gtl::STLClearObject(&next_savings_);
2527 index_in_sorted_savings_ = 0;
2528
2529 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2530 next_saving_type_and_index_for_arc_.clear();
2531 next_saving_type_and_index_for_arc_.resize(
2532 costs_and_savings_per_arc_.size(), {-1, -1});
2533 }
2534 }
2535 UpdateReinjectedSavings();
2536 to_update_ = false;
2537 }
2538
2539 void UpdateWithType(int type) {
2540 CHECK(!single_vehicle_type_);
2541 Update(/*update_best_saving*/ true, type);
2542 }
2543
2544 const std::vector<Saving>& GetSortedSavingsForVehicleType(int type) {
2545 CHECK(sorted_) << "Savings not sorted yet!";
2546 CHECK_LT(type, vehicle_types_);
2547 return sorted_savings_per_vehicle_type_[type];
2548 }
2549
2551 CHECK(outgoing_new_reinjected_savings_ == nullptr);
2552 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
2553 }
2554
2556 CHECK(incoming_new_reinjected_savings_ == nullptr);
2557 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
2558 }
2559
2560 private:
2561 struct SavingAndArc {
2562 Saving saving;
2563 int64_t arc_index;
2564
2565 bool operator<(const SavingAndArc& other) const {
2566 return std::tie(saving, arc_index) <
2567 std::tie(other.saving, other.arc_index);
2568 }
2569 };
2570
2571 // Skips the Saving for the arc before_node-->after_node, by adding it to the
2572 // skipped_savings_ vector of the nodes, if they're uncontained.
2573 void SkipSavingForArc(const SavingAndArc& saving_and_arc) {
2574 const Saving& saving = saving_and_arc.saving;
2575 const int64_t before_node = savings_db_->GetBeforeNodeFromSaving(saving);
2576 const int64_t after_node = savings_db_->GetAfterNodeFromSaving(saving);
2577 if (!savings_db_->Contains(before_node)) {
2578 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
2579 }
2580 if (!savings_db_->Contains(after_node)) {
2581 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
2582 }
2583 }
2584
2585 // Called within Update() when update_best_saving is true, this method updates
2586 // the next_savings_ and skipped savings vectors for a given arc_index and
2587 // vehicle type.
2588 // When a Saving with the right type has already been added to next_savings_
2589 // for this arc, no action is needed on next_savings_.
2590 // Otherwise, if such a Saving exists, GetNextSavingForArcWithType() will find
2591 // and assign it to next_saving, which is then used to update next_savings_.
2592 // Finally, the right Saving is skipped for this arc: if looking for a
2593 // specific type (i.e. type != -1), next_saving (which has the correct type)
2594 // is skipped, otherwise the current_saving_ is.
2595 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index, int type) {
2596 if (single_vehicle_type_) {
2597 // No next Saving, skip the current Saving.
2598 CHECK_EQ(type, -1);
2599 SkipSavingForArc(current_saving_);
2600 return;
2601 }
2602 CHECK_GE(arc_index, 0);
2603 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
2604 const int previous_index = type_and_index.second;
2605 const int previous_type = type_and_index.first;
2606 bool next_saving_added = false;
2607 Saving next_saving;
2608
2609 if (previous_index >= 0) {
2610 // Next Saving already added for this arc.
2611 DCHECK_GE(previous_type, 0);
2612 if (type == -1 || previous_type == type) {
2613 // Not looking for a specific type, or correct type already in
2614 // next_savings_.
2615 next_saving_added = true;
2616 next_saving = next_savings_[previous_index].saving;
2617 }
2618 }
2619
2620 if (!next_saving_added &&
2621 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
2622 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
2623 if (previous_index >= 0) {
2624 // Update the previous saving.
2625 next_savings_[previous_index] = {next_saving, arc_index};
2626 } else {
2627 // Insert the new next Saving for this arc.
2628 type_and_index.second = next_savings_.size();
2629 next_savings_.push_back({next_saving, arc_index});
2630 }
2631 next_saving_added = true;
2632 }
2633
2634 // Skip the Saving based on the vehicle type.
2635 if (type == -1) {
2636 // Skip the current Saving.
2637 SkipSavingForArc(current_saving_);
2638 } else {
2639 // Skip the Saving with the correct type, already added to next_savings_
2640 // if it was found.
2641 if (next_saving_added) {
2642 SkipSavingForArc({next_saving, arc_index});
2643 }
2644 }
2645 }
2646
2647 void UpdateReinjectedSavings() {
2648 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
2649 &incoming_reinjected_savings_,
2650 using_incoming_reinjected_saving_);
2651 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
2652 &outgoing_reinjected_savings_,
2653 !using_incoming_reinjected_saving_);
2654 incoming_new_reinjected_savings_ = nullptr;
2655 outgoing_new_reinjected_savings_ = nullptr;
2656 }
2657
2658 void UpdateGivenReinjectedSavings(
2659 std::deque<SavingAndArc>* new_reinjected_savings,
2660 std::deque<SavingAndArc>** reinjected_savings,
2661 bool using_reinjected_savings) {
2662 if (new_reinjected_savings == nullptr) {
2663 // No new reinjected savings, update the previous ones if needed.
2664 if (*reinjected_savings != nullptr && using_reinjected_savings) {
2665 CHECK(!(*reinjected_savings)->empty());
2666 (*reinjected_savings)->pop_front();
2667 if ((*reinjected_savings)->empty()) {
2668 *reinjected_savings = nullptr;
2669 }
2670 }
2671 return;
2672 }
2673
2674 // New savings reinjected.
2675 // Forget about the previous reinjected savings and add the new ones if
2676 // there are any.
2677 if (*reinjected_savings != nullptr) {
2678 (*reinjected_savings)->clear();
2679 }
2680 *reinjected_savings = nullptr;
2681 if (!new_reinjected_savings->empty()) {
2682 *reinjected_savings = new_reinjected_savings;
2683 }
2684 }
2685
2686 bool HasReinjectedSavings() {
2687 return outgoing_reinjected_savings_ != nullptr ||
2688 incoming_reinjected_savings_ != nullptr;
2689 }
2690
2691 void UpdateArcIndicesCostsAndSavings(
2692 int64_t before_node, int64_t after_node,
2693 const std::pair<int64_t, Saving>& cost_and_saving) {
2694 if (single_vehicle_type_) {
2695 return;
2696 }
2697 absl::flat_hash_map<int, int>& arc_indices =
2698 arc_indices_per_before_node_[before_node];
2699 const auto& arc_inserted = arc_indices.insert(
2700 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
2701 const int index = arc_inserted.first->second;
2702 if (arc_inserted.second) {
2703 costs_and_savings_per_arc_.push_back({cost_and_saving});
2704 } else {
2705 DCHECK_LT(index, costs_and_savings_per_arc_.size());
2706 costs_and_savings_per_arc_[index].push_back(cost_and_saving);
2707 }
2708 }
2709
2710 bool GetNextSavingForArcWithType(int64_t arc_index, int type,
2711 Saving* next_saving) {
2712 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2713 costs_and_savings_per_arc_[arc_index];
2714
2715 bool found_saving = false;
2716 while (!costs_and_savings.empty() && !found_saving) {
2717 const Saving& saving = costs_and_savings.back().second;
2718 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
2719 *next_saving = saving;
2720 found_saving = true;
2721 }
2722 costs_and_savings.pop_back();
2723 }
2724 return found_saving;
2725 }
2726
2727 const SavingsFilteredHeuristic* const savings_db_;
2728 const int vehicle_types_;
2729 int64_t index_in_sorted_savings_;
2730 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
2731 std::vector<SavingAndArc> sorted_savings_;
2732 std::vector<SavingAndArc> next_savings_;
2733 std::vector<std::pair</*type*/ int, /*index*/ int>>
2734 next_saving_type_and_index_for_arc_;
2735 SavingAndArc current_saving_;
2736 const bool single_vehicle_type_;
2737 std::vector<std::vector<std::pair</*cost*/ int64_t, Saving>>>
2738 costs_and_savings_per_arc_;
2739 std::vector<absl::flat_hash_map</*after_node*/ int, /*arc_index*/ int>>
2740 arc_indices_per_before_node_;
2741 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
2742 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
2743 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
2744 std::deque<SavingAndArc>* incoming_reinjected_savings_;
2745 bool using_incoming_reinjected_saving_;
2746 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
2747 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
2748 bool sorted_;
2749 bool to_update_;
2750};
2751
2752// SavingsFilteredHeuristic
2753
2754SavingsFilteredHeuristic::SavingsFilteredHeuristic(
2755 RoutingModel* model, const RoutingIndexManager* manager,
2757 : RoutingFilteredHeuristic(model, filter_manager),
2758 vehicle_type_curator_(nullptr),
2759 manager_(manager),
2760 savings_params_(parameters) {
2761 DCHECK_GT(savings_params_.neighbors_ratio, 0);
2762 DCHECK_LE(savings_params_.neighbors_ratio, 1);
2763 DCHECK_GT(savings_params_.max_memory_usage_bytes, 0);
2764 DCHECK_GT(savings_params_.arc_coefficient, 0);
2765 const int size = model->Size();
2766 size_squared_ = size * size;
2767}
2768
2770
2772 if (vehicle_type_curator_ == nullptr) {
2773 vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
2774 model()->GetVehicleTypeContainer());
2775 }
2776 // Only store empty vehicles in the vehicle_type_curator_.
2777 vehicle_type_curator_->Reset(
2778 [this](int vehicle) { return VehicleIsEmpty(vehicle); });
2779 if (!ComputeSavings()) return false;
2781 // Free all the space used to store the Savings in the container.
2782 savings_container_.reset();
2784 if (!Commit()) return false;
2786 return Commit();
2787}
2788
2790 int type, int64_t before_node, int64_t after_node) {
2791 auto vehicle_is_compatible = [this, before_node, after_node](int vehicle) {
2792 if (!model()->VehicleVar(before_node)->Contains(vehicle) ||
2793 !model()->VehicleVar(after_node)->Contains(vehicle)) {
2794 return false;
2795 }
2796 // Try to commit the arc on this vehicle.
2797 DCHECK(VehicleIsEmpty(vehicle));
2798 const int64_t start = model()->Start(vehicle);
2799 const int64_t end = model()->End(vehicle);
2800 SetValue(start, before_node);
2801 SetValue(before_node, after_node);
2802 SetValue(after_node, end);
2803 return Commit();
2804 };
2805
2807 ->GetCompatibleVehicleOfType(
2808 type, vehicle_is_compatible,
2809 /*stop_and_return_vehicle*/ [](int) { return false; })
2810 .first;
2811}
2812
2813void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
2814 std::vector<std::vector<int64_t>>* adjacency_lists) {
2815 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
2816 for (int64_t neighbor : (*adjacency_lists)[node]) {
2817 if (model()->IsStart(neighbor) || model()->IsEnd(neighbor)) {
2818 continue;
2819 }
2820 (*adjacency_lists)[neighbor].push_back(node);
2821 }
2822 }
2823 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
2824 adjacency_lists->begin(), [](std::vector<int64_t> vec) {
2825 std::sort(vec.begin(), vec.end());
2826 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
2827 return vec;
2828 });
2829}
2830
2831// Computes the savings related to each pair of non-start and non-end nodes.
2832// The savings value for an arc a-->b for a vehicle starting at node s and
2833// ending at node e is:
2834// saving = cost(s-->a-->e) + cost(s-->b-->e) - cost(s-->a-->b-->e), i.e.
2835// saving = cost(a-->e) + cost(s-->b) - cost(a-->b)
2836// The saving value also considers a coefficient for the cost of the arc
2837// a-->b, which results in:
2838// saving = cost(a-->e) + cost(s-->b) - [arc_coefficient_ * cost(a-->b)]
2839// The higher this saving value, the better the arc.
2840// Here, the value stored for the savings is -saving, which are therefore
2841// considered in decreasing order.
2842bool SavingsFilteredHeuristic::ComputeSavings() {
2843 const int num_vehicle_types = vehicle_type_curator_->NumTypes();
2844 const int size = model()->Size();
2845
2846 std::vector<int64_t> uncontained_non_start_end_nodes;
2847 uncontained_non_start_end_nodes.reserve(size);
2848 for (int node = 0; node < size; node++) {
2849 if (!model()->IsStart(node) && !model()->IsEnd(node) && !Contains(node)) {
2850 uncontained_non_start_end_nodes.push_back(node);
2851 }
2852 }
2853
2854 const int64_t saving_neighbors =
2855 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
2856 static_cast<int64_t>(uncontained_non_start_end_nodes.size()));
2857
2859 absl::make_unique<SavingsContainer<Saving>>(this, num_vehicle_types);
2860 savings_container_->InitializeContainer(size, saving_neighbors);
2861 if (StopSearch()) return false;
2862 std::vector<std::vector<int64_t>> adjacency_lists(size);
2863
2864 for (int type = 0; type < num_vehicle_types; ++type) {
2865 const int vehicle =
2866 vehicle_type_curator_->GetLowestFixedCostVehicleOfType(type);
2867 if (vehicle < 0) {
2868 continue;
2869 }
2870
2871 const int64_t cost_class =
2872 model()->GetCostClassIndexOfVehicle(vehicle).value();
2873 const int64_t start = model()->Start(vehicle);
2874 const int64_t end = model()->End(vehicle);
2875 const int64_t fixed_cost = model()->GetFixedCostOfVehicle(vehicle);
2876
2877 // Compute the neighbors for each non-start/end node not already inserted in
2878 // the model.
2879 for (int before_node : uncontained_non_start_end_nodes) {
2880 std::vector<std::pair</*cost*/ int64_t, /*node*/ int64_t>>
2881 costed_after_nodes;
2882 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
2883 if (StopSearch()) return false;
2884 for (int after_node : uncontained_non_start_end_nodes) {
2885 if (after_node != before_node) {
2886 costed_after_nodes.push_back(std::make_pair(
2887 model()->GetArcCostForClass(before_node, after_node, cost_class),
2888 after_node));
2889 }
2890 }
2891 if (saving_neighbors < costed_after_nodes.size()) {
2892 std::nth_element(costed_after_nodes.begin(),
2893 costed_after_nodes.begin() + saving_neighbors,
2894 costed_after_nodes.end());
2895 costed_after_nodes.resize(saving_neighbors);
2896 }
2897 adjacency_lists[before_node].resize(costed_after_nodes.size());
2898 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
2899 adjacency_lists[before_node].begin(),
2900 [](std::pair<int64_t, int64_t> cost_and_node) {
2901 return cost_and_node.second;
2902 });
2903 }
2904 if (savings_params_.add_reverse_arcs) {
2905 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
2906 }
2907 if (StopSearch()) return false;
2908
2909 // Build the savings for this vehicle type given the adjacency_lists.
2910 for (int before_node : uncontained_non_start_end_nodes) {
2911 const int64_t before_to_end_cost =
2912 model()->GetArcCostForClass(before_node, end, cost_class);
2913 const int64_t start_to_before_cost =
2914 CapSub(model()->GetArcCostForClass(start, before_node, cost_class),
2915 fixed_cost);
2916 if (StopSearch()) return false;
2917 for (int64_t after_node : adjacency_lists[before_node]) {
2918 if (model()->IsStart(after_node) || model()->IsEnd(after_node) ||
2919 before_node == after_node || Contains(after_node)) {
2920 continue;
2921 }
2922 const int64_t arc_cost =
2923 model()->GetArcCostForClass(before_node, after_node, cost_class);
2924 const int64_t start_to_after_cost =
2925 CapSub(model()->GetArcCostForClass(start, after_node, cost_class),
2926 fixed_cost);
2927 const int64_t after_to_end_cost =
2928 model()->GetArcCostForClass(after_node, end, cost_class);
2929
2930 const double weighted_arc_cost_fp =
2931 savings_params_.arc_coefficient * arc_cost;
2932 const int64_t weighted_arc_cost =
2934 ? static_cast<int64_t>(weighted_arc_cost_fp)
2935 : std::numeric_limits<int64_t>::max();
2936 const int64_t saving_value = CapSub(
2937 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
2938
2939 const Saving saving =
2940 BuildSaving(-saving_value, type, before_node, after_node);
2941
2942 const int64_t total_cost =
2943 CapAdd(CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
2944
2945 savings_container_->AddNewSaving(saving, total_cost, before_node,
2946 after_node, type);
2947 }
2948 }
2949 }
2950 savings_container_->Sort();
2951 return !StopSearch();
2952}
2953
2954int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
2955 int num_vehicle_types) const {
2956 const int64_t size = model()->Size();
2957
2958 const int64_t num_neighbors_with_ratio =
2959 std::max(1.0, size * savings_params_.neighbors_ratio);
2960
2961 // A single Saving takes 2*8 bytes of memory.
2962 // max_memory_usage_in_savings_unit = num_savings * multiplicative_factor,
2963 // Where multiplicative_factor is the memory taken (in Savings unit) for each
2964 // computed Saving.
2965 const double max_memory_usage_in_savings_unit =
2966 savings_params_.max_memory_usage_bytes / 16;
2967
2968 // In the SavingsContainer, for each Saving, the Savings are stored:
2969 // - Once in "sorted_savings_per_vehicle_type", and (at most) once in
2970 // "sorted_savings_" --> factor 2
2971 // - If num_vehicle_types > 1, they're also stored by arc_index in
2972 // "costs_and_savings_per_arc", along with their int64_t cost --> factor 1.5
2973 //
2974 // On top of that,
2975 // - In the sequential version, the Saving* are also stored by in-coming and
2976 // outgoing node (in in/out_savings_ptr), adding another 2*8 bytes per
2977 // Saving --> factor 1.
2978 // - In the parallel version, skipped Savings are also stored in
2979 // skipped_savings_starting/ending_at_, resulting in a maximum added factor
2980 // of 2 for each Saving.
2981 // These extra factors are given by ExtraSavingsMemoryMultiplicativeFactor().
2982 double multiplicative_factor = 2.0 + ExtraSavingsMemoryMultiplicativeFactor();
2983 if (num_vehicle_types > 1) {
2984 multiplicative_factor += 1.5;
2985 }
2986 const double num_savings =
2987 max_memory_usage_in_savings_unit / multiplicative_factor;
2988 const int64_t num_neighbors_with_memory_restriction =
2989 std::max(1.0, num_savings / (num_vehicle_types * size));
2990
2991 return std::min(num_neighbors_with_ratio,
2992 num_neighbors_with_memory_restriction);
2993}
2994
2995// SequentialSavingsFilteredHeuristic
2996
2997void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
2998 const int vehicle_types = vehicle_type_curator_->NumTypes();
2999 DCHECK_GT(vehicle_types, 0);
3000 const int size = model()->Size();
3001 // Store savings for each incoming and outgoing node and by vehicle type. This
3002 // is necessary to quickly extend partial chains without scanning all savings.
3003 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
3004 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
3005 for (int type = 0; type < vehicle_types; type++) {
3006 const int vehicle_type_offset = type * size;
3007 const std::vector<Saving>& sorted_savings_for_type =
3008 savings_container_->GetSortedSavingsForVehicleType(type);
3009 for (const Saving& saving : sorted_savings_for_type) {
3010 DCHECK_EQ(GetVehicleTypeFromSaving(saving), type);
3011 const int before_node = GetBeforeNodeFromSaving(saving);
3012 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3013 const int after_node = GetAfterNodeFromSaving(saving);
3014 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3015 }
3016 }
3017
3018 // Build routes from savings.
3019 while (savings_container_->HasSaving()) {
3020 if (StopSearch()) return;
3021 // First find the best saving to start a new route.
3022 const Saving saving = savings_container_->GetSaving();
3023 int before_node = GetBeforeNodeFromSaving(saving);
3024 int after_node = GetAfterNodeFromSaving(saving);
3025 const bool nodes_not_contained =
3026 !Contains(before_node) && !Contains(after_node);
3027
3028 bool committed = false;
3029
3030 if (nodes_not_contained) {
3031 // Find the right vehicle to start the route with this Saving.
3032 const int type = GetVehicleTypeFromSaving(saving);
3033 const int vehicle =
3034 StartNewRouteWithBestVehicleOfType(type, before_node, after_node);
3035
3036 if (vehicle >= 0) {
3037 committed = true;
3038 const int64_t start = model()->Start(vehicle);
3039 const int64_t end = model()->End(vehicle);
3040 // Then extend the route from both ends of the partial route.
3041 int in_index = 0;
3042 int out_index = 0;
3043 const int saving_offset = type * size;
3044
3045 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
3046 out_index <
3047 out_savings_ptr[saving_offset + before_node].size()) {
3048 if (StopSearch()) return;
3049 // First determine how to extend the route.
3050 int before_before_node = -1;
3051 int after_after_node = -1;
3052 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
3053 const Saving& in_saving =
3054 *(in_savings_ptr[saving_offset + after_node][in_index]);
3055 if (out_index <
3056 out_savings_ptr[saving_offset + before_node].size()) {
3057 const Saving& out_saving =
3058 *(out_savings_ptr[saving_offset + before_node][out_index]);
3059 if (GetSavingValue(in_saving) < GetSavingValue(out_saving)) {
3060 // Should extend after after_node
3061 after_after_node = GetAfterNodeFromSaving(in_saving);
3062 } else {
3063 // Should extend before before_node
3064 before_before_node = GetBeforeNodeFromSaving(out_saving);
3065 }
3066 } else {
3067 // Should extend after after_node
3068 after_after_node = GetAfterNodeFromSaving(in_saving);
3069 }
3070 } else {
3071 // Should extend before before_node
3072 before_before_node = GetBeforeNodeFromSaving(
3073 *(out_savings_ptr[saving_offset + before_node][out_index]));
3074 }
3075 // Extend the route
3076 if (after_after_node != -1) {
3077 DCHECK_EQ(before_before_node, -1);
3078 // Extending after after_node
3079 if (!Contains(after_after_node)) {
3080 SetValue(after_node, after_after_node);
3081 SetValue(after_after_node, end);
3082 if (Commit()) {
3083 in_index = 0;
3084 after_node = after_after_node;
3085 } else {
3086 ++in_index;
3087 }
3088 } else {
3089 ++in_index;
3090 }
3091 } else {
3092 // Extending before before_node
3093 CHECK_GE(before_before_node, 0);
3094 if (!Contains(before_before_node)) {
3095 SetValue(start, before_before_node);
3096 SetValue(before_before_node, before_node);
3097 if (Commit()) {
3098 out_index = 0;
3099 before_node = before_before_node;
3100 } else {
3101 ++out_index;
3102 }
3103 } else {
3104 ++out_index;
3105 }
3106 }
3107 }
3108 }
3109 }
3110 savings_container_->Update(nodes_not_contained && !committed);
3111 }
3112}
3113
3114// ParallelSavingsFilteredHeuristic
3115
3116void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3117 // Initialize the vehicles of the first/last non start/end nodes served by
3118 // each route.
3119 const int64_t size = model()->Size();
3120 const int vehicles = model()->vehicles();
3121
3122 first_node_on_route_.resize(vehicles, -1);
3123 last_node_on_route_.resize(vehicles, -1);
3124 vehicle_of_first_or_last_node_.resize(size, -1);
3125
3126 for (int vehicle = 0; vehicle < vehicles; vehicle++) {
3127 const int64_t start = model()->Start(vehicle);
3128 const int64_t end = model()->End(vehicle);
3129 if (!Contains(start)) {
3130 continue;
3131 }
3132 int64_t node = Value(start);
3133 if (node != end) {
3134 vehicle_of_first_or_last_node_[node] = vehicle;
3135 first_node_on_route_[vehicle] = node;
3136
3137 int64_t next = Value(node);
3138 while (next != end) {
3139 node = next;
3140 next = Value(node);
3141 }
3142 vehicle_of_first_or_last_node_[node] = vehicle;
3143 last_node_on_route_[vehicle] = node;
3144 }
3145 }
3146
3147 while (savings_container_->HasSaving()) {
3148 if (StopSearch()) return;
3149 const Saving saving = savings_container_->GetSaving();
3150 const int64_t before_node = GetBeforeNodeFromSaving(saving);
3151 const int64_t after_node = GetAfterNodeFromSaving(saving);
3152 const int type = GetVehicleTypeFromSaving(saving);
3153
3154 if (!Contains(before_node) && !Contains(after_node)) {
3155 // Neither nodes are contained, start a new route.
3156 bool committed = false;
3157
3158 const int vehicle =
3159 StartNewRouteWithBestVehicleOfType(type, before_node, after_node);
3160
3161 if (vehicle >= 0) {
3162 committed = true;
3163 // Store before_node and after_node as first and last nodes of the route
3164 vehicle_of_first_or_last_node_[before_node] = vehicle;
3165 vehicle_of_first_or_last_node_[after_node] = vehicle;
3166 first_node_on_route_[vehicle] = before_node;
3167 last_node_on_route_[vehicle] = after_node;
3168 savings_container_->ReinjectSkippedSavingsStartingAt(after_node);
3169 savings_container_->ReinjectSkippedSavingsEndingAt(before_node);
3170 }
3171 savings_container_->Update(!committed);
3172 continue;
3173 }
3174
3175 if (Contains(before_node) && Contains(after_node)) {
3176 // Merge the two routes if before_node is last and after_node first of its
3177 // route, the two nodes aren't already on the same route, and the vehicle
3178 // types are compatible.
3179 const int v1 = vehicle_of_first_or_last_node_[before_node];
3180 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3181
3182 const int v2 = vehicle_of_first_or_last_node_[after_node];
3183 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3184
3185 if (before_node == last_node && after_node == first_node && v1 != v2 &&
3186 vehicle_type_curator_->Type(v1) == vehicle_type_curator_->Type(v2)) {
3187 CHECK_EQ(Value(before_node), model()->End(v1));
3188 CHECK_EQ(Value(model()->Start(v2)), after_node);
3189
3190 // We try merging the two routes.
3191 // TODO(user): Try to use skipped savings to start new routes when
3192 // a vehicle becomes available after a merge (not trivial because it can
3193 // result in an infinite loop).
3194 MergeRoutes(v1, v2, before_node, after_node);
3195 }
3196 }
3197
3198 if (Contains(before_node) && !Contains(after_node)) {
3199 const int vehicle = vehicle_of_first_or_last_node_[before_node];
3200 const int64_t last_node =
3201 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3202
3203 if (before_node == last_node) {
3204 const int64_t end = model()->End(vehicle);
3205 CHECK_EQ(Value(before_node), end);
3206
3207 const int route_type = vehicle_type_curator_->Type(vehicle);
3208 if (type != route_type) {
3209 // The saving doesn't correspond to the type of the vehicle serving
3210 // before_node. We update the container with the correct type.
3211 savings_container_->UpdateWithType(route_type);
3212 continue;
3213 }
3214
3215 // Try adding after_node on route of before_node.
3216 SetValue(before_node, after_node);
3217 SetValue(after_node, end);
3218 if (Commit()) {
3219 if (first_node_on_route_[vehicle] != before_node) {
3220 // before_node is no longer the start or end of its route
3221 DCHECK_NE(Value(model()->Start(vehicle)), before_node);
3222 vehicle_of_first_or_last_node_[before_node] = -1;
3223 }
3224 vehicle_of_first_or_last_node_[after_node] = vehicle;
3225 last_node_on_route_[vehicle] = after_node;
3226 savings_container_->ReinjectSkippedSavingsStartingAt(after_node);
3227 }
3228 }
3229 }
3230
3231 if (!Contains(before_node) && Contains(after_node)) {
3232 const int vehicle = vehicle_of_first_or_last_node_[after_node];
3233 const int64_t first_node =
3234 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3235
3236 if (after_node == first_node) {
3237 const int64_t start = model()->Start(vehicle);
3238 CHECK_EQ(Value(start), after_node);
3239
3240 const int route_type = vehicle_type_curator_->Type(vehicle);
3241 if (type != route_type) {
3242 // The saving doesn't correspond to the type of the vehicle serving
3243 // after_node. We update the container with the correct type.
3244 savings_container_->UpdateWithType(route_type);
3245 continue;
3246 }
3247
3248 // Try adding before_node on route of after_node.
3249 SetValue(before_node, after_node);
3250 SetValue(start, before_node);
3251 if (Commit()) {
3252 if (last_node_on_route_[vehicle] != after_node) {
3253 // after_node is no longer the start or end of its route
3254 DCHECK_NE(Value(after_node), model()->End(vehicle));
3255 vehicle_of_first_or_last_node_[after_node] = -1;
3256 }
3257 vehicle_of_first_or_last_node_[before_node] = vehicle;
3258 first_node_on_route_[vehicle] = before_node;
3259 savings_container_->ReinjectSkippedSavingsEndingAt(before_node);
3260 }
3261 }
3262 }
3263 savings_container_->Update(/*update_best_saving*/ false);
3264 }
3265}
3266
3267void ParallelSavingsFilteredHeuristic::MergeRoutes(int first_vehicle,
3268 int second_vehicle,
3269 int64_t before_node,
3270 int64_t after_node) {
3271 if (StopSearch()) return;
3272 const int64_t new_first_node = first_node_on_route_[first_vehicle];
3273 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
3274 CHECK_EQ(Value(model()->Start(first_vehicle)), new_first_node);
3275 const int64_t new_last_node = last_node_on_route_[second_vehicle];
3276 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
3277 CHECK_EQ(Value(new_last_node), model()->End(second_vehicle));
3278
3279 // Select the vehicle with lower fixed cost to merge the routes.
3280 int used_vehicle = first_vehicle;
3281 int unused_vehicle = second_vehicle;
3282 if (model()->GetFixedCostOfVehicle(first_vehicle) >
3283 model()->GetFixedCostOfVehicle(second_vehicle)) {
3284 used_vehicle = second_vehicle;
3285 unused_vehicle = first_vehicle;
3286 }
3287
3288 SetValue(before_node, after_node);
3289 SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle));
3290 if (used_vehicle == first_vehicle) {
3291 SetValue(new_last_node, model()->End(used_vehicle));
3292 } else {
3293 SetValue(model()->Start(used_vehicle), new_first_node);
3294 }
3295 bool committed = Commit();
3296 if (!committed &&
3297 model()->GetVehicleClassIndexOfVehicle(first_vehicle).value() !=
3298 model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) {
3299 // Try committing on other vehicle instead.
3300 std::swap(used_vehicle, unused_vehicle);
3301 SetValue(before_node, after_node);
3302 SetValue(model()->Start(unused_vehicle), model()->End(unused_vehicle));
3303 if (used_vehicle == first_vehicle) {
3304 SetValue(new_last_node, model()->End(used_vehicle));
3305 } else {
3306 SetValue(model()->Start(used_vehicle), new_first_node);
3307 }
3308 committed = Commit();
3309 }
3310 if (committed) {
3311 // Make unused_vehicle available
3312 vehicle_type_curator_->ReinjectVehicleOfClass(
3313 unused_vehicle,
3314 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).value(),
3315 model()->GetFixedCostOfVehicle(unused_vehicle));
3316
3317 // Update the first and last nodes on vehicles.
3318 first_node_on_route_[unused_vehicle] = -1;
3319 last_node_on_route_[unused_vehicle] = -1;
3320 vehicle_of_first_or_last_node_[before_node] = -1;
3321 vehicle_of_first_or_last_node_[after_node] = -1;
3322 first_node_on_route_[used_vehicle] = new_first_node;
3323 last_node_on_route_[used_vehicle] = new_last_node;
3324 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
3325 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
3326 }
3327}
3328
3329// ChristofidesFilteredHeuristic
3330
3333 bool use_minimum_matching)
3334 : RoutingFilteredHeuristic(model, filter_manager),
3335 use_minimum_matching_(use_minimum_matching) {}
3336
3337// TODO(user): Support pickup & delivery.
3339 const int size = model()->Size() - model()->vehicles() + 1;
3340 // Node indices for Christofides solver.
3341 // 0: start/end node
3342 // >0: non start/end nodes
3343 // TODO(user): Add robustness to fixed arcs by collapsing them into meta-
3344 // nodes.
3345 std::vector<int> indices(1, 0);
3346 for (int i = 1; i < size; ++i) {
3347 if (!model()->IsStart(i) && !model()->IsEnd(i)) {
3348 indices.push_back(i);
3349 }
3350 }
3351 const int num_cost_classes = model()->GetCostClassesCount();
3352 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
3353 std::vector<bool> class_covered(num_cost_classes, false);
3354 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
3355 const int64_t cost_class =
3356 model()->GetCostClassIndexOfVehicle(vehicle).value();
3357 if (!class_covered[cost_class]) {
3358 class_covered[cost_class] = true;
3359 const int64_t start = model()->Start(vehicle);
3360 const int64_t end = model()->End(vehicle);
3361 auto cost = [this, &indices, start, end, cost_class](int from, int to) {
3362 DCHECK_LT(from, indices.size());
3363 DCHECK_LT(to, indices.size());
3364 const int from_index = (from == 0) ? start : indices[from];
3365 const int to_index = (to == 0) ? end : indices[to];
3366 const int64_t cost =
3367 model()->GetArcCostForClass(from_index, to_index, cost_class);
3368 // To avoid overflow issues, capping costs at kint64max/2, the maximum
3369 // value supported by MinCostPerfectMatching.
3370 // TODO(user): Investigate if ChristofidesPathSolver should not
3371 // return a status to bail out fast in case of problem.
3373 };
3374 using Cost = decltype(cost);
3376 indices.size(), cost);
3377 if (use_minimum_matching_) {
3378 christofides_solver.SetMatchingAlgorithm(
3380 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
3381 }
3382 if (christofides_solver.Solve()) {
3383 path_per_cost_class[cost_class] =
3384 christofides_solver.TravelingSalesmanPath();
3385 }
3386 }
3387 }
3388 // TODO(user): Investigate if sorting paths per cost improves solutions.
3389 for (int vehicle = 0; vehicle < model()->vehicles(); ++vehicle) {
3390 const int64_t cost_class =
3391 model()->GetCostClassIndexOfVehicle(vehicle).value();
3392 const std::vector<int>& path = path_per_cost_class[cost_class];
3393 if (path.empty()) continue;
3394 DCHECK_EQ(0, path[0]);
3395 DCHECK_EQ(0, path.back());
3396 // Extend route from start.
3397 int prev = GetStartChainEnd(vehicle);
3398 const int end = model()->End(vehicle);
3399 for (int i = 1; i < path.size() - 1 && prev != end; ++i) {
3400 if (StopSearch()) return false;
3401 int next = indices[path[i]];
3402 if (!Contains(next)) {
3403 SetValue(prev, next);
3404 SetValue(next, end);
3405 if (Commit()) {
3406 prev = next;
3407 }
3408 }
3409 }
3410 }
3412 return Commit();
3413}
3414
3415// Sweep heuristic
3416// TODO(user): Clean up to match other first solution strategies.
3417
3418namespace {
3419struct SweepIndex {
3420 SweepIndex(const int64_t index, const double angle, const double distance)
3422 ~SweepIndex() {}
3423
3424 int64_t index;
3425 double angle;
3426 double distance;
3427};
3428
3429struct SweepIndexSortAngle {
3430 bool operator()(const SweepIndex& node1, const SweepIndex& node2) const {
3431 return (node1.angle < node2.angle);
3432 }
3433} SweepIndexAngleComparator;
3434
3435struct SweepIndexSortDistance {
3436 bool operator()(const SweepIndex& node1, const SweepIndex& node2) const {
3437 return (node1.distance < node2.distance);
3438 }
3439} SweepIndexDistanceComparator;
3440} // namespace
3441
3443 const std::vector<std::pair<int64_t, int64_t>>& points)
3444 : coordinates_(2 * points.size(), 0), sectors_(1) {
3445 for (int64_t i = 0; i < points.size(); ++i) {
3446 coordinates_[2 * i] = points[i].first;
3447 coordinates_[2 * i + 1] = points[i].second;
3448 }
3449}
3450
3451// Splits the space of the indices into sectors and sorts the indices of each
3452// sector with ascending angle from the depot.
3453void SweepArranger::ArrangeIndices(std::vector<int64_t>* indices) {
3454 const double pi_rad = 3.14159265;
3455 // Suppose that the center is at x0, y0.
3456 const int x0 = coordinates_[0];
3457 const int y0 = coordinates_[1];
3458
3459 std::vector<SweepIndex> sweep_indices;
3460 for (int64_t index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3461 ++index) {
3462 const int x = coordinates_[2 * index];
3463 const int y = coordinates_[2 * index + 1];
3464 const double x_delta = x - x0;
3465 const double y_delta = y - y0;
3466 double square_distance = x_delta * x_delta + y_delta * y_delta;
3467 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3468 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
3469 SweepIndex sweep_index(index, angle, square_distance);
3470 sweep_indices.push_back(sweep_index);
3471 }
3472 std::sort(sweep_indices.begin(), sweep_indices.end(),
3473 SweepIndexDistanceComparator);
3474
3475 const int size = static_cast<int>(sweep_indices.size()) / sectors_;
3476 for (int sector = 0; sector < sectors_; ++sector) {
3477 std::vector<SweepIndex> cluster;
3478 std::vector<SweepIndex>::iterator begin =
3479 sweep_indices.begin() + sector * size;
3480 std::vector<SweepIndex>::iterator end =
3481 sector == sectors_ - 1 ? sweep_indices.end()
3482 : sweep_indices.begin() + (sector + 1) * size;
3483 std::sort(begin, end, SweepIndexAngleComparator);
3484 }
3485 for (const SweepIndex& sweep_index : sweep_indices) {
3486 indices->push_back(sweep_index.index);
3487 }
3488}
3489
3490namespace {
3491
3492struct Link {
3493 Link(std::pair<int, int> link, double value, int vehicle_class,
3494 int64_t start_depot, int64_t end_depot)
3495 : link(link),
3496 value(value),
3500 ~Link() {}
3501
3502 std::pair<int, int> link;
3503 int64_t value;
3506 int64_t end_depot;
3507};
3508
3509// The RouteConstructor creates the routes of a VRP instance subject to its
3510// constraints by iterating on a list of arcs appearing in descending order
3511// of priority.
3512// TODO(user): Use the dimension class in this class.
3513// TODO(user): Add support for vehicle-dependent dimension transits.
3514class RouteConstructor {
3515 public:
3516 RouteConstructor(Assignment* const assignment, RoutingModel* const model,
3517 bool check_assignment, int64_t num_indices,
3518 const std::vector<Link>& links_list)
3519 : assignment_(assignment),
3520 model_(model),
3521 check_assignment_(check_assignment),
3522 solver_(model_->solver()),
3523 num_indices_(num_indices),
3524 links_list_(links_list),
3525 nexts_(model_->Nexts()),
3526 in_route_(num_indices_, -1),
3527 final_routes_(),
3528 index_to_chain_index_(num_indices, -1),
3529 index_to_vehicle_class_index_(num_indices, -1) {
3530 {
3531 const std::vector<std::string> dimension_names =
3532 model_->GetAllDimensionNames();
3533 dimensions_.assign(dimension_names.size(), nullptr);
3534 for (int i = 0; i < dimension_names.size(); ++i) {
3535 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
3536 }
3537 }
3538 cumuls_.resize(dimensions_.size());
3539 for (std::vector<int64_t>& cumuls : cumuls_) {
3540 cumuls.resize(num_indices_);
3541 }
3542 new_possible_cumuls_.resize(dimensions_.size());
3543 }
3544
3545 ~RouteConstructor() {}
3546
3547 void Construct() {
3548 model_->solver()->TopPeriodicCheck();
3549 // Initial State: Each order is served by its own vehicle.
3550 for (int index = 0; index < num_indices_; ++index) {
3551 if (!model_->IsStart(index) && !model_->IsEnd(index)) {
3552 std::vector<int> route(1, index);
3553 routes_.push_back(route);
3554 in_route_[index] = routes_.size() - 1;
3555 }
3556 }
3557
3558 for (const Link& link : links_list_) {
3559 model_->solver()->TopPeriodicCheck();
3560 const int index1 = link.link.first;
3561 const int index2 = link.link.second;
3562 const int vehicle_class = link.vehicle_class;
3563 const int64_t start_depot = link.start_depot;
3564 const int64_t end_depot = link.end_depot;
3565
3566 // Initialisation of cumuls_ if the indices are encountered for first time
3567 if (index_to_vehicle_class_index_[index1] < 0) {
3568 for (int dimension_index = 0; dimension_index < dimensions_.size();
3569 ++dimension_index) {
3570 cumuls_[dimension_index][index1] =
3571 std::max(dimensions_[dimension_index]->GetTransitValue(
3572 start_depot, index1, 0),
3573 dimensions_[dimension_index]->CumulVar(index1)->Min());
3574 }
3575 }
3576 if (index_to_vehicle_class_index_[index2] < 0) {
3577 for (int dimension_index = 0; dimension_index < dimensions_.size();
3578 ++dimension_index) {
3579 cumuls_[dimension_index][index2] =
3580 std::max(dimensions_[dimension_index]->GetTransitValue(
3581 start_depot, index2, 0),
3582 dimensions_[dimension_index]->CumulVar(index2)->Min());
3583 }
3584 }
3585
3586 const int route_index1 = in_route_[index1];
3587 const int route_index2 = in_route_[index2];
3588 const bool merge =
3589 route_index1 >= 0 && route_index2 >= 0 &&
3590 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
3591 index2, route_index1, route_index2, vehicle_class,
3593 if (Merge(merge, route_index1, route_index2)) {
3594 index_to_vehicle_class_index_[index1] = vehicle_class;
3595 index_to_vehicle_class_index_[index2] = vehicle_class;
3596 }
3597 }
3598
3599 model_->solver()->TopPeriodicCheck();
3600 // Beyond this point not checking limits anymore as the rest of the code is
3601 // linear and that given we managed to build a solution would be stupid to
3602 // drop it now.
3603 for (int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3604 if (!gtl::ContainsKey(deleted_chains_, chain_index)) {
3605 final_chains_.push_back(chains_[chain_index]);
3606 }
3607 }
3608 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
3609 for (int route_index = 0; route_index < routes_.size(); ++route_index) {
3610 if (!gtl::ContainsKey(deleted_routes_, route_index)) {
3611 final_routes_.push_back(routes_[route_index]);
3612 }
3613 }
3614 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
3615
3616 const int extra_vehicles = std::max(
3617 0, static_cast<int>(final_chains_.size()) - model_->vehicles());
3618 // Bind the Start and End of each chain
3619 int chain_index = 0;
3620 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
3621 ++chain_index) {
3622 if (chain_index - extra_vehicles >= model_->vehicles()) {
3623 break;
3624 }
3625 const int start = final_chains_[chain_index].head;
3626 const int end = final_chains_[chain_index].tail;
3627 assignment_->Add(
3628 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3629 assignment_->SetValue(
3630 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
3631 assignment_->Add(nexts_[end]);
3632 assignment_->SetValue(nexts_[end],
3633 model_->End(chain_index - extra_vehicles));
3634 }
3635
3636 // Create the single order routes
3637 for (int route_index = 0; route_index < final_routes_.size();
3638 ++route_index) {
3639 if (chain_index - extra_vehicles >= model_->vehicles()) {
3640 break;
3641 }
3642 DCHECK_LT(route_index, final_routes_.size());
3643 const int head = final_routes_[route_index].front();
3644 const int tail = final_routes_[route_index].back();
3645 if (head == tail && head < model_->Size()) {
3646 assignment_->Add(
3647 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3648 assignment_->SetValue(
3649 model_->NextVar(model_->Start(chain_index - extra_vehicles)), head);
3650 assignment_->Add(nexts_[tail]);
3651 assignment_->SetValue(nexts_[tail],
3652 model_->End(chain_index - extra_vehicles));
3653 ++chain_index;
3654 }
3655 }
3656
3657 // Unperformed
3658 for (int index = 0; index < model_->Size(); ++index) {
3659 IntVar* const next = nexts_[index];
3660 if (!assignment_->Contains(next)) {
3661 assignment_->Add(next);
3662 if (next->Contains(index)) {
3663 assignment_->SetValue(next, index);
3664 }
3665 }
3666 }
3667 }
3668
3669 const std::vector<std::vector<int>>& final_routes() const {
3670 return final_routes_;
3671 }
3672
3673 private:
3674 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
3675
3676 struct RouteSort {
3677 bool operator()(const std::vector<int>& route1,
3678 const std::vector<int>& route2) const {
3679 return (route1.size() < route2.size());
3680 }
3681 } RouteComparator;
3682
3683 struct Chain {
3684 int head;
3685 int tail;
3687 };
3688
3689 struct ChainSort {
3690 bool operator()(const Chain& chain1, const Chain& chain2) const {
3691 return (chain1.nodes < chain2.nodes);
3692 }
3693 } ChainComparator;
3694
3695 bool Head(int node) const {
3696 return (node == routes_[in_route_[node]].front());
3697 }
3698
3699 bool Tail(int node) const {
3700 return (node == routes_[in_route_[node]].back());
3701 }
3702
3703 bool FeasibleRoute(const std::vector<int>& route, int64_t route_cumul,
3704 int dimension_index) {
3705 const RoutingDimension& dimension = *dimensions_[dimension_index];
3706 std::vector<int>::const_iterator it = route.begin();
3707 int64_t cumul = route_cumul;
3708 while (it != route.end()) {
3709 const int previous = *it;
3710 const int64_t cumul_previous = cumul;
3711 gtl::InsertOrDie(&(new_possible_cumuls_[dimension_index]), previous,
3712 cumul_previous);
3713 ++it;
3714 if (it == route.end()) {
3715 return true;
3716 }
3717 const int next = *it;
3718 int64_t available_from_previous =
3719 cumul_previous + dimension.GetTransitValue(previous, next, 0);
3720 int64_t available_cumul_next =
3721 std::max(cumuls_[dimension_index][next], available_from_previous);
3722
3723 const int64_t slack = available_cumul_next - available_from_previous;
3724 if (slack > dimension.SlackVar(previous)->Max()) {
3725 available_cumul_next =
3726 available_from_previous + dimension.SlackVar(previous)->Max();
3727 }
3728
3729 if (available_cumul_next > dimension.CumulVar(next)->Max()) {
3730 return false;
3731 }
3732 if (available_cumul_next <= cumuls_[dimension_index][next]) {
3733 return true;
3734 }
3735 cumul = available_cumul_next;
3736 }
3737 return true;
3738 }
3739
3740 bool CheckRouteConnection(const std::vector<int>& route1,
3741 const std::vector<int>& route2, int dimension_index,
3742 int64_t start_depot, int64_t end_depot) {
3743 const int tail1 = route1.back();
3744 const int head2 = route2.front();
3745 const int tail2 = route2.back();
3746 const RoutingDimension& dimension = *dimensions_[dimension_index];
3747 int non_depot_node = -1;
3748 for (int node = 0; node < num_indices_; ++node) {
3749 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
3750 non_depot_node = node;
3751 break;
3752 }
3753 }
3754 CHECK_GE(non_depot_node, 0);
3755 const int64_t depot_threshold =
3756 std::max(dimension.SlackVar(non_depot_node)->Max(),
3757 dimension.CumulVar(non_depot_node)->Max());
3758
3759 int64_t available_from_tail1 = cumuls_[dimension_index][tail1] +
3760 dimension.GetTransitValue(tail1, head2, 0);
3761 int64_t new_available_cumul_head2 =
3762 std::max(cumuls_[dimension_index][head2], available_from_tail1);
3763
3764 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
3765 if (slack > dimension.SlackVar(tail1)->Max()) {
3766 new_available_cumul_head2 =
3767 available_from_tail1 + dimension.SlackVar(tail1)->Max();
3768 }
3769
3770 bool feasible_route = true;
3771 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
3772 return false;
3773 }
3774 if (new_available_cumul_head2 <= cumuls_[dimension_index][head2]) {
3775 return true;
3776 }
3777
3778 feasible_route =
3779 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
3780 const int64_t new_possible_cumul_tail2 =
3781 gtl::ContainsKey(new_possible_cumuls_[dimension_index], tail2)
3782 ? new_possible_cumuls_[dimension_index][tail2]
3783 : cumuls_[dimension_index][tail2];
3784
3785 if (!feasible_route || (new_possible_cumul_tail2 +
3786 dimension.GetTransitValue(tail2, end_depot, 0) >
3787 depot_threshold)) {
3788 return false;
3789 }
3790 return true;
3791 }
3792
3793 bool FeasibleMerge(const std::vector<int>& route1,
3794 const std::vector<int>& route2, int node1, int node2,
3795 int route_index1, int route_index2, int vehicle_class,
3796 int64_t start_depot, int64_t end_depot) {
3797 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
3798 return false;
3799 }
3800
3801 // Vehicle Class Check
3802 if (!((index_to_vehicle_class_index_[node1] == -1 &&
3803 index_to_vehicle_class_index_[node2] == -1) ||
3804 (index_to_vehicle_class_index_[node1] == vehicle_class &&
3805 index_to_vehicle_class_index_[node2] == -1) ||
3806 (index_to_vehicle_class_index_[node1] == -1 &&
3807 index_to_vehicle_class_index_[node2] == vehicle_class) ||
3808 (index_to_vehicle_class_index_[node1] == vehicle_class &&
3809 index_to_vehicle_class_index_[node2] == vehicle_class))) {
3810 return false;
3811 }
3812
3813 // Check Route1 -> Route2 connection for every dimension
3814 bool merge = true;
3815 for (int dimension_index = 0; dimension_index < dimensions_.size();
3816 ++dimension_index) {
3817 new_possible_cumuls_[dimension_index].clear();
3818 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
3820 if (!merge) {
3821 return false;
3822 }
3823 }
3824 return true;
3825 }
3826
3827 bool CheckTempAssignment(Assignment* const temp_assignment,
3828 int new_chain_index, int old_chain_index, int head1,
3829 int tail1, int head2, int tail2) {
3830 // TODO(user): If the chain index is greater than the number of vehicles,
3831 // use another vehicle instead.
3832 if (new_chain_index >= model_->vehicles()) return false;
3833 const int start = head1;
3834 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
3835 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
3836 start);
3837 temp_assignment->Add(nexts_[tail1]);
3838 temp_assignment->SetValue(nexts_[tail1], head2);
3839 temp_assignment->Add(nexts_[tail2]);
3840 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
3841 for (int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3842 if ((chain_index != new_chain_index) &&
3843 (chain_index != old_chain_index) &&
3844 (!gtl::ContainsKey(deleted_chains_, chain_index))) {
3845 const int start = chains_[chain_index].head;
3846 const int end = chains_[chain_index].tail;
3847 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
3848 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
3849 start);
3850 temp_assignment->Add(nexts_[end]);
3851 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
3852 }
3853 }
3854 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
3855 }
3856
3857 bool UpdateAssignment(const std::vector<int>& route1,
3858 const std::vector<int>& route2) {
3859 bool feasible = true;
3860 const int head1 = route1.front();
3861 const int tail1 = route1.back();
3862 const int head2 = route2.front();
3863 const int tail2 = route2.back();
3864 const int chain_index1 = index_to_chain_index_[head1];
3865 const int chain_index2 = index_to_chain_index_[head2];
3866 if (chain_index1 < 0 && chain_index2 < 0) {
3867 const int chain_index = chains_.size();
3868 if (check_assignment_) {
3869 Assignment* const temp_assignment =
3870 solver_->MakeAssignment(assignment_);
3871 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
3872 tail1, head2, tail2);
3873 }
3874 if (feasible) {
3875 Chain chain;
3876 chain.head = head1;
3877 chain.tail = tail2;
3878 chain.nodes = 2;
3879 index_to_chain_index_[head1] = chain_index;
3880 index_to_chain_index_[tail2] = chain_index;
3881 chains_.push_back(chain);
3882 }
3883 } else if (chain_index1 >= 0 && chain_index2 < 0) {
3884 if (check_assignment_) {
3885 Assignment* const temp_assignment =
3886 solver_->MakeAssignment(assignment_);
3887 feasible =
3888 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
3889 head1, tail1, head2, tail2);
3890 }
3891 if (feasible) {
3892 index_to_chain_index_[tail2] = chain_index1;
3893 chains_[chain_index1].head = head1;
3894 chains_[chain_index1].tail = tail2;
3895 ++chains_[chain_index1].nodes;
3896 }
3897 } else if (chain_index1 < 0 && chain_index2 >= 0) {
3898 if (check_assignment_) {
3899 Assignment* const temp_assignment =
3900 solver_->MakeAssignment(assignment_);
3901 feasible =
3902 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
3903 head1, tail1, head2, tail2);
3904 }
3905 if (feasible) {
3906 index_to_chain_index_[head1] = chain_index2;
3907 chains_[chain_index2].head = head1;
3908 chains_[chain_index2].tail = tail2;
3909 ++chains_[chain_index2].nodes;
3910 }
3911 } else {
3912 if (check_assignment_) {
3913 Assignment* const temp_assignment =
3914 solver_->MakeAssignment(assignment_);
3915 feasible =
3916 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
3917 head1, tail1, head2, tail2);
3918 }
3919 if (feasible) {
3920 index_to_chain_index_[tail2] = chain_index1;
3921 chains_[chain_index1].head = head1;
3922 chains_[chain_index1].tail = tail2;
3923 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
3924 deleted_chains_.insert(chain_index2);
3925 }
3926 }
3927 if (feasible) {
3928 assignment_->Add(nexts_[tail1]);
3929 assignment_->SetValue(nexts_[tail1], head2);
3930 }
3931 return feasible;
3932 }
3933
3934 bool Merge(bool merge, int index1, int index2) {
3935 if (merge) {
3936 if (UpdateAssignment(routes_[index1], routes_[index2])) {
3937 // Connection Route1 -> Route2
3938 for (const int node : routes_[index2]) {
3939 in_route_[node] = index1;
3940 routes_[index1].push_back(node);
3941 }
3942 for (int dimension_index = 0; dimension_index < dimensions_.size();
3943 ++dimension_index) {
3944 for (const std::pair<int, int64_t> new_possible_cumul :
3945 new_possible_cumuls_[dimension_index]) {
3946 cumuls_[dimension_index][new_possible_cumul.first] =
3947 new_possible_cumul.second;
3948 }
3949 }
3950 deleted_routes_.insert(index2);
3951 return true;
3952 }
3953 }
3954 return false;
3955 }
3956
3957 Assignment* const assignment_;
3958 RoutingModel* const model_;
3959 const bool check_assignment_;
3960 Solver* const solver_;
3961 const int64_t num_indices_;
3962 const std::vector<Link> links_list_;
3963 std::vector<IntVar*> nexts_;
3964 std::vector<const RoutingDimension*> dimensions_; // Not owned.
3965 std::vector<std::vector<int64_t>> cumuls_;
3966 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
3967 std::vector<std::vector<int>> routes_;
3968 std::vector<int> in_route_;
3969 absl::flat_hash_set<int> deleted_routes_;
3970 std::vector<std::vector<int>> final_routes_;
3971 std::vector<Chain> chains_;
3972 absl::flat_hash_set<int> deleted_chains_;
3973 std::vector<Chain> final_chains_;
3974 std::vector<int> index_to_chain_index_;
3975 std::vector<int> index_to_vehicle_class_index_;
3976};
3977
3978// Decision Builder building a first solution based on Sweep heuristic for
3979// Vehicle Routing Problem.
3980// Suitable only when distance is considered as the cost.
3981class SweepBuilder : public DecisionBuilder {
3982 public:
3983 SweepBuilder(RoutingModel* const model, bool check_assignment)
3984 : model_(model), check_assignment_(check_assignment) {}
3985 ~SweepBuilder() override {}
3986
3987 Decision* Next(Solver* const solver) override {
3988 // Setup the model of the instance for the Sweep Algorithm
3989 ModelSetup();
3990
3991 // Build the assignment routes for the model
3992 Assignment* const assignment = solver->MakeAssignment();
3993 route_constructor_ = absl::make_unique<RouteConstructor>(
3994 assignment, model_, check_assignment_, num_indices_, links_);
3995 // This call might cause backtracking if the search limit is reached.
3996 route_constructor_->Construct();
3997 route_constructor_.reset(nullptr);
3998 // This call might cause backtracking if the solution is not feasible.
3999 assignment->Restore();
4000
4001 return nullptr;
4002 }
4003
4004 private:
4005 void ModelSetup() {
4006 const int depot = model_->GetDepot();
4007 num_indices_ = model_->Size() + model_->vehicles();
4008 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4009 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4010 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4011 }
4012 std::vector<int64_t> indices;
4013 model_->sweep_arranger()->ArrangeIndices(&indices);
4014 for (int i = 0; i < indices.size() - 1; ++i) {
4015 const int64_t first = indices[i];
4016 const int64_t second = indices[i + 1];
4017 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4018 (model_->IsStart(second) || !model_->IsEnd(second))) {
4019 if (first != depot && second != depot) {
4020 Link link(std::make_pair(first, second), 0, 0, depot, depot);
4021 links_.push_back(link);
4022 }
4023 }
4024 }
4025 }
4026
4027 RoutingModel* const model_;
4028 std::unique_ptr<RouteConstructor> route_constructor_;
4029 const bool check_assignment_;
4030 int64_t num_indices_;
4031 std::vector<Link> links_;
4032};
4033} // namespace
4034
4036 bool check_assignment) {
4037 return model->solver()->RevAlloc(new SweepBuilder(model, check_assignment));
4038}
4039
4040// AllUnperformed
4041
4042namespace {
4043// Decision builder to build a solution with all nodes inactive. It does no
4044// branching and may fail if some nodes cannot be made inactive.
4045
4046class AllUnperformed : public DecisionBuilder {
4047 public:
4048 // Does not take ownership of model.
4049 explicit AllUnperformed(RoutingModel* const model) : model_(model) {}
4050 ~AllUnperformed() override {}
4051 Decision* Next(Solver* const solver) override {
4052 // Solver::(Un)FreezeQueue is private, passing through the public API
4053 // on PropagationBaseObject.
4054 model_->CostVar()->FreezeQueue();
4055 for (int i = 0; i < model_->Size(); ++i) {
4056 if (!model_->IsStart(i)) {
4057 model_->ActiveVar(i)->SetValue(0);
4058 }
4059 }
4060 model_->CostVar()->UnfreezeQueue();
4061 return nullptr;
4062 }
4063
4064 private:
4065 RoutingModel* const model_;
4066};
4067} // namespace
4068
4070 return model->solver()->RevAlloc(new AllUnperformed(model));
4071}
4072
4073namespace {
4074// The description is in routing.h:MakeGuidedSlackFinalizer
4075class GuidedSlackFinalizer : public DecisionBuilder {
4076 public:
4077 GuidedSlackFinalizer(const RoutingDimension* dimension, RoutingModel* model,
4078 std::function<int64_t(int64_t)> initializer);
4079 Decision* Next(Solver* solver) override;
4080
4081 private:
4082 int64_t SelectValue(int64_t index);
4083 int64_t ChooseVariable();
4084
4085 const RoutingDimension* const dimension_;
4086 RoutingModel* const model_;
4087 const std::function<int64_t(int64_t)> initializer_;
4088 RevArray<bool> is_initialized_;
4089 std::vector<int64_t> initial_values_;
4090 Rev<int64_t> current_index_;
4091 Rev<int64_t> current_route_;
4092 RevArray<int64_t> last_delta_used_;
4093
4094 DISALLOW_COPY_AND_ASSIGN(GuidedSlackFinalizer);
4095};
4096
4097GuidedSlackFinalizer::GuidedSlackFinalizer(
4098 const RoutingDimension* dimension, RoutingModel* model,
4099 std::function<int64_t(int64_t)> initializer)
4100 : dimension_(ABSL_DIE_IF_NULL(dimension)),
4101 model_(ABSL_DIE_IF_NULL(model)),
4102 initializer_(std::move(initializer)),
4103 is_initialized_(dimension->slacks().size(), false),
4104 initial_values_(dimension->slacks().size(),
4105 std::numeric_limits<int64_t>::min()),
4106 current_index_(model_->Start(0)),
4107 current_route_(0),
4108 last_delta_used_(dimension->slacks().size(), 0) {}
4109
4110Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4111 CHECK_EQ(solver, model_->solver());
4112 const int node_idx = ChooseVariable();
4113 CHECK(node_idx == -1 ||
4114 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
4115 if (node_idx != -1) {
4116 if (!is_initialized_[node_idx]) {
4117 initial_values_[node_idx] = initializer_(node_idx);
4118 is_initialized_.SetValue(solver, node_idx, true);
4119 }
4120 const int64_t value = SelectValue(node_idx);
4121 IntVar* const slack_variable = dimension_->SlackVar(node_idx);
4122 return solver->MakeAssignVariableValue(slack_variable, value);
4123 }
4124 return nullptr;
4125}
4126
4127int64_t GuidedSlackFinalizer::SelectValue(int64_t index) {
4128 const IntVar* const slack_variable = dimension_->SlackVar(index);
4129 const int64_t center = initial_values_[index];
4130 const int64_t max_delta =
4131 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4132 1;
4133 int64_t delta = last_delta_used_[index];
4134
4135 // The sequence of deltas is 0, 1, -1, 2, -2 ...
4136 // Only the values inside the domain of variable are returned.
4137 while (std::abs(delta) < max_delta &&
4138 !slack_variable->Contains(center + delta)) {
4139 if (delta > 0) {
4140 delta = -delta;
4141 } else {
4142 delta = -delta + 1;
4143 }
4144 }
4145 last_delta_used_.SetValue(model_->solver(), index, delta);
4146 return center + delta;
4147}
4148
4149int64_t GuidedSlackFinalizer::ChooseVariable() {
4150 int64_t int_current_node = current_index_.Value();
4151 int64_t int_current_route = current_route_.Value();
4152
4153 while (int_current_route < model_->vehicles()) {
4154 while (!model_->IsEnd(int_current_node) &&
4155 dimension_->SlackVar(int_current_node)->Bound()) {
4156 int_current_node = model_->NextVar(int_current_node)->Value();
4157 }
4158 if (!model_->IsEnd(int_current_node)) {
4159 break;
4160 }
4161 int_current_route += 1;
4162 if (int_current_route < model_->vehicles()) {
4163 int_current_node = model_->Start(int_current_route);
4164 }
4165 }
4166
4167 CHECK(int_current_route == model_->vehicles() ||
4168 !dimension_->SlackVar(int_current_node)->Bound());
4169 current_index_.SetValue(model_->solver(), int_current_node);
4170 current_route_.SetValue(model_->solver(), int_current_route);
4171 if (int_current_route < model_->vehicles()) {
4172 return int_current_node;
4173 } else {
4174 return -1;
4175 }
4176}
4177} // namespace
4178
4179DecisionBuilder* RoutingModel::MakeGuidedSlackFinalizer(
4180 const RoutingDimension* dimension,
4181 std::function<int64_t(int64_t)> initializer) {
4182 return solver_->RevAlloc(
4183 new GuidedSlackFinalizer(dimension, this, std::move(initializer)));
4184}
4185
4186int64_t RoutingDimension::ShortestTransitionSlack(int64_t node) const {
4187 CHECK_EQ(base_dimension_, this);
4188 CHECK(!model_->IsEnd(node));
4189 // Recall that the model is cumul[i+1] = cumul[i] + transit[i] + slack[i]. Our
4190 // aim is to find a value for slack[i] such that cumul[i+1] + transit[i+1] is
4191 // minimized.
4192 const int64_t next = model_->NextVar(node)->Value();
4193 if (model_->IsEnd(next)) {
4194 return SlackVar(node)->Min();
4195 }
4196 const int64_t next_next = model_->NextVar(next)->Value();
4197 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4198 CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value());
4199 const RoutingModel::StateDependentTransit transit_from_next =
4200 model_->StateDependentTransitCallback(
4201 state_dependent_class_evaluators_
4202 [state_dependent_vehicle_to_class_[serving_vehicle]])(next,
4203 next_next);
4204 // We have that transit[i+1] is a function of cumul[i+1].
4205 const int64_t next_cumul_min = CumulVar(next)->Min();
4206 const int64_t next_cumul_max = CumulVar(next)->Max();
4207 const int64_t optimal_next_cumul =
4208 transit_from_next.transit_plus_identity->RangeMinArgument(
4209 next_cumul_min, next_cumul_max + 1);
4210 // A few checks to make sure we're on the same page.
4211 DCHECK_LE(next_cumul_min, optimal_next_cumul);
4212 DCHECK_LE(optimal_next_cumul, next_cumul_max);
4213 // optimal_next_cumul = cumul + transit + optimal_slack, so
4214 // optimal_slack = optimal_next_cumul - cumul - transit.
4215 // In the current implementation TransitVar(i) = transit[i] + slack[i], so we
4216 // have to find the transit from the evaluators.
4217 const int64_t current_cumul = CumulVar(node)->Value();
4218 const int64_t current_state_independent_transit = model_->TransitCallback(
4219 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node, next);
4220 const int64_t current_state_dependent_transit =
4221 model_
4222 ->StateDependentTransitCallback(
4223 state_dependent_class_evaluators_
4224 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4225 next)
4226 .transit->Query(current_cumul);
4227 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4228 current_state_independent_transit -
4229 current_state_dependent_transit;
4230 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4231 CHECK_LE(optimal_slack, SlackVar(node)->Max());
4232 return optimal_slack;
4233}
4234
4235namespace {
4236class GreedyDescentLSOperator : public LocalSearchOperator {
4237 public:
4238 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4239
4240 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta) override;
4241 void Start(const Assignment* assignment) override;
4242
4243 private:
4244 int64_t FindMaxDistanceToDomain(const Assignment* assignment);
4245
4246 const std::vector<IntVar*> variables_;
4247 const Assignment* center_;
4248 int64_t current_step_;
4249 // The deltas are returned in this order:
4250 // (current_step_, 0, ... 0), (-current_step_, 0, ... 0),
4251 // (0, current_step_, ... 0), (0, -current_step_, ... 0),
4252 // ...
4253 // (0, ... 0, current_step_), (0, ... 0, -current_step_).
4254 // current_direction_ keeps track what was the last returned delta.
4255 int64_t current_direction_;
4256
4257 DISALLOW_COPY_AND_ASSIGN(GreedyDescentLSOperator);
4258};
4259
4260GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4261 : variables_(std::move(variables)),
4262 center_(nullptr),
4263 current_step_(0),
4264 current_direction_(0) {}
4265
4266bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment* delta,
4267 Assignment* /*deltadelta*/) {
4268 static const int64_t sings[] = {1, -1};
4269 for (; 1 <= current_step_; current_step_ /= 2) {
4270 for (; current_direction_ < 2 * variables_.size();) {
4271 const int64_t variable_idx = current_direction_ / 2;
4272 IntVar* const variable = variables_[variable_idx];
4273 const int64_t sign_index = current_direction_ % 2;
4274 const int64_t sign = sings[sign_index];
4275 const int64_t offset = sign * current_step_;
4276 const int64_t new_value = center_->Value(variable) + offset;
4277 ++current_direction_;
4278 if (variable->Contains(new_value)) {
4279 delta->Add(variable);
4280 delta->SetValue(variable, new_value);
4281 return true;
4282 }
4283 }
4284 current_direction_ = 0;
4285 }
4286 return false;
4287}
4288
4289void GreedyDescentLSOperator::Start(const Assignment* assignment) {
4290 CHECK(assignment != nullptr);
4291 current_step_ = FindMaxDistanceToDomain(assignment);
4292 center_ = assignment;
4293}
4294
4295int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
4296 const Assignment* assignment) {
4297 int64_t result = std::numeric_limits<int64_t>::min();
4298 for (const IntVar* const var : variables_) {
4299 result = std::max(result, std::abs(var->Max() - assignment->Value(var)));
4300 result = std::max(result, std::abs(var->Min() - assignment->Value(var)));
4301 }
4302 return result;
4303}
4304} // namespace
4305
4306std::unique_ptr<LocalSearchOperator> RoutingModel::MakeGreedyDescentLSOperator(
4307 std::vector<IntVar*> variables) {
4308 return std::unique_ptr<LocalSearchOperator>(
4309 new GreedyDescentLSOperator(std::move(variables)));
4310}
4311
4313 const RoutingDimension* dimension) {
4314 CHECK(dimension != nullptr);
4315 CHECK(dimension->base_dimension() == dimension);
4316 std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t index) {
4317 return dimension->ShortestTransitionSlack(index);
4318 };
4319 DecisionBuilder* const guided_finalizer =
4320 MakeGuidedSlackFinalizer(dimension, slack_guide);
4321 DecisionBuilder* const slacks_finalizer =
4322 solver_->MakeSolveOnce(guided_finalizer);
4323 std::vector<IntVar*> start_cumuls(vehicles_, nullptr);
4324 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
4325 start_cumuls[vehicle_idx] = dimension->CumulVar(starts_[vehicle_idx]);
4326 }
4327 LocalSearchOperator* const hill_climber =
4328 solver_->RevAlloc(new GreedyDescentLSOperator(start_cumuls));
4330 solver_->MakeLocalSearchPhaseParameters(CostVar(), hill_climber,
4331 slacks_finalizer);
4332 Assignment* const first_solution = solver_->MakeAssignment();
4333 first_solution->Add(start_cumuls);
4334 for (IntVar* const cumul : start_cumuls) {
4335 first_solution->SetValue(cumul, cumul->Min());
4336 }
4337 DecisionBuilder* const finalizer =
4338 solver_->MakeLocalSearchPhase(first_solution, parameters);
4339 return finalizer;
4340}
4341} // namespace operations_research
const std::vector< IntVar * > vars_
Definition: alldiff_cst.cc:44
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:491
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:887
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
#define CHECK_GT(val1, val2)
Definition: base/logging.h:703
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:891
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
#define DCHECK(condition)
Definition: base/logging.h:885
#define CHECK_LE(val1, val2)
Definition: base/logging.h:700
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:886
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:41
#define VLOG(verboselevel)
Definition: base/logging.h:979
const std::vector< T * > * Raw() const
bool Contains(const T *val) const
const E & Element(const V *const var) const
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
An Assignment is a variable -> domains mapping, used to report solutions to the user.
void SetValue(const IntVar *const var, int64_t value)
int64_t Value(const IntVar *const var) const
IntVarElement * Add(IntVar *const var)
const IntContainer & IntVarContainer() const
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(const std::vector< int > &vehicles)
Computes and returns the distance of each uninserted node to every vehicle in "vehicles" as a std::ve...
void AppendEvaluatedPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int64_t vehicle, std::vector< ValuedPosition > *valued_positions)
Helper method to the ComputeEvaluatorSortedPositions* methods.
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
void InitializePriorityQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, Queue *priority_queue)
Initializes the priority_queue by inserting the best entry corresponding to each node,...
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
Returns the cost of inserting 'node_to_insert' between 'insert_after' and 'insert_before' on the 'veh...
int64_t GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor)
Inserts 'node' just after 'predecessor', and just before 'successor', resulting in the following subs...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
std::vector< NodeIndex > TravelingSalesmanPath()
Definition: christofides.h:249
void SetMatchingAlgorithm(MatchingAlgorithm matching)
Definition: christofides.h:69
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
static constexpr Value PARALLEL_CHEAPEST_INSERTION
bool operator<(const NodeEntry &other) const
int64_t value() const
int vehicle() const
void set_vehicle(int vehicle)
int insert_after() const
int GetHeapIndex() const
void set_insert_after(int insert_after)
NodeEntry(int node_to_insert, int insert_after, int vehicle)
void SetHeapIndex(int h)
void set_value(int64_t value)
int node_to_insert() const
bool operator<(const PairEntry &other) const
int64_t value() const
int vehicle() const
void set_vehicle(int vehicle)
int GetHeapIndex() const
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle)
int pickup_insert_after() const
void set_pickup_insert_after(int pickup_insert_after)
int delivery_insert_after() const
void SetHeapIndex(int h)
int delivery_to_insert() const
void set_value(int64_t value)
int pickup_to_insert() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
virtual int64_t Min() const =0
virtual int64_t Max() const =0
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
Generic filter-based heuristic applied to IntVars.
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
void ResetSolution()
Resets the data members for a new solution.
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
virtual bool InitializeSolution()
Virtual method to initialize the solution.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
Assignment *const BuildSolution()
Builds a solution.
The class IntVar is a subset of IntExpr.
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
The base class for all local search operators.
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
virtual int64_t RangeMinArgument(int64_t from, int64_t to) const =0
Dimensions represent quantities accumulated at nodes along the routes.
Definition: routing.h:2494
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
Definition: routing.h:2513
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
Definition: routing.h:2747
Filter-based heuristic dedicated to routing.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
virtual void SetVehicleIndex(int64_t node, int vehicle)
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
bool StopSearch() override
Returns true if the search must be stopped.
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
const Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
Manager for any NodeIndex <-> variable index conversion.
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(int64_t index, int64_t max_cardinality, F f) const
Calls f for each variable index of indices in the same disjunctions as the node corresponding to the ...
Definition: routing.h:727
RoutingIndexPair IndexPair
Definition: routing.h:243
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
Definition: routing.h:834
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
Definition: routing.cc:1356
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
Definition: routing.cc:1862
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
Definition: routing.h:1319
int64_t Size() const
Returns the number of next variables in the model.
Definition: routing.h:1460
RoutingIndexPairs IndexPairs
Definition: routing.h:244
int64_t Start(int vehicle) const
Model inspection.
Definition: routing.h:1277
int vehicles() const
Returns the number of vehicle routes in the model.
Definition: routing.h:1458
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64_t(int64_t)> initializer)
The next few members are in the public section only for testing purposes.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
Definition: routing.h:1321
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
Definition: routing.cc:3484
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64_t node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
Definition: routing.cc:1856
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
Definition: routing.h:1283
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
Definition: routing.h:1366
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
Definition: routing.h:1349
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
Definition: routing.h:1279
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
void InitializeContainer(int64_t size, int64_t saving_neighbors)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
std::unique_ptr< SavingsContainer< Saving > > savings_container_
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
Finds the best available vehicle of type "type" to start a new route to serve the arc before_node-->a...
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
SweepArranger(const std::vector< std::pair< int64_t, int64_t > > &points)
void ArrangeIndices(std::vector< int64_t > *indices)
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
std::pair< int, int > GetCompatibleVehicleOfType(int type, std::function< bool(int)> vehicle_is_compatible, std::function< bool(int)> stop_and_return_vehicle)
Searches for the best compatible vehicle of the given type, i.e.
void Reset(const std::function< bool(int)> &store_vehicle)
Resets the vehicles stored, storing only vehicles from the vehicle_type_container_ for which store_ve...
Block * next
SatParameters parameters
IntVar * var
Definition: expr_array.cc:1874
const std::vector< IntVar * > cumuls_
GRBmodel * model
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition: macros.h:29
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
void STLClearObject(T *obj)
Definition: stl_util.h:123
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
int64_t CapSub(int64_t x, int64_t y)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
static const int kUnassigned
Definition: routing.cc:702
STL namespace.
int64_t delta
Definition: resource.cc:1692
int64_t cost
int vehicle_class
int head
std::pair< int, int > link
int nodes
double distance
double angle
int64_t start_depot
int64_t index
int64_t value
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
int64_t end_depot
int tail
std::function< int64_t(int64_t, int64_t)> evaluator_
Definition: search.cc:1368
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
What follows is relevant for models with time/state dependent transits.
Definition: routing.h:260
RangeMinMaxIndexFunction * transit_plus_identity
f(x)
Definition: routing.h:262
Definition: routing.h:355
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
Definition: routing.h:374
std::vector< std::deque< int > > vehicles_per_vehicle_class
Definition: routing.h:375
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.