37 #include "absl/base/attributes.h" 38 #include "absl/container/flat_hash_map.h" 39 #include "absl/container/flat_hash_set.h" 40 #include "absl/flags/flag.h" 41 #include "absl/memory/memory.h" 42 #include "absl/meta/type_traits.h" 43 #include "absl/strings/str_cat.h" 44 #include "absl/strings/string_view.h" 63 class LocalSearchPhaseParameters;
66 ABSL_FLAG(
bool, routing_shift_insertion_cost_by_penalty,
true,
67 "Shift insertion costs by the penalty of the inserted node(s).");
70 "The number of sectors the space is divided into before it is sweeped" 78 const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
80 sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
81 const std::vector<std::deque<int>>& all_vehicles_per_class =
83 vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
85 for (
int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
86 std::set<VehicleClassEntry>& stored_class_entries =
87 sorted_vehicle_classes_per_type_[type];
88 stored_class_entries.clear();
91 std::vector<int>& stored_vehicles =
93 stored_vehicles.clear();
95 if (store_vehicle(vehicle)) {
96 stored_vehicles.push_back(vehicle);
99 if (!stored_vehicles.empty()) {
100 stored_class_entries.insert(class_entry);
107 const std::function<
bool(
int)>& remove_vehicle) {
108 for (std::set<VehicleClassEntry>& class_entries :
109 sorted_vehicle_classes_per_type_) {
110 auto class_entry_it = class_entries.begin();
111 while (class_entry_it != class_entries.end()) {
113 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
114 vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
115 [&remove_vehicle](
int vehicle) {
116 return remove_vehicle(vehicle);
119 if (vehicles.empty()) {
120 class_entry_it = class_entries.erase(class_entry_it);
129 int type,
const std::function<
bool(
int)>& vehicle_is_compatible)
const {
131 sorted_vehicle_classes_per_type_[type]) {
133 vehicles_per_vehicle_class_[vehicle_class_entry.vehicle_class]) {
134 if (vehicle_is_compatible(vehicle))
return true;
141 int type, std::function<
bool(
int)> vehicle_is_compatible,
142 std::function<
bool(
int)> stop_and_return_vehicle) {
143 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
144 sorted_vehicle_classes_per_type_[type];
145 auto vehicle_class_it = sorted_classes.begin();
147 while (vehicle_class_it != sorted_classes.end()) {
149 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
150 DCHECK(!vehicles.empty());
152 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
154 const int vehicle = *vehicle_it;
155 if (vehicle_is_compatible(vehicle)) {
156 vehicles.erase(vehicle_it);
157 if (vehicles.empty()) {
158 sorted_classes.erase(vehicle_class_it);
160 return {vehicle, -1};
162 if (stop_and_return_vehicle(vehicle)) {
163 return {-1, vehicle};
182 bool has_pickup_deliveries,
bool has_node_precedences,
183 bool has_single_vehicle_node) {
184 if (has_pickup_deliveries || has_node_precedences) {
187 if (has_single_vehicle_node) {
198 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
199 : heuristic_(std::move(heuristic)) {}
202 Assignment*
const assignment = heuristic_->BuildSolution();
203 if (assignment !=
nullptr) {
204 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
205 VLOG(2) <<
"Number of rejected decisions: " 206 << heuristic_->number_of_rejects();
215 return heuristic_->number_of_decisions();
219 return heuristic_->number_of_rejects();
223 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
224 heuristic_->DebugString(),
")");
232 Solver* solver,
const std::vector<IntVar*>& vars,
234 : assignment_(solver->MakeAssignment()),
237 delta_(solver->MakeAssignment()),
238 is_in_delta_(
vars_.size(), false),
239 empty_(solver->MakeAssignment()),
240 filter_manager_(filter_manager),
241 number_of_decisions_(0),
242 number_of_rejects_(0) {
244 delta_indices_.reserve(vars_.size());
248 number_of_decisions_ = 0;
249 number_of_rejects_ = 0;
270 const std::function<int64_t(int64_t)>& next_accessor) {
275 start_chain_ends_.resize(
model()->vehicles());
276 end_chain_starts_.resize(
model()->vehicles());
278 for (
int v = 0; v < model_->
vehicles(); v++) {
279 int64_t node = model_->
Start(v);
280 while (!model_->
IsEnd(node)) {
281 const int64_t
next = next_accessor(node);
290 end_chain_starts_[v] =
model()->
End(v);
303 ++number_of_decisions_;
304 const bool accept = FilterAccept();
307 const int delta_size = delta_container.
Size();
310 for (
int i = 0; i < delta_size; ++i) {
319 ++number_of_rejects_;
322 for (
const int delta_index : delta_indices_) {
323 is_in_delta_[delta_index] =
false;
326 delta_indices_.clear();
334 bool IntVarFilteredHeuristic::FilterAccept() {
335 if (!filter_manager_)
return true;
337 return filter_manager_->
Accept(monitor, delta_, empty_,
349 bool RoutingFilteredHeuristic::InitializeSolution() {
359 start_chain_ends_.clear();
360 start_chain_ends_.resize(
model()->vehicles(), -1);
361 end_chain_starts_.clear();
362 end_chain_starts_.resize(
model()->vehicles(), -1);
365 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
367 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
373 start_chain_ends_[vehicle] = node;
376 std::vector<int64_t> starts(
Size() +
model()->vehicles(), -1);
377 std::vector<int64_t> ends(
Size() +
model()->vehicles(), -1);
383 std::vector<bool> touched(
Size(),
false);
384 for (
int node = 0; node <
Size(); ++node) {
386 while (!
model()->
IsEnd(current) && !touched[current]) {
387 touched[current] =
true;
388 IntVar*
const next_var =
Var(current);
389 if (next_var->Bound()) {
390 current = next_var->Value();
395 starts[ends[current]] = starts[node];
396 ends[starts[node]] = ends[current];
401 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
402 end_chain_starts_[vehicle] = starts[
model()->
End(vehicle)];
403 int64_t node = start_chain_ends_[vehicle];
404 if (!
model()->IsEnd(node)) {
409 while (!
model()->IsEnd(node)) {
427 node, 1, [
this, node](
int alternate) {
428 if (node != alternate && !
Contains(alternate)) {
443 std::vector<bool> to_make_unperformed(
Size(),
false);
444 for (
const auto& [pickups, deliveries] :
445 model()->GetPickupAndDeliveryPairs()) {
446 int64_t performed_pickup = -1;
447 for (int64_t pickup : pickups) {
449 performed_pickup = pickup;
453 int64_t performed_delivery = -1;
454 for (int64_t delivery : deliveries) {
456 performed_delivery = delivery;
460 if ((performed_pickup == -1) != (performed_delivery == -1)) {
461 if (performed_pickup != -1) {
462 to_make_unperformed[performed_pickup] =
true;
464 if (performed_delivery != -1) {
465 to_make_unperformed[performed_delivery] =
true;
473 const int64_t next_of_next =
Value(
next);
485 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
486 std::function<int64_t(int64_t)> penalty_evaluator,
490 penalty_evaluator_(std::move(penalty_evaluator)) {}
492 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
494 const std::vector<int>& vehicles) {
495 const absl::flat_hash_set<int> vehicle_set(vehicles.begin(), vehicles.end());
496 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
499 for (
int node = 0; node <
model()->
Size(); node++) {
501 std::vector<StartEndValue>& start_end_distances =
502 start_end_distances_per_node[node];
504 const int64_t num_allowed_vehicles = vehicle_var->
Size();
506 const auto add_distance = [
this, node, num_allowed_vehicles,
507 &start_end_distances](
int vehicle) {
508 const int64_t start =
model()->
Start(vehicle);
509 const int64_t end =
model()->
End(vehicle);
513 CapAdd(
model()->GetArcCostForVehicle(start, node, vehicle),
514 model()->GetArcCostForVehicle(node, end, vehicle));
515 start_end_distances.push_back({num_allowed_vehicles,
distance, vehicle});
520 if (num_allowed_vehicles < vehicles.size()) {
521 std::unique_ptr<IntVarIterator> it(
524 if (vehicle < 0 || !vehicle_set.contains(vehicle))
continue;
525 add_distance(vehicle);
528 for (
const int vehicle : vehicles) {
529 if (!vehicle_var->
Contains(vehicle))
continue;
530 add_distance(vehicle);
535 std::sort(start_end_distances.begin(), start_end_distances.end(),
537 return second < first;
540 return start_end_distances_per_node;
543 template <
class Queue>
545 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
546 Queue* priority_queue) {
548 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
550 for (
int node = 0; node < num_nodes; node++) {
552 std::vector<StartEndValue>& start_end_distances =
553 (*start_end_distances_per_node)[node];
554 if (start_end_distances.empty()) {
558 const StartEndValue& start_end_value = start_end_distances.back();
559 priority_queue->push(std::make_pair(start_end_value, node));
560 start_end_distances.pop_back();
573 int64_t node_to_insert, int64_t start, int64_t next_after_start,
574 int vehicle, std::vector<NodeInsertion>* node_insertions) {
575 DCHECK(node_insertions !=
nullptr);
576 int64_t insert_after = start;
577 while (!
model()->IsEnd(insert_after)) {
578 const int64_t insert_before =
579 (insert_after == start) ? next_after_start :
Value(insert_after);
580 node_insertions->push_back(
581 {insert_after, vehicle,
583 insert_before, vehicle)});
584 insert_after = insert_before;
589 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
592 evaluator_(node_to_insert, insert_before, vehicle)),
593 evaluator_(insert_after, insert_before, vehicle));
597 int64_t node_to_insert)
const {
606 void SortAndExtractPairSeconds(std::vector<std::pair<int64_t, T>>* pairs,
607 std::vector<T>* sorted_seconds) {
608 CHECK(pairs !=
nullptr);
609 CHECK(sorted_seconds !=
nullptr);
610 std::sort(pairs->begin(), pairs->end());
611 sorted_seconds->reserve(pairs->size());
612 for (
const std::pair<int64_t, T>& p : *pairs) {
613 sorted_seconds->push_back(p.second);
626 : value_(std::numeric_limits<int64_t>::
max()),
638 if (bucket_ != other.bucket_) {
639 return bucket_ > other.bucket_;
643 if (value_ != other.value_) {
644 return value_ > other.value_;
646 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
647 return vehicle_ == -1;
649 return std::tie(pickup_insert_after_, pickup_to_insert_,
650 delivery_insert_after_, delivery_to_insert_, vehicle_) >
651 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
652 other.delivery_insert_after_, other.delivery_to_insert_,
671 const int pickup_to_insert_;
672 int pickup_insert_after_;
673 const int delivery_to_insert_;
674 const int delivery_insert_after_;
676 const int64_t bucket_;
683 : value_(std::numeric_limits<int64_t>::
max()),
691 if (bucket_ != other.bucket_) {
692 return bucket_ > other.bucket_;
694 if (value_ != other.value_) {
695 return value_ > other.value_;
697 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
698 return vehicle_ == -1;
700 return std::tie(insert_after_, node_to_insert_, vehicle_) >
701 std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
715 const int node_to_insert_;
718 const int64_t bucket_;
726 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
727 std::function<int64_t(int64_t)> penalty_evaluator,
731 std::move(penalty_evaluator),
734 node_index_to_vehicle_(
model->Size(), -1),
735 empty_vehicle_type_curator_(nullptr) {
740 if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
749 std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
753 void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
755 !node_index_to_neighbors_by_cost_class_.empty()) {
761 const int64_t num_neighbors = NumNeighbors();
764 DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
766 const RoutingModel& routing_model = *
model();
767 const int64_t size = routing_model.Size();
768 node_index_to_neighbors_by_cost_class_.resize(size);
769 const int num_cost_classes = routing_model.GetCostClassesCount();
770 for (int64_t node_index = 0; node_index < size; node_index++) {
771 node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
772 for (
int cc = 0; cc < num_cost_classes; cc++) {
773 node_index_to_neighbors_by_cost_class_[node_index][cc] =
774 absl::make_unique<SparseBitset<int64_t>>(size);
778 for (int64_t node_index = 0; node_index < size; ++node_index) {
779 DCHECK(!routing_model.IsEnd(node_index));
780 if (routing_model.IsStart(node_index)) {
787 for (
int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
788 if (!routing_model.HasVehicleWithCostClassIndex(
789 RoutingCostClassIndex(cost_class))) {
793 std::vector<std::pair< int64_t, int64_t>>
795 costed_after_nodes.reserve(size);
796 for (
int after_node = 0; after_node < size; ++after_node) {
797 if (after_node != node_index && !routing_model.IsStart(after_node)) {
798 costed_after_nodes.push_back(
799 std::make_pair(routing_model.GetArcCostForClass(
800 node_index, after_node, cost_class),
804 std::nth_element(costed_after_nodes.begin(),
805 costed_after_nodes.begin() + num_neighbors - 1,
806 costed_after_nodes.end());
807 costed_after_nodes.resize(num_neighbors);
809 for (
auto [
cost, neighbor] : costed_after_nodes) {
810 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
814 DCHECK(!routing_model.IsEnd(neighbor) &&
815 !routing_model.IsStart(neighbor));
816 node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
820 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
821 const int64_t vehicle_start = routing_model.Start(vehicle);
822 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
824 node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
831 bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
832 int cost_class, int64_t node_index, int64_t neighbor_index)
const {
834 (*node_index_to_neighbors_by_cost_class_[node_index]
835 [cost_class])[neighbor_index];
838 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
839 std::vector<bool> node_is_visited(
model()->
Size(), -1);
842 node =
Value(node)) {
843 if (node_index_to_vehicle_[node] != v) {
846 node_is_visited[node] =
true;
850 for (
int node = 0; node <
model()->
Size(); node++) {
851 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
860 ComputeNeighborhoods();
861 if (empty_vehicle_type_curator_ ==
nullptr) {
862 empty_vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
863 model()->GetVehicleTypeContainer());
866 empty_vehicle_type_curator_->Reset(
871 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
872 absl::flat_hash_map<int, std::map<int64_t, std::vector<int>>>
873 vehicle_to_pair_nodes;
876 int pickup_vehicle = -1;
877 for (int64_t pickup : index_pair.first) {
879 pickup_vehicle = node_index_to_vehicle_[pickup];
883 int delivery_vehicle = -1;
884 for (int64_t delivery : index_pair.second) {
886 delivery_vehicle = node_index_to_vehicle_[delivery];
890 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
891 pairs_to_insert_by_bucket[GetBucketOfPair(index_pair)].push_back(
index);
893 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
894 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle][1];
895 for (int64_t delivery : index_pair.second) {
896 pair_nodes.push_back(delivery);
899 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
900 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle][1];
901 for (int64_t pickup : index_pair.first) {
902 pair_nodes.push_back(pickup);
906 for (
const auto& [vehicle,
nodes] : vehicle_to_pair_nodes) {
907 if (!InsertNodesOnRoutes(
nodes, {vehicle}))
return false;
910 if (!InsertPairsAndNodesByRequirementTopologicalOrder())
return false;
914 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
915 std::map<int64_t, std::vector<int>> nodes_by_bucket;
916 for (
int node = 0; node <
model()->
Size(); ++node) {
919 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
922 InsertFarthestNodesAsSeeds();
924 if (!SequentialInsertNodes(nodes_by_bucket))
return false;
925 }
else if (!InsertNodesOnRoutes(nodes_by_bucket, {})) {
929 DCHECK(CheckVehicleIndices());
933 bool GlobalCheapestInsertionFilteredHeuristic::
934 InsertPairsAndNodesByRequirementTopologicalOrder() {
937 for (
const std::vector<int>& types :
938 model()->GetTopologicallySortedVisitTypes()) {
939 for (
int type : types) {
940 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
941 for (
int index :
model()->GetPairIndicesOfType(type)) {
942 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pairs[
index])]
945 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
946 std::map<int64_t, std::vector<int>> nodes_by_bucket;
947 for (
int node :
model()->GetSingleNodesOfType(type)) {
948 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
950 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
956 bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
957 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket) {
959 std::vector<PairEntries> pickup_to_entries;
960 std::vector<PairEntries> delivery_to_entries;
961 std::vector<int> pair_indices_to_insert;
962 for (
const auto& [bucket, pair_indices] : pair_indices_by_bucket) {
963 pair_indices_to_insert.insert(pair_indices_to_insert.end(),
964 pair_indices.begin(), pair_indices.end());
965 if (!InitializePairPositions(pair_indices_to_insert, &priority_queue,
966 &pickup_to_entries, &delivery_to_entries)) {
969 while (!priority_queue.
IsEmpty()) {
970 if (StopSearchAndCleanup(&priority_queue)) {
973 PairEntry*
const entry = priority_queue.
Top();
975 const int64_t delivery = entry->delivery_to_insert();
977 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
978 &delivery_to_entries);
982 const int entry_vehicle = entry->vehicle();
983 if (entry_vehicle == -1) {
988 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
989 &delivery_to_entries);
995 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle)) {
996 if (!InsertPairEntryUsingEmptyVehicleTypeCurator(
997 pair_indices_to_insert, entry, &priority_queue,
998 &pickup_to_entries, &delivery_to_entries)) {
1006 const int64_t pickup_insert_after = entry->pickup_insert_after();
1007 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1008 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
1010 const int64_t delivery_insert_after = entry->delivery_insert_after();
1011 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1012 ? pickup_insert_before
1013 :
Value(delivery_insert_after);
1014 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
1016 if (!UpdateAfterPairInsertion(
1017 pair_indices_to_insert, entry_vehicle, pickup,
1018 pickup_insert_after, delivery, delivery_insert_after,
1019 &priority_queue, &pickup_to_entries, &delivery_to_entries)) {
1023 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1024 &delivery_to_entries);
1031 pair_indices_to_insert.erase(
1033 pair_indices_to_insert.begin(), pair_indices_to_insert.end(),
1034 [
this, &pickup_delivery_pairs](
int pair_index) {
1038 for (int64_t pickup : pickup_delivery_pairs[pair_index].first) {
1043 pair_indices_to_insert.end());
1048 bool GlobalCheapestInsertionFilteredHeuristic::
1049 InsertPairEntryUsingEmptyVehicleTypeCurator(
1050 const std::vector<int>& pair_indices,
1051 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1053 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1055 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1057 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1058 delivery_to_entries) {
1059 const int entry_vehicle = pair_entry->vehicle();
1060 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle));
1066 const int64_t pickup = pair_entry->pickup_to_insert();
1067 const int64_t delivery = pair_entry->delivery_to_insert();
1068 const int64_t entry_fixed_cost =
1070 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
1071 delivery](
int vehicle) {
1077 const int64_t end =
model()->
End(vehicle);
1086 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1089 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1090 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1091 empty_vehicle_type_curator_->Type(entry_vehicle),
1092 vehicle_is_compatible, stop_and_return_vehicle);
1093 if (compatible_vehicle >= 0) {
1095 const int64_t vehicle_start =
model()->
Start(compatible_vehicle);
1096 const int num_previous_vehicle_entries =
1097 pickup_to_entries->at(vehicle_start).size() +
1098 delivery_to_entries->at(vehicle_start).size();
1099 if (!UpdateAfterPairInsertion(
1100 pair_indices, compatible_vehicle, pickup, vehicle_start, delivery,
1101 pickup, priority_queue, pickup_to_entries, delivery_to_entries)) {
1104 if (compatible_vehicle != entry_vehicle) {
1110 DCHECK_EQ(num_previous_vehicle_entries, 0);
1116 const int new_empty_vehicle =
1117 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1118 empty_vehicle_type_curator_->Type(compatible_vehicle));
1120 if (new_empty_vehicle >= 0) {
1124 UpdatePairPositions(pair_indices, new_empty_vehicle,
1125 model()->Start(new_empty_vehicle), priority_queue,
1126 pickup_to_entries, delivery_to_entries);
1128 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1135 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1136 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1137 pair_entry->set_pickup_insert_after(
1138 model()->Start(next_fixed_cost_empty_vehicle));
1139 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1140 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1141 UpdatePairEntry(pair_entry, priority_queue);
1143 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1144 delivery_to_entries);
1150 bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1151 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
1152 const absl::flat_hash_set<int>& vehicles) {
1154 std::vector<NodeEntries> position_to_node_entries;
1155 std::vector<int> nodes_to_insert;
1156 for (
const auto& [bucket,
nodes] : nodes_by_bucket) {
1157 nodes_to_insert.insert(nodes_to_insert.end(),
nodes.begin(),
nodes.end());
1158 if (!InitializePositions(nodes_to_insert, vehicles, &priority_queue,
1159 &position_to_node_entries)) {
1168 const bool all_vehicles =
1171 while (!priority_queue.
IsEmpty()) {
1172 NodeEntry*
const node_entry = priority_queue.
Top();
1173 if (StopSearchAndCleanup(&priority_queue)) {
1178 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1182 const int entry_vehicle = node_entry->vehicle();
1183 if (entry_vehicle == -1) {
1186 SetValue(node_to_insert, node_to_insert);
1188 DeleteNodeEntry(node_entry, &priority_queue,
1189 &position_to_node_entries);
1195 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles)) {
1197 if (!InsertNodeEntryUsingEmptyVehicleTypeCurator(
1198 nodes_to_insert, all_vehicles, node_entry, &priority_queue,
1199 &position_to_node_entries)) {
1205 const int64_t insert_after = node_entry->insert_after();
1208 if (!UpdatePositions(nodes_to_insert, entry_vehicle, node_to_insert,
1209 all_vehicles, &priority_queue,
1210 &position_to_node_entries) ||
1211 !UpdatePositions(nodes_to_insert, entry_vehicle, insert_after,
1212 all_vehicles, &priority_queue,
1213 &position_to_node_entries)) {
1216 SetVehicleIndex(node_to_insert, entry_vehicle);
1218 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1223 nodes_to_insert.erase(
1224 std::remove_if(nodes_to_insert.begin(), nodes_to_insert.end(),
1225 [
this](
int node) {
return Contains(node); }),
1226 nodes_to_insert.end());
1231 bool GlobalCheapestInsertionFilteredHeuristic::
1232 InsertNodeEntryUsingEmptyVehicleTypeCurator(
1233 const std::vector<int>&
nodes,
bool all_vehicles,
1234 GlobalCheapestInsertionFilteredHeuristic::NodeEntry*
const node_entry,
1236 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1238 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1239 position_to_node_entries) {
1240 const int entry_vehicle = node_entry->vehicle();
1241 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles));
1248 const int64_t node_to_insert = node_entry->node_to_insert();
1249 const int64_t entry_fixed_cost =
1251 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1252 node_to_insert](
int vehicle) {
1259 model()->End(vehicle));
1266 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1269 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1270 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1271 empty_vehicle_type_curator_->Type(entry_vehicle),
1272 vehicle_is_compatible, stop_and_return_vehicle);
1273 if (compatible_vehicle >= 0) {
1275 if (!UpdatePositions(
nodes, compatible_vehicle, node_to_insert,
1276 all_vehicles, priority_queue,
1277 position_to_node_entries)) {
1280 const int64_t compatible_start =
model()->
Start(compatible_vehicle);
1281 const bool no_prior_entries_for_this_vehicle =
1282 position_to_node_entries->at(compatible_start).empty();
1283 if (!UpdatePositions(
nodes, compatible_vehicle, compatible_start,
1284 all_vehicles, priority_queue,
1285 position_to_node_entries)) {
1288 SetVehicleIndex(node_to_insert, compatible_vehicle);
1289 if (compatible_vehicle != entry_vehicle) {
1295 DCHECK(no_prior_entries_for_this_vehicle);
1301 const int new_empty_vehicle =
1302 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1303 empty_vehicle_type_curator_->Type(compatible_vehicle));
1305 if (new_empty_vehicle >= 0) {
1309 if (!UpdatePositions(
nodes, new_empty_vehicle,
1310 model()->Start(new_empty_vehicle), all_vehicles,
1311 priority_queue, position_to_node_entries)) {
1315 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1322 position_to_node_entries->at(node_entry->insert_after()).erase(node_entry);
1323 node_entry->set_insert_after(
model()->Start(next_fixed_cost_empty_vehicle));
1324 position_to_node_entries->at(node_entry->insert_after()).insert(node_entry);
1325 node_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1326 UpdateNodeEntry(node_entry, priority_queue);
1328 DeleteNodeEntry(node_entry, priority_queue, position_to_node_entries);
1334 bool GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1335 const std::map<int64_t, std::vector<int>>& nodes_by_bucket) {
1336 std::vector<bool> is_vehicle_used;
1337 absl::flat_hash_set<int> used_vehicles;
1338 std::vector<int> unused_vehicles;
1340 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1341 if (!used_vehicles.empty() &&
1342 !InsertNodesOnRoutes(nodes_by_bucket, used_vehicles)) {
1346 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1348 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1352 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1355 while (vehicle >= 0) {
1356 if (!InsertNodesOnRoutes(nodes_by_bucket, {vehicle})) {
1359 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1365 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1366 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1367 absl::flat_hash_set<int>* used_vehicles) {
1368 is_vehicle_used->clear();
1369 is_vehicle_used->resize(
model()->vehicles());
1371 used_vehicles->clear();
1372 used_vehicles->reserve(
model()->vehicles());
1374 unused_vehicles->clear();
1375 unused_vehicles->reserve(
model()->vehicles());
1377 for (
int vehicle = 0; vehicle <
model()->
vehicles(); vehicle++) {
1379 (*is_vehicle_used)[vehicle] =
true;
1380 used_vehicles->insert(vehicle);
1382 (*is_vehicle_used)[vehicle] =
false;
1383 unused_vehicles->push_back(vehicle);
1388 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1391 const int num_seeds = static_cast<int>(
1394 std::vector<bool> is_vehicle_used;
1395 absl::flat_hash_set<int> used_vehicles;
1396 std::vector<int> unused_vehicles;
1397 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1398 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1403 std::priority_queue<Seed> farthest_node_queue;
1406 int inserted_seeds = 0;
1407 while (inserted_seeds++ < num_seeds) {
1408 if (InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1409 &is_vehicle_used) < 0) {
1418 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1419 empty_vehicle_type_curator_->Update(
1423 template <
class Queue>
1424 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1425 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1426 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
1427 while (!priority_queue->empty()) {
1429 const Seed& seed = priority_queue->top();
1431 const int seed_node = seed.second;
1432 const int seed_vehicle = seed.first.vehicle;
1434 std::vector<StartEndValue>& other_start_end_values =
1435 (*start_end_distances_per_node)[seed_node];
1440 priority_queue->pop();
1441 other_start_end_values.clear();
1444 if (!(*is_vehicle_used)[seed_vehicle]) {
1446 const int64_t start =
model()->
Start(seed_vehicle);
1447 const int64_t end =
model()->
End(seed_vehicle);
1451 priority_queue->pop();
1452 (*is_vehicle_used)[seed_vehicle] =
true;
1453 other_start_end_values.clear();
1454 SetVehicleIndex(seed_node, seed_vehicle);
1455 return seed_vehicle;
1462 priority_queue->pop();
1463 if (!other_start_end_values.empty()) {
1464 const StartEndValue& next_seed_value = other_start_end_values.back();
1465 priority_queue->push(std::make_pair(next_seed_value, seed_node));
1466 other_start_end_values.pop_back();
1473 bool GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1474 const std::vector<int>& pair_indices,
1476 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1477 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1479 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1480 delivery_to_entries) {
1481 priority_queue->Clear();
1482 pickup_to_entries->clear();
1483 pickup_to_entries->resize(
model()->
Size());
1484 delivery_to_entries->clear();
1485 delivery_to_entries->resize(
model()->
Size());
1488 for (
int index : pair_indices) {
1490 for (int64_t pickup : index_pair.first) {
1492 for (int64_t delivery : index_pair.second) {
1494 if (StopSearchAndCleanup(priority_queue))
return false;
1500 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1505 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1509 InitializeInsertionEntriesPerformingPair(
1510 pickup, delivery, priority_queue, pickup_to_entries,
1511 delivery_to_entries);
1518 void GlobalCheapestInsertionFilteredHeuristic::
1519 InitializeInsertionEntriesPerformingPair(
1520 int64_t pickup, int64_t delivery,
1522 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1524 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1526 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1527 delivery_to_entries) {
1529 struct PairInsertion {
1530 int64_t insert_pickup_after;
1531 int64_t insert_delivery_after;
1534 std::vector<PairInsertion> pair_insertions;
1535 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
1537 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1538 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1543 const int64_t start =
model()->
Start(vehicle);
1544 std::vector<NodeInsertion> pickup_insertions;
1546 &pickup_insertions);
1547 for (
const auto [insert_pickup_after, pickup_vehicle,
1548 unused_pickup_value] : pickup_insertions) {
1550 std::vector<NodeInsertion> delivery_insertions;
1552 Value(insert_pickup_after), vehicle,
1553 &delivery_insertions);
1554 for (
const auto [insert_delivery_after, delivery_vehicle,
1555 unused_delivery_value] : delivery_insertions) {
1556 pair_insertions.push_back(
1557 {insert_pickup_after, insert_delivery_after, vehicle});
1561 for (
const auto& [insert_pickup_after, insert_delivery_after, vehicle] :
1563 DCHECK_NE(insert_pickup_after, insert_delivery_after);
1564 AddPairEntry(pickup, insert_pickup_after, delivery, insert_delivery_after,
1565 vehicle, priority_queue, pickup_to_entries,
1566 delivery_to_entries);
1575 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1576 existing_insertion_positions;
1578 for (
const int64_t pickup_insert_after :
1579 GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
1580 if (!
Contains(pickup_insert_after)) {
1583 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1590 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1591 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1597 int64_t delivery_insert_after = pickup;
1598 while (!
model()->
IsEnd(delivery_insert_after)) {
1599 const std::pair<int64_t, int64_t> insertion_position = {
1600 pickup_insert_after, delivery_insert_after};
1601 DCHECK(!existing_insertion_positions.contains(insertion_position));
1602 existing_insertion_positions.insert(insertion_position);
1604 AddPairEntry(pickup, pickup_insert_after, delivery,
1605 delivery_insert_after, vehicle, priority_queue,
1606 pickup_to_entries, delivery_to_entries);
1607 delivery_insert_after = (delivery_insert_after == pickup)
1608 ?
Value(pickup_insert_after)
1609 :
Value(delivery_insert_after);
1614 for (
const int64_t delivery_insert_after :
1615 GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
1616 if (!
Contains(delivery_insert_after)) {
1619 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1621 model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
1630 int64_t pickup_insert_after =
model()->
Start(vehicle);
1631 while (pickup_insert_after != delivery_insert_after) {
1632 if (!existing_insertion_positions.contains(
1633 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1634 AddPairEntry(pickup, pickup_insert_after, delivery,
1635 delivery_insert_after, vehicle, priority_queue,
1636 pickup_to_entries, delivery_to_entries);
1638 pickup_insert_after =
Value(pickup_insert_after);
1644 bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1645 const std::vector<int>& pair_indices,
int vehicle, int64_t pickup,
1646 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1648 std::vector<PairEntries>* pickup_to_entries,
1649 std::vector<PairEntries>* delivery_to_entries) {
1650 if (!UpdatePairPositions(pair_indices, vehicle, pickup_position,
1651 priority_queue, pickup_to_entries,
1652 delivery_to_entries) ||
1653 !UpdatePairPositions(pair_indices, vehicle, pickup, priority_queue,
1654 pickup_to_entries, delivery_to_entries) ||
1655 !UpdatePairPositions(pair_indices, vehicle, delivery, priority_queue,
1656 pickup_to_entries, delivery_to_entries)) {
1659 if (delivery_position != pickup &&
1660 !UpdatePairPositions(pair_indices, vehicle, delivery_position,
1661 priority_queue, pickup_to_entries,
1662 delivery_to_entries)) {
1665 SetVehicleIndex(pickup, vehicle);
1666 SetVehicleIndex(delivery, vehicle);
1670 bool GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
1671 const std::vector<int>& pair_indices,
int vehicle,
1672 int64_t pickup_insert_after,
1674 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1675 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1677 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1678 delivery_to_entries) {
1681 using Pair = std::pair<int64_t, int64_t>;
1682 using Insertion = std::pair<Pair, int64_t>;
1683 absl::flat_hash_set<Insertion> existing_insertions;
1684 std::vector<PairEntry*> to_remove;
1685 for (PairEntry*
const pair_entry :
1686 pickup_to_entries->at(pickup_insert_after)) {
1688 DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
1689 if (
Contains(pair_entry->pickup_to_insert()) ||
1690 Contains(pair_entry->delivery_to_insert())) {
1691 to_remove.push_back(pair_entry);
1693 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1694 .contains(pair_entry));
1695 UpdatePairEntry(pair_entry, priority_queue);
1696 existing_insertions.insert(
1697 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1698 pair_entry->delivery_insert_after()});
1701 for (PairEntry*
const pair_entry : to_remove) {
1702 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1703 delivery_to_entries);
1708 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1711 for (
int pair_index : pair_indices) {
1712 if (StopSearchAndCleanup(priority_queue))
return false;
1714 pickup_delivery_pairs[pair_index];
1715 for (int64_t pickup : index_pair.first) {
1717 !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
1720 for (int64_t delivery : index_pair.second) {
1724 int64_t delivery_insert_after = pickup;
1725 while (!
model()->IsEnd(delivery_insert_after)) {
1726 const Insertion insertion = {{pickup, delivery},
1727 delivery_insert_after};
1728 if (!existing_insertions.contains(insertion)) {
1729 AddPairEntry(pickup, pickup_insert_after, delivery,
1730 delivery_insert_after, vehicle, priority_queue,
1731 pickup_to_entries, delivery_to_entries);
1733 if (delivery_insert_after == pickup) {
1734 delivery_insert_after = pickup_insert_before;
1736 delivery_insert_after =
Value(delivery_insert_after);
1745 bool GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
1746 const std::vector<int>& pair_indices,
int vehicle,
1747 int64_t delivery_insert_after,
1749 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1750 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1752 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1753 delivery_to_entries) {
1756 using Pair = std::pair<int64_t, int64_t>;
1757 using Insertion = std::pair<Pair, int64_t>;
1758 absl::flat_hash_set<Insertion> existing_insertions;
1759 std::vector<PairEntry*> to_remove;
1760 for (PairEntry*
const pair_entry :
1761 delivery_to_entries->at(delivery_insert_after)) {
1763 DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
1764 if (
Contains(pair_entry->pickup_to_insert()) ||
1765 Contains(pair_entry->delivery_to_insert())) {
1766 to_remove.push_back(pair_entry);
1768 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1769 .contains(pair_entry));
1770 existing_insertions.insert(
1771 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1772 pair_entry->pickup_insert_after()});
1773 UpdatePairEntry(pair_entry, priority_queue);
1776 for (PairEntry*
const pair_entry : to_remove) {
1777 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1778 delivery_to_entries);
1785 for (
int pair_index : pair_indices) {
1786 if (StopSearchAndCleanup(priority_queue))
return false;
1788 pickup_delivery_pairs[pair_index];
1789 for (int64_t delivery : index_pair.second) {
1791 !IsNeighborForCostClass(cost_class, delivery_insert_after,
1795 for (int64_t pickup : index_pair.first) {
1799 int64_t pickup_insert_after =
model()->
Start(vehicle);
1800 while (pickup_insert_after != delivery_insert_after) {
1801 const Insertion insertion = {{pickup, delivery}, pickup_insert_after};
1802 if (!existing_insertions.contains(insertion)) {
1803 AddPairEntry(pickup, pickup_insert_after, delivery,
1804 delivery_insert_after, vehicle, priority_queue,
1805 pickup_to_entries, delivery_to_entries);
1807 pickup_insert_after =
Value(pickup_insert_after);
1815 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1816 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1818 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1819 std::vector<PairEntries>* pickup_to_entries,
1820 std::vector<PairEntries>* delivery_to_entries) {
1821 priority_queue->
Remove(entry);
1822 if (entry->pickup_insert_after() != -1) {
1823 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1825 if (entry->delivery_insert_after() != -1) {
1826 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1831 void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1832 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1833 int64_t delivery_insert_after,
int vehicle,
1835 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1836 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1838 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1839 delivery_entries)
const {
1842 if (!pickup_vehicle_var->Contains(vehicle) ||
1843 !delivery_vehicle_var->Contains(vehicle)) {
1847 const auto vehicle_is_compatible = [pickup_vehicle_var,
1848 delivery_vehicle_var](
int vehicle) {
1849 return pickup_vehicle_var->
Contains(vehicle) &&
1850 delivery_vehicle_var->Contains(vehicle);
1852 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
1853 empty_vehicle_type_curator_->Type(vehicle),
1854 vehicle_is_compatible)) {
1858 const int num_allowed_vehicles =
1859 std::min(pickup_vehicle_var->Size(), delivery_vehicle_var->Size());
1860 if (pickup_insert_after == -1) {
1863 PairEntry* pair_entry =
1864 new PairEntry(pickup, -1, delivery, -1, -1, num_allowed_vehicles);
1865 pair_entry->set_value(
1866 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1870 priority_queue->
Add(pair_entry);
1874 PairEntry*
const pair_entry =
1875 new PairEntry(pickup, pickup_insert_after, delivery,
1876 delivery_insert_after, vehicle, num_allowed_vehicles);
1877 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1878 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1882 pickup_entries->at(pickup_insert_after).insert(pair_entry);
1883 delivery_entries->at(delivery_insert_after).insert(pair_entry);
1884 priority_queue->
Add(pair_entry);
1887 void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1888 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1890 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1892 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1893 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1894 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1895 pair_entry->vehicle()));
1903 GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1904 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1905 int64_t delivery_insert_after,
int vehicle)
const {
1907 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1909 pickup, pickup_insert_after, pickup_insert_before, vehicle);
1912 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1913 ? pickup_insert_before
1914 :
Value(delivery_insert_after);
1916 delivery, delivery_insert_after, delivery_insert_before, vehicle);
1918 const int64_t penalty_shift =
1919 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1922 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
1925 bool GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1926 const std::vector<int>&
nodes,
const absl::flat_hash_set<int>& vehicles,
1928 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1929 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1930 position_to_node_entries) {
1931 priority_queue->
Clear();
1932 position_to_node_entries->clear();
1933 position_to_node_entries->resize(
model()->
Size());
1935 const int num_vehicles =
1937 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
1939 for (
int node :
nodes) {
1943 if (StopSearchAndCleanup(priority_queue))
return false;
1947 AddNodeEntry(node, -1, -1, all_vehicles, priority_queue,
nullptr);
1950 InitializeInsertionEntriesPerformingNode(node, vehicles, priority_queue,
1951 position_to_node_entries);
1956 void GlobalCheapestInsertionFilteredHeuristic::
1957 InitializeInsertionEntriesPerformingNode(
1958 int64_t node,
const absl::flat_hash_set<int>& vehicles,
1960 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1962 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1963 position_to_node_entries) {
1964 const int num_vehicles =
1966 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
1969 auto vehicles_it = vehicles.begin();
1970 for (
int v = 0; v < num_vehicles; v++) {
1971 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
1973 const int64_t start =
model()->
Start(vehicle);
1975 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1976 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1981 std::vector<NodeInsertion> insertions;
1984 for (
const auto [insert_after, insertion_vehicle,
value] : insertions) {
1986 AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
1987 position_to_node_entries);
1995 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
1996 int v,
int cost_class) {
1998 (all_vehicles || vehicles.contains(v));
2002 for (
const int64_t insert_after :
2003 GetNeighborsOfNodeForCostClass(cost_class, node)) {
2007 const int vehicle = node_index_to_vehicle_[insert_after];
2008 if (vehicle == -1 ||
2009 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
2013 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
2014 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
2019 AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
2020 position_to_node_entries);
2025 bool GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
2026 const std::vector<int>&
nodes,
int vehicle, int64_t insert_after,
2029 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
2030 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
2034 std::vector<NodeEntry*> to_remove;
2035 absl::flat_hash_set<int> existing_insertions;
2036 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
2037 DCHECK_EQ(node_entry->insert_after(), insert_after);
2038 const int64_t node_to_insert = node_entry->node_to_insert();
2040 to_remove.push_back(node_entry);
2042 UpdateNodeEntry(node_entry, priority_queue);
2043 existing_insertions.insert(node_to_insert);
2046 for (NodeEntry*
const node_entry : to_remove) {
2047 DeleteNodeEntry(node_entry, priority_queue, node_entries);
2050 for (
int node_to_insert :
nodes) {
2051 if (StopSearchAndCleanup(priority_queue))
return false;
2053 !existing_insertions.contains(node_to_insert) &&
2054 IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
2055 AddNodeEntry(node_to_insert, insert_after, vehicle, all_vehicles,
2056 priority_queue, node_entries);
2062 void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
2063 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
2065 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
2066 std::vector<NodeEntries>* node_entries) {
2067 priority_queue->
Remove(entry);
2068 if (entry->insert_after() != -1) {
2069 node_entries->at(entry->insert_after()).erase(entry);
2074 void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2075 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2077 std::vector<NodeEntries>* node_entries)
const {
2079 const int64_t penalty_shift =
2080 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2084 if (!vehicle_var->Contains(vehicle)) {
2088 const auto vehicle_is_compatible = [vehicle_var](
int vehicle) {
2089 return vehicle_var->
Contains(vehicle);
2091 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2092 empty_vehicle_type_curator_->Type(vehicle),
2093 vehicle_is_compatible)) {
2097 const int num_allowed_vehicles = vehicle_var->Size();
2098 if (insert_after == -1) {
2100 if (!all_vehicles) {
2106 NodeEntry*
const node_entry =
2107 new NodeEntry(node, -1, -1, num_allowed_vehicles);
2108 node_entry->set_value(
CapSub(node_penalty, penalty_shift));
2109 priority_queue->
Add(node_entry);
2114 node, insert_after,
Value(insert_after), vehicle);
2115 if (!all_vehicles && insertion_cost > node_penalty) {
2122 NodeEntry*
const node_entry =
2123 new NodeEntry(node, insert_after, vehicle, num_allowed_vehicles);
2124 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
2127 node_entries->at(insert_after).insert(node_entry);
2128 priority_queue->
Add(node_entry);
2131 void GlobalCheapestInsertionFilteredHeuristic::UpdateNodeEntry(
2132 NodeEntry*
const node_entry,
2134 const int64_t node = node_entry->node_to_insert();
2135 const int64_t insert_after = node_entry->insert_after();
2138 node, insert_after,
Value(insert_after), node_entry->vehicle());
2139 const int64_t penalty_shift =
2140 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2144 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
2155 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
2160 std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
2162 start_end_distances_per_node_ =
2168 std::vector<bool> visited(
model()->
Size(),
false);
2170 std::vector<NodeInsertion> pickup_insertion_positions;
2173 std::vector<NodeInsertion> delivery_insertion_positions;
2178 std::vector<std::pair<int, int>> domain_to_pair;
2179 for (
int i = 0; i < index_pairs.size(); ++i) {
2181 for (int64_t pickup : index_pairs[i].first) {
2184 for (int64_t delivery : index_pairs[i].first) {
2187 domain_to_pair.emplace_back(domain, i);
2189 std::sort(domain_to_pair.begin(), domain_to_pair.end());
2190 for (
const auto [domain, pair] : domain_to_pair) {
2191 const auto& index_pair = index_pairs[pair];
2192 for (int64_t pickup : index_pair.first) {
2196 for (int64_t delivery : index_pair.second) {
2203 visited[pickup] =
true;
2204 visited[delivery] =
true;
2205 ComputeEvaluatorSortedPositions(pickup, &pickup_insertion_positions);
2206 for (
const auto [insert_pickup_after, pickup_vehicle,
2207 unused_pickup_value] : pickup_insertion_positions) {
2208 const int insert_pickup_before =
Value(insert_pickup_after);
2209 ComputeEvaluatorSortedPositionsOnRouteAfter(
2210 delivery, pickup, insert_pickup_before, pickup_vehicle,
2211 &delivery_insertion_positions);
2213 for (
const auto [insert_delivery_after, unused_delivery_vehicle,
2214 unused_delivery_value] :
2215 delivery_insertion_positions) {
2216 InsertBetween(pickup, insert_pickup_after, insert_pickup_before);
2217 const int64_t insert_delivery_before =
2218 (insert_delivery_after == insert_pickup_after) ? pickup
2219 : (insert_delivery_after == pickup)
2220 ? insert_pickup_before
2221 :
Value(insert_delivery_after);
2223 insert_delivery_before);
2237 std::priority_queue<Seed> node_queue;
2241 std::vector<NodeInsertion> insertion_positions;
2242 while (!node_queue.empty()) {
2243 const int node = node_queue.top().second;
2245 if (
Contains(node) || visited[node])
continue;
2246 ComputeEvaluatorSortedPositions(node, &insertion_positions);
2247 for (
const auto [insert_after, unused_vehicle, unused_value] :
2248 insertion_positions) {
2260 void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2261 int64_t node, std::vector<NodeInsertion>* sorted_insertions) {
2262 DCHECK(sorted_insertions !=
nullptr);
2264 sorted_insertions->clear();
2267 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2268 const int64_t start =
model()->
Start(vehicle);
2272 std::sort(sorted_insertions->begin(), sorted_insertions->end());
2276 void LocalCheapestInsertionFilteredHeuristic::
2277 ComputeEvaluatorSortedPositionsOnRouteAfter(
2278 int64_t node, int64_t start, int64_t next_after_start,
int vehicle,
2279 std::vector<NodeInsertion>* sorted_insertions) {
2280 DCHECK(sorted_insertions !=
nullptr);
2282 sorted_insertions->clear();
2287 std::sort(sorted_insertions->begin(), sorted_insertions->end());
2300 std::vector<std::vector<int64_t>> deliveries(
Size());
2301 std::vector<std::vector<int64_t>> pickups(
Size());
2303 for (
int first : pair.first) {
2304 for (
int second : pair.second) {
2305 deliveries[first].push_back(second);
2306 pickups[second].push_back(first);
2313 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
2314 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2315 sorted_vehicles[vehicle] = vehicle;
2317 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
2318 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
2320 for (
const int vehicle : sorted_vehicles) {
2322 bool extend_route =
true;
2327 while (extend_route) {
2328 extend_route =
false;
2330 int64_t
index = last_node;
2339 std::vector<int64_t> neighbors;
2341 std::unique_ptr<IntVarIterator> it(
2342 model()->Nexts()[
index]->MakeDomainIterator(
false));
2344 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
2347 for (
int i = 0; !found && i < neighbors.size(); ++i) {
2351 next = FindTopSuccessor(
index, neighbors);
2354 SortSuccessors(
index, &neighbors);
2355 ABSL_FALLTHROUGH_INTENDED;
2357 next = neighbors[i];
2364 bool contains_pickups =
false;
2365 for (int64_t pickup : pickups[
next]) {
2367 contains_pickups =
true;
2371 if (!contains_pickups) {
2375 std::vector<int64_t> next_deliveries;
2376 if (
next < deliveries.size()) {
2377 next_deliveries = GetPossibleNextsFromIterator(
2378 next, deliveries[
next].begin(), deliveries[
next].end());
2380 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
2381 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
2386 delivery = FindTopSuccessor(
next, next_deliveries);
2389 SortSuccessors(
next, &next_deliveries);
2390 ABSL_FALLTHROUGH_INTENDED;
2392 delivery = next_deliveries[j];
2410 if (
model()->IsEnd(end) && last_node != delivery) {
2411 last_node = delivery;
2412 extend_route =
true;
2427 bool CheapestAdditionFilteredHeuristic::
2428 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
2429 int vehicle2)
const {
2430 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2431 builder_.GetStartChainEnd(vehicle1));
2432 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
2433 builder_.GetStartChainEnd(vehicle2));
2434 if (has_partial_route1 == has_partial_route2) {
2435 return vehicle2 < vehicle1;
2437 return has_partial_route2 < has_partial_route1;
2450 int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2451 int64_t node,
const std::vector<int64_t>& successors) {
2453 int64_t best_successor = -1;
2454 for (int64_t successor : successors) {
2455 const int64_t evaluation = (successor >= 0)
2456 ? evaluator_(node, successor)
2458 if (evaluation < best_evaluation ||
2459 (evaluation == best_evaluation && successor > best_successor)) {
2460 best_evaluation = evaluation;
2461 best_successor = successor;
2464 return best_successor;
2467 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2468 int64_t node, std::vector<int64_t>* successors) {
2469 std::vector<std::pair<int64_t, int64_t>> values;
2470 values.reserve(successors->size());
2471 for (int64_t successor : *successors) {
2474 values.push_back({evaluator_(node, successor), -successor});
2476 std::sort(values.begin(), values.end());
2477 successors->clear();
2478 for (
auto value : values) {
2479 successors->push_back(-
value.second);
2490 comparator_(std::move(comparator)) {}
2492 int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2493 int64_t node,
const std::vector<int64_t>& successors) {
2494 return *std::min_element(successors.begin(), successors.end(),
2495 [
this, node](
int successor1,
int successor2) {
2496 return comparator_(node, successor1, successor2);
2500 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2501 int64_t node, std::vector<int64_t>* successors) {
2502 std::sort(successors->begin(), successors->end(),
2503 [
this, node](
int successor1,
int successor2) {
2504 return comparator_(node, successor1, successor2);
2556 template <
typename Saving>
2561 : savings_db_(savings_db),
2562 index_in_sorted_savings_(0),
2563 vehicle_types_(vehicle_types),
2564 single_vehicle_type_(vehicle_types == 1),
2565 using_incoming_reinjected_saving_(false),
2570 sorted_savings_per_vehicle_type_.clear();
2571 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
2572 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2573 savings.reserve(size * saving_neighbors);
2576 sorted_savings_.clear();
2577 costs_and_savings_per_arc_.clear();
2578 arc_indices_per_before_node_.clear();
2580 if (!single_vehicle_type_) {
2581 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
2582 arc_indices_per_before_node_.resize(size);
2583 for (
int before_node = 0; before_node < size; before_node++) {
2584 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
2587 skipped_savings_starting_at_.clear();
2588 skipped_savings_starting_at_.resize(size);
2589 skipped_savings_ending_at_.clear();
2590 skipped_savings_ending_at_.resize(size);
2591 incoming_reinjected_savings_ =
nullptr;
2592 outgoing_reinjected_savings_ =
nullptr;
2593 incoming_new_reinjected_savings_ =
nullptr;
2594 outgoing_new_reinjected_savings_ =
nullptr;
2598 int64_t before_node, int64_t after_node,
int vehicle_type) {
2599 CHECK(!sorted_savings_per_vehicle_type_.empty())
2600 <<
"Container not initialized!";
2601 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
2602 UpdateArcIndicesCostsAndSavings(before_node, after_node,
2603 {total_cost, saving});
2607 CHECK(!sorted_) <<
"Container already sorted!";
2609 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2610 std::sort(savings.begin(), savings.end());
2613 if (single_vehicle_type_) {
2614 const auto& savings = sorted_savings_per_vehicle_type_[0];
2615 sorted_savings_.resize(savings.size());
2616 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
2617 [](
const Saving& saving) {
2618 return SavingAndArc({saving, -1});
2624 sorted_savings_.reserve(vehicle_types_ *
2625 costs_and_savings_per_arc_.size());
2627 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
2629 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2630 costs_and_savings_per_arc_[arc_index];
2631 DCHECK(!costs_and_savings.empty());
2634 costs_and_savings.begin(), costs_and_savings.end(),
2635 [](
const std::pair<int64_t, Saving>& cs1,
2636 const std::pair<int64_t, Saving>& cs2) {
return cs1 > cs2; });
2641 const int64_t
cost = costs_and_savings.back().first;
2642 while (!costs_and_savings.empty() &&
2643 costs_and_savings.back().first ==
cost) {
2644 sorted_savings_.push_back(
2645 {costs_and_savings.back().second, arc_index});
2646 costs_and_savings.pop_back();
2649 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2650 next_saving_type_and_index_for_arc_.clear();
2651 next_saving_type_and_index_for_arc_.resize(
2652 costs_and_savings_per_arc_.size(), {-1, -1});
2655 index_in_sorted_savings_ = 0;
2660 return index_in_sorted_savings_ < sorted_savings_.size() ||
2661 HasReinjectedSavings();
2665 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
2667 <<
"Update() should be called between two calls to GetSaving() !";
2671 if (HasReinjectedSavings()) {
2672 if (incoming_reinjected_savings_ !=
nullptr &&
2673 outgoing_reinjected_savings_ !=
nullptr) {
2675 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
2676 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
2677 if (incoming_saving < outgoing_saving) {
2678 current_saving_ = incoming_saving;
2679 using_incoming_reinjected_saving_ =
true;
2681 current_saving_ = outgoing_saving;
2682 using_incoming_reinjected_saving_ =
false;
2685 if (incoming_reinjected_savings_ !=
nullptr) {
2686 current_saving_ = incoming_reinjected_savings_->front();
2687 using_incoming_reinjected_saving_ =
true;
2689 if (outgoing_reinjected_savings_ !=
nullptr) {
2690 current_saving_ = outgoing_reinjected_savings_->front();
2691 using_incoming_reinjected_saving_ =
false;
2695 current_saving_ = sorted_savings_[index_in_sorted_savings_];
2697 return current_saving_.saving;
2700 void Update(
bool update_best_saving,
int type = -1) {
2701 CHECK(to_update_) <<
"Container already up to date!";
2702 if (update_best_saving) {
2703 const int64_t arc_index = current_saving_.arc_index;
2704 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
2706 if (!HasReinjectedSavings()) {
2707 index_in_sorted_savings_++;
2709 if (index_in_sorted_savings_ == sorted_savings_.size()) {
2710 sorted_savings_.swap(next_savings_);
2712 index_in_sorted_savings_ = 0;
2714 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2715 next_saving_type_and_index_for_arc_.clear();
2716 next_saving_type_and_index_for_arc_.resize(
2717 costs_and_savings_per_arc_.size(), {-1, -1});
2720 UpdateReinjectedSavings();
2725 CHECK(!single_vehicle_type_);
2730 CHECK(sorted_) <<
"Savings not sorted yet!";
2732 return sorted_savings_per_vehicle_type_[type];
2736 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
2737 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
2741 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
2742 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
2746 struct SavingAndArc {
2750 bool operator<(
const SavingAndArc& other)
const {
2751 return std::tie(saving, arc_index) <
2752 std::tie(other.saving, other.arc_index);
2758 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
2759 const Saving& saving = saving_and_arc.saving;
2762 if (!savings_db_->
Contains(before_node)) {
2763 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
2765 if (!savings_db_->
Contains(after_node)) {
2766 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
2780 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
2781 if (single_vehicle_type_) {
2784 SkipSavingForArc(current_saving_);
2788 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
2789 const int previous_index = type_and_index.second;
2790 const int previous_type = type_and_index.first;
2791 bool next_saving_added =
false;
2794 if (previous_index >= 0) {
2797 if (type == -1 || previous_type == type) {
2800 next_saving_added =
true;
2801 next_saving = next_savings_[previous_index].saving;
2805 if (!next_saving_added &&
2806 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
2808 if (previous_index >= 0) {
2810 next_savings_[previous_index] = {next_saving, arc_index};
2813 type_and_index.second = next_savings_.size();
2814 next_savings_.push_back({next_saving, arc_index});
2816 next_saving_added =
true;
2822 SkipSavingForArc(current_saving_);
2826 if (next_saving_added) {
2827 SkipSavingForArc({next_saving, arc_index});
2832 void UpdateReinjectedSavings() {
2833 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
2834 &incoming_reinjected_savings_,
2835 using_incoming_reinjected_saving_);
2836 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
2837 &outgoing_reinjected_savings_,
2838 !using_incoming_reinjected_saving_);
2839 incoming_new_reinjected_savings_ =
nullptr;
2840 outgoing_new_reinjected_savings_ =
nullptr;
2843 void UpdateGivenReinjectedSavings(
2844 std::deque<SavingAndArc>* new_reinjected_savings,
2845 std::deque<SavingAndArc>** reinjected_savings,
2846 bool using_reinjected_savings) {
2847 if (new_reinjected_savings ==
nullptr) {
2849 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
2850 CHECK(!(*reinjected_savings)->empty());
2851 (*reinjected_savings)->pop_front();
2852 if ((*reinjected_savings)->empty()) {
2853 *reinjected_savings =
nullptr;
2862 if (*reinjected_savings !=
nullptr) {
2863 (*reinjected_savings)->clear();
2865 *reinjected_savings =
nullptr;
2866 if (!new_reinjected_savings->empty()) {
2867 *reinjected_savings = new_reinjected_savings;
2871 bool HasReinjectedSavings() {
2872 return outgoing_reinjected_savings_ !=
nullptr ||
2873 incoming_reinjected_savings_ !=
nullptr;
2876 void UpdateArcIndicesCostsAndSavings(
2877 int64_t before_node, int64_t after_node,
2878 const std::pair<int64_t, Saving>& cost_and_saving) {
2879 if (single_vehicle_type_) {
2882 absl::flat_hash_map<int, int>& arc_indices =
2883 arc_indices_per_before_node_[before_node];
2884 const auto& arc_inserted = arc_indices.insert(
2885 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
2886 const int index = arc_inserted.first->second;
2887 if (arc_inserted.second) {
2888 costs_and_savings_per_arc_.push_back({cost_and_saving});
2891 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
2895 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
2897 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2898 costs_and_savings_per_arc_[arc_index];
2900 bool found_saving =
false;
2901 while (!costs_and_savings.empty() && !found_saving) {
2902 const Saving& saving = costs_and_savings.back().second;
2904 *next_saving = saving;
2905 found_saving =
true;
2907 costs_and_savings.pop_back();
2909 return found_saving;
2913 int64_t index_in_sorted_savings_;
2914 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
2915 std::vector<SavingAndArc> sorted_savings_;
2916 std::vector<SavingAndArc> next_savings_;
2917 std::vector<std::pair< int,
int>>
2918 next_saving_type_and_index_for_arc_;
2919 SavingAndArc current_saving_;
2920 std::vector<std::vector<std::pair< int64_t,
Saving>>>
2921 costs_and_savings_per_arc_;
2922 std::vector<absl::flat_hash_map< int,
int>>
2923 arc_indices_per_before_node_;
2924 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
2925 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
2926 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
2927 std::deque<SavingAndArc>* incoming_reinjected_savings_;
2928 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
2929 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
2930 const int vehicle_types_;
2931 const bool single_vehicle_type_;
2932 bool using_incoming_reinjected_saving_;
2943 vehicle_type_curator_(nullptr),
2950 size_squared_ = size * size;
2958 model()->GetVehicleTypeContainer());
2963 if (!ComputeSavings())
return false;
2968 if (!
Commit())
return false;
2974 int type, int64_t before_node, int64_t after_node) {
2975 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
2982 const int64_t start =
model()->
Start(vehicle);
2983 const int64_t end =
model()->
End(vehicle);
2991 ->GetCompatibleVehicleOfType(
2992 type, vehicle_is_compatible,
2993 [](
int) {
return false; })
2997 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
2998 std::vector<std::vector<int64_t>>* adjacency_lists) {
2999 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
3000 for (int64_t neighbor : (*adjacency_lists)[node]) {
3004 (*adjacency_lists)[neighbor].push_back(node);
3007 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
3008 adjacency_lists->begin(), [](std::vector<int64_t> vec) {
3009 std::sort(vec.begin(), vec.end());
3010 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
3026 bool SavingsFilteredHeuristic::ComputeSavings() {
3030 std::vector<int64_t> uncontained_non_start_end_nodes;
3031 uncontained_non_start_end_nodes.reserve(size);
3032 for (
int node = 0; node < size; node++) {
3034 uncontained_non_start_end_nodes.push_back(node);
3038 const int64_t saving_neighbors =
3039 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
3040 static_cast<int64_t>(uncontained_non_start_end_nodes.size()));
3043 absl::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
3046 std::vector<std::vector<int64_t>> adjacency_lists(size);
3048 for (
int type = 0; type < num_vehicle_types; ++type) {
3055 const int64_t cost_class =
3057 const int64_t start =
model()->
Start(vehicle);
3058 const int64_t end =
model()->
End(vehicle);
3063 for (
int before_node : uncontained_non_start_end_nodes) {
3064 std::vector<std::pair< int64_t, int64_t>>
3066 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
3068 for (
int after_node : uncontained_non_start_end_nodes) {
3069 if (after_node != before_node) {
3070 costed_after_nodes.push_back(std::make_pair(
3071 model()->GetArcCostForClass(before_node, after_node, cost_class),
3075 if (saving_neighbors < costed_after_nodes.size()) {
3076 std::nth_element(costed_after_nodes.begin(),
3077 costed_after_nodes.begin() + saving_neighbors,
3078 costed_after_nodes.end());
3079 costed_after_nodes.resize(saving_neighbors);
3081 adjacency_lists[before_node].resize(costed_after_nodes.size());
3082 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
3083 adjacency_lists[before_node].begin(),
3084 [](std::pair<int64_t, int64_t> cost_and_node) {
3085 return cost_and_node.second;
3089 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
3094 for (
int before_node : uncontained_non_start_end_nodes) {
3095 const int64_t before_to_end_cost =
3097 const int64_t start_to_before_cost =
3098 CapSub(
model()->GetArcCostForClass(start, before_node, cost_class),
3101 for (int64_t after_node : adjacency_lists[before_node]) {
3102 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
3103 before_node == after_node ||
Contains(after_node)) {
3106 const int64_t arc_cost =
3108 const int64_t start_to_after_cost =
3109 CapSub(
model()->GetArcCostForClass(start, after_node, cost_class),
3111 const int64_t after_to_end_cost =
3114 const double weighted_arc_cost_fp =
3116 const int64_t weighted_arc_cost =
3118 ? static_cast<int64_t>(weighted_arc_cost_fp)
3119 : std::numeric_limits<int64_t>::
max();
3120 const int64_t saving_value =
CapSub(
3121 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
3124 BuildSaving(-saving_value, type, before_node, after_node);
3126 const int64_t total_cost =
3127 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
3138 int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
3139 int num_vehicle_types)
const {
3142 const int64_t num_neighbors_with_ratio =
3149 const double max_memory_usage_in_savings_unit =
3167 if (num_vehicle_types > 1) {
3168 multiplicative_factor += 1.5;
3170 const double num_savings =
3171 max_memory_usage_in_savings_unit / multiplicative_factor;
3172 const int64_t num_neighbors_with_memory_restriction =
3173 std::max(1.0, num_savings / (num_vehicle_types * size));
3175 return std::min(num_neighbors_with_ratio,
3176 num_neighbors_with_memory_restriction);
3181 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3187 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
3188 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
3189 for (
int type = 0; type < vehicle_types; type++) {
3190 const int vehicle_type_offset = type * size;
3191 const std::vector<Saving>& sorted_savings_for_type =
3193 for (
const Saving& saving : sorted_savings_for_type) {
3196 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3198 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3209 const bool nodes_not_contained =
3212 bool committed =
false;
3214 if (nodes_not_contained) {
3222 const int64_t start =
model()->
Start(vehicle);
3223 const int64_t end =
model()->
End(vehicle);
3227 const int saving_offset = type * size;
3229 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
3231 out_savings_ptr[saving_offset + before_node].size()) {
3234 int before_before_node = -1;
3235 int after_after_node = -1;
3236 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
3237 const Saving& in_saving =
3238 *(in_savings_ptr[saving_offset + after_node][in_index]);
3240 out_savings_ptr[saving_offset + before_node].size()) {
3241 const Saving& out_saving =
3242 *(out_savings_ptr[saving_offset + before_node][out_index]);
3257 *(out_savings_ptr[saving_offset + before_node][out_index]));
3260 if (after_after_node != -1) {
3264 SetValue(after_node, after_after_node);
3268 after_node = after_after_node;
3278 if (!
Contains(before_before_node)) {
3279 SetValue(start, before_before_node);
3280 SetValue(before_before_node, before_node);
3283 before_node = before_before_node;
3300 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3306 first_node_on_route_.resize(vehicles, -1);
3307 last_node_on_route_.resize(vehicles, -1);
3308 vehicle_of_first_or_last_node_.resize(size, -1);
3310 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
3311 const int64_t start =
model()->
Start(vehicle);
3312 const int64_t end =
model()->
End(vehicle);
3316 int64_t node =
Value(start);
3318 vehicle_of_first_or_last_node_[node] = vehicle;
3319 first_node_on_route_[vehicle] = node;
3322 while (
next != end) {
3326 vehicle_of_first_or_last_node_[node] = vehicle;
3327 last_node_on_route_[vehicle] = node;
3340 bool committed =
false;
3348 vehicle_of_first_or_last_node_[before_node] = vehicle;
3349 vehicle_of_first_or_last_node_[after_node] = vehicle;
3350 first_node_on_route_[vehicle] = before_node;
3351 last_node_on_route_[vehicle] = after_node;
3363 const int v1 = vehicle_of_first_or_last_node_[before_node];
3364 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3366 const int v2 = vehicle_of_first_or_last_node_[after_node];
3367 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3369 if (before_node == last_node && after_node == first_node && v1 != v2 &&
3378 MergeRoutes(v1, v2, before_node, after_node);
3383 const int vehicle = vehicle_of_first_or_last_node_[before_node];
3384 const int64_t last_node =
3385 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3387 if (before_node == last_node) {
3388 const int64_t end =
model()->
End(vehicle);
3392 if (type != route_type) {
3403 if (first_node_on_route_[vehicle] != before_node) {
3406 vehicle_of_first_or_last_node_[before_node] = -1;
3408 vehicle_of_first_or_last_node_[after_node] = vehicle;
3409 last_node_on_route_[vehicle] = after_node;
3416 const int vehicle = vehicle_of_first_or_last_node_[after_node];
3417 const int64_t first_node =
3418 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3420 if (after_node == first_node) {
3421 const int64_t start =
model()->
Start(vehicle);
3425 if (type != route_type) {
3436 if (last_node_on_route_[vehicle] != after_node) {
3439 vehicle_of_first_or_last_node_[after_node] = -1;
3441 vehicle_of_first_or_last_node_[before_node] = vehicle;
3442 first_node_on_route_[vehicle] = before_node;
3451 void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
3453 int64_t before_node,
3454 int64_t after_node) {
3456 const int64_t new_first_node = first_node_on_route_[first_vehicle];
3457 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
3459 const int64_t new_last_node = last_node_on_route_[second_vehicle];
3460 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
3464 int used_vehicle = first_vehicle;
3465 int unused_vehicle = second_vehicle;
3466 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
3467 model()->GetFixedCostOfVehicle(second_vehicle)) {
3468 used_vehicle = second_vehicle;
3469 unused_vehicle = first_vehicle;
3474 if (used_vehicle == first_vehicle) {
3479 bool committed =
Commit();
3481 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
3482 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
3484 std::swap(used_vehicle, unused_vehicle);
3487 if (used_vehicle == first_vehicle) {
3498 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
3499 model()->GetFixedCostOfVehicle(unused_vehicle));
3502 first_node_on_route_[unused_vehicle] = -1;
3503 last_node_on_route_[unused_vehicle] = -1;
3504 vehicle_of_first_or_last_node_[before_node] = -1;
3505 vehicle_of_first_or_last_node_[after_node] = -1;
3506 first_node_on_route_[used_vehicle] = new_first_node;
3507 last_node_on_route_[used_vehicle] = new_last_node;
3508 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
3509 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
3517 bool use_minimum_matching)
3519 use_minimum_matching_(use_minimum_matching) {}
3529 std::vector<int> indices(1, 0);
3530 for (
int i = 1; i < size; ++i) {
3531 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
3532 indices.push_back(i);
3536 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
3537 std::vector<bool> class_covered(num_cost_classes,
false);
3538 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3539 const int64_t cost_class =
3541 if (!class_covered[cost_class]) {
3542 class_covered[cost_class] =
true;
3543 const int64_t start =
model()->
Start(vehicle);
3544 const int64_t end =
model()->
End(vehicle);
3545 auto cost = [
this, &indices, start, end, cost_class](
int from,
int to) {
3548 const int from_index = (from == 0) ? start : indices[from];
3549 const int to_index = (to == 0) ? end : indices[to];
3550 const int64_t
cost =
3558 using Cost = decltype(
cost);
3560 indices.size(),
cost);
3561 if (use_minimum_matching_) {
3564 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
3566 if (christofides_solver.Solve()) {
3567 path_per_cost_class[cost_class] =
3568 christofides_solver.TravelingSalesmanPath();
3573 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3574 const int64_t cost_class =
3576 const std::vector<int>& path = path_per_cost_class[cost_class];
3577 if (path.empty())
continue;
3582 const int end =
model()->
End(vehicle);
3583 for (
int i = 1; i < path.size() - 1 && prev != end; ++i) {
3585 int next = indices[path[i]];
3613 struct SweepIndexSortAngle {
3614 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3615 return (node1.angle < node2.angle);
3617 } SweepIndexAngleComparator;
3619 struct SweepIndexSortDistance {
3620 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3621 return (node1.distance < node2.distance);
3623 } SweepIndexDistanceComparator;
3627 const std::vector<std::pair<int64_t, int64_t>>& points)
3628 : coordinates_(2 * points.size(), 0), sectors_(1) {
3629 for (int64_t i = 0; i < points.size(); ++i) {
3630 coordinates_[2 * i] = points[i].first;
3631 coordinates_[2 * i + 1] = points[i].second;
3638 const double pi_rad = 3.14159265;
3640 const int x0 = coordinates_[0];
3641 const int y0 = coordinates_[1];
3643 std::vector<SweepIndex> sweep_indices;
3644 for (int64_t
index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3646 const int x = coordinates_[2 *
index];
3647 const int y = coordinates_[2 *
index + 1];
3648 const double x_delta = x - x0;
3649 const double y_delta = y - y0;
3650 double square_distance = x_delta * x_delta + y_delta * y_delta;
3651 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3653 SweepIndex sweep_index(
index,
angle, square_distance);
3654 sweep_indices.push_back(sweep_index);
3656 std::sort(sweep_indices.begin(), sweep_indices.end(),
3657 SweepIndexDistanceComparator);
3659 const int size = static_cast<int>(sweep_indices.size()) / sectors_;
3660 for (
int sector = 0; sector < sectors_; ++sector) {
3661 std::vector<SweepIndex> cluster;
3662 std::vector<SweepIndex>::iterator begin =
3663 sweep_indices.begin() + sector * size;
3664 std::vector<SweepIndex>::iterator end =
3665 sector == sectors_ - 1 ? sweep_indices.end()
3666 : sweep_indices.begin() + (sector + 1) * size;
3667 std::sort(begin, end, SweepIndexAngleComparator);
3669 for (
const SweepIndex& sweep_index : sweep_indices) {
3670 indices->push_back(sweep_index.index);
3698 class RouteConstructor {
3700 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
3701 bool check_assignment, int64_t num_indices,
3702 const std::vector<Link>& links_list)
3703 : assignment_(assignment),
3705 check_assignment_(check_assignment),
3706 solver_(model_->solver()),
3707 num_indices_(num_indices),
3708 links_list_(links_list),
3709 nexts_(model_->Nexts()),
3710 in_route_(num_indices_, -1),
3712 index_to_chain_index_(num_indices, -1),
3713 index_to_vehicle_class_index_(num_indices, -1) {
3715 const std::vector<std::string> dimension_names =
3716 model_->GetAllDimensionNames();
3717 dimensions_.assign(dimension_names.size(),
nullptr);
3718 for (
int i = 0; i < dimension_names.size(); ++i) {
3719 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
3722 cumuls_.resize(dimensions_.size());
3723 for (std::vector<int64_t>& cumuls :
cumuls_) {
3724 cumuls.resize(num_indices_);
3726 new_possible_cumuls_.resize(dimensions_.size());
3729 ~RouteConstructor() {}
3732 model_->solver()->TopPeriodicCheck();
3735 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
3736 std::vector<int> route(1,
index);
3737 routes_.push_back(route);
3738 in_route_[
index] = routes_.size() - 1;
3742 for (
const Link&
link : links_list_) {
3743 model_->solver()->TopPeriodicCheck();
3744 const int index1 =
link.link.first;
3745 const int index2 =
link.link.second;
3751 if (index_to_vehicle_class_index_[index1] < 0) {
3752 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3753 ++dimension_index) {
3754 cumuls_[dimension_index][index1] =
3755 std::max(dimensions_[dimension_index]->GetTransitValue(
3757 dimensions_[dimension_index]->CumulVar(index1)->Min());
3760 if (index_to_vehicle_class_index_[index2] < 0) {
3761 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3762 ++dimension_index) {
3763 cumuls_[dimension_index][index2] =
3764 std::max(dimensions_[dimension_index]->GetTransitValue(
3766 dimensions_[dimension_index]->CumulVar(index2)->Min());
3770 const int route_index1 = in_route_[index1];
3771 const int route_index2 = in_route_[index2];
3773 route_index1 >= 0 && route_index2 >= 0 &&
3774 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
3777 if (Merge(merge, route_index1, route_index2)) {
3783 model_->solver()->TopPeriodicCheck();
3787 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3788 if (!deleted_chains_.contains(chain_index)) {
3789 final_chains_.push_back(chains_[chain_index]);
3792 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
3793 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
3794 if (!deleted_routes_.contains(route_index)) {
3795 final_routes_.push_back(routes_[route_index]);
3798 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
3800 const int extra_vehicles =
std::max(
3801 0, static_cast<int>(final_chains_.size()) - model_->vehicles());
3803 int chain_index = 0;
3804 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
3806 if (chain_index - extra_vehicles >= model_->vehicles()) {
3809 const int start = final_chains_[chain_index].head;
3810 const int end = final_chains_[chain_index].tail;
3812 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3813 assignment_->SetValue(
3814 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
3815 assignment_->Add(nexts_[end]);
3816 assignment_->SetValue(nexts_[end],
3817 model_->End(chain_index - extra_vehicles));
3821 for (
int route_index = 0; route_index < final_routes_.size();
3823 if (chain_index - extra_vehicles >= model_->vehicles()) {
3826 DCHECK_LT(route_index, final_routes_.size());
3827 const int head = final_routes_[route_index].front();
3828 const int tail = final_routes_[route_index].back();
3829 if (
head ==
tail && head < model_->Size()) {
3831 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3832 assignment_->SetValue(
3833 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
3834 assignment_->Add(nexts_[
tail]);
3835 assignment_->SetValue(nexts_[
tail],
3836 model_->End(chain_index - extra_vehicles));
3844 if (!assignment_->Contains(
next)) {
3845 assignment_->Add(
next);
3854 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
3857 bool operator()(
const std::vector<int>& route1,
3858 const std::vector<int>& route2)
const {
3859 return (route1.size() < route2.size());
3870 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
3871 return (chain1.nodes < chain2.nodes);
3875 bool Head(
int node)
const {
3876 return (node == routes_[in_route_[node]].front());
3879 bool Tail(
int node)
const {
3880 return (node == routes_[in_route_[node]].back());
3883 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
3884 int dimension_index) {
3885 const RoutingDimension& dimension = *dimensions_[dimension_index];
3886 std::vector<int>::const_iterator it = route.begin();
3887 int64_t cumul = route_cumul;
3888 while (it != route.end()) {
3889 const int previous = *it;
3890 const int64_t cumul_previous = cumul;
3894 if (it == route.end()) {
3897 const int next = *it;
3898 int64_t available_from_previous =
3899 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
3900 int64_t available_cumul_next =
3903 const int64_t slack = available_cumul_next - available_from_previous;
3904 if (slack > dimension.SlackVar(previous)->Max()) {
3905 available_cumul_next =
3906 available_from_previous + dimension.SlackVar(previous)->Max();
3909 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
3912 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
3915 cumul = available_cumul_next;
3920 bool CheckRouteConnection(
const std::vector<int>& route1,
3921 const std::vector<int>& route2,
int dimension_index,
3923 const int tail1 = route1.back();
3924 const int head2 = route2.front();
3925 const int tail2 = route2.back();
3926 const RoutingDimension& dimension = *dimensions_[dimension_index];
3927 int non_depot_node = -1;
3928 for (
int node = 0; node < num_indices_; ++node) {
3929 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
3930 non_depot_node = node;
3935 const int64_t depot_threshold =
3936 std::max(dimension.SlackVar(non_depot_node)->Max(),
3937 dimension.CumulVar(non_depot_node)->Max());
3939 int64_t available_from_tail1 =
cumuls_[dimension_index][tail1] +
3940 dimension.GetTransitValue(tail1, head2, 0);
3941 int64_t new_available_cumul_head2 =
3944 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
3945 if (slack > dimension.SlackVar(tail1)->Max()) {
3946 new_available_cumul_head2 =
3947 available_from_tail1 + dimension.SlackVar(tail1)->Max();
3950 bool feasible_route =
true;
3951 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
3954 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
3959 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
3960 const int64_t new_possible_cumul_tail2 =
3961 new_possible_cumuls_[dimension_index].contains(tail2)
3962 ? new_possible_cumuls_[dimension_index][tail2]
3963 :
cumuls_[dimension_index][tail2];
3965 if (!feasible_route || (new_possible_cumul_tail2 +
3966 dimension.GetTransitValue(tail2,
end_depot, 0) >
3973 bool FeasibleMerge(
const std::vector<int>& route1,
3974 const std::vector<int>& route2,
int node1,
int node2,
3977 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
3982 if (!((index_to_vehicle_class_index_[node1] == -1 &&
3983 index_to_vehicle_class_index_[node2] == -1) ||
3985 index_to_vehicle_class_index_[node2] == -1) ||
3986 (index_to_vehicle_class_index_[node1] == -1 &&
3995 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3996 ++dimension_index) {
3997 new_possible_cumuls_[dimension_index].clear();
3998 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
4007 bool CheckTempAssignment(Assignment*
const temp_assignment,
4008 int new_chain_index,
int old_chain_index,
int head1,
4009 int tail1,
int head2,
int tail2) {
4012 if (new_chain_index >= model_->vehicles())
return false;
4013 const int start = head1;
4014 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
4015 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
4017 temp_assignment->Add(nexts_[tail1]);
4018 temp_assignment->SetValue(nexts_[tail1], head2);
4019 temp_assignment->Add(nexts_[tail2]);
4020 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
4021 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4022 if ((chain_index != new_chain_index) &&
4023 (chain_index != old_chain_index) &&
4024 (!deleted_chains_.contains(chain_index))) {
4025 const int start = chains_[chain_index].head;
4026 const int end = chains_[chain_index].tail;
4027 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
4028 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
4030 temp_assignment->Add(nexts_[end]);
4031 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
4034 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
4037 bool UpdateAssignment(
const std::vector<int>& route1,
4038 const std::vector<int>& route2) {
4039 bool feasible =
true;
4040 const int head1 = route1.front();
4041 const int tail1 = route1.back();
4042 const int head2 = route2.front();
4043 const int tail2 = route2.back();
4044 const int chain_index1 = index_to_chain_index_[head1];
4045 const int chain_index2 = index_to_chain_index_[head2];
4046 if (chain_index1 < 0 && chain_index2 < 0) {
4047 const int chain_index = chains_.size();
4048 if (check_assignment_) {
4049 Assignment*
const temp_assignment =
4050 solver_->MakeAssignment(assignment_);
4051 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
4052 tail1, head2, tail2);
4059 index_to_chain_index_[head1] = chain_index;
4060 index_to_chain_index_[tail2] = chain_index;
4061 chains_.push_back(chain);
4063 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
4064 if (check_assignment_) {
4065 Assignment*
const temp_assignment =
4066 solver_->MakeAssignment(assignment_);
4068 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4069 head1, tail1, head2, tail2);
4072 index_to_chain_index_[tail2] = chain_index1;
4073 chains_[chain_index1].head = head1;
4074 chains_[chain_index1].tail = tail2;
4075 ++chains_[chain_index1].nodes;
4077 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
4078 if (check_assignment_) {
4079 Assignment*
const temp_assignment =
4080 solver_->MakeAssignment(assignment_);
4082 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
4083 head1, tail1, head2, tail2);
4086 index_to_chain_index_[head1] = chain_index2;
4087 chains_[chain_index2].head = head1;
4088 chains_[chain_index2].tail = tail2;
4089 ++chains_[chain_index2].nodes;
4092 if (check_assignment_) {
4093 Assignment*
const temp_assignment =
4094 solver_->MakeAssignment(assignment_);
4096 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4097 head1, tail1, head2, tail2);
4100 index_to_chain_index_[tail2] = chain_index1;
4101 chains_[chain_index1].head = head1;
4102 chains_[chain_index1].tail = tail2;
4103 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
4104 deleted_chains_.insert(chain_index2);
4108 assignment_->Add(nexts_[tail1]);
4109 assignment_->SetValue(nexts_[tail1], head2);
4114 bool Merge(
bool merge,
int index1,
int index2) {
4116 if (UpdateAssignment(routes_[index1], routes_[index2])) {
4118 for (
const int node : routes_[index2]) {
4119 in_route_[node] = index1;
4120 routes_[index1].push_back(node);
4122 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4123 ++dimension_index) {
4124 for (
const std::pair<int, int64_t> new_possible_cumul :
4125 new_possible_cumuls_[dimension_index]) {
4126 cumuls_[dimension_index][new_possible_cumul.first] =
4127 new_possible_cumul.second;
4130 deleted_routes_.insert(index2);
4137 Assignment*
const assignment_;
4138 RoutingModel*
const model_;
4139 const bool check_assignment_;
4140 Solver*
const solver_;
4141 const int64_t num_indices_;
4142 const std::vector<Link> links_list_;
4143 std::vector<IntVar*> nexts_;
4144 std::vector<const RoutingDimension*> dimensions_;
4145 std::vector<std::vector<int64_t>>
cumuls_;
4146 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
4147 std::vector<std::vector<int>> routes_;
4148 std::vector<int> in_route_;
4149 absl::flat_hash_set<int> deleted_routes_;
4150 std::vector<std::vector<int>> final_routes_;
4151 std::vector<Chain> chains_;
4152 absl::flat_hash_set<int> deleted_chains_;
4153 std::vector<Chain> final_chains_;
4154 std::vector<int> index_to_chain_index_;
4155 std::vector<int> index_to_vehicle_class_index_;
4161 class SweepBuilder :
public DecisionBuilder {
4163 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
4164 : model_(
model), check_assignment_(check_assignment) {}
4165 ~SweepBuilder()
override {}
4167 Decision* Next(Solver*
const solver)
override {
4172 Assignment*
const assignment = solver->MakeAssignment();
4173 route_constructor_ = absl::make_unique<RouteConstructor>(
4174 assignment, model_, check_assignment_, num_indices_, links_);
4176 route_constructor_->Construct();
4177 route_constructor_.reset(
nullptr);
4179 assignment->Restore();
4186 const int depot = model_->GetDepot();
4187 num_indices_ = model_->Size() + model_->vehicles();
4188 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4189 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4190 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4192 std::vector<int64_t> indices;
4193 model_->sweep_arranger()->ArrangeIndices(&indices);
4194 for (
int i = 0; i < indices.size() - 1; ++i) {
4195 const int64_t first = indices[i];
4196 const int64_t second = indices[i + 1];
4197 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4198 (model_->IsStart(second) || !model_->IsEnd(second))) {
4199 if (first != depot && second != depot) {
4200 Link
link(std::make_pair(first, second), 0, 0, depot, depot);
4201 links_.push_back(
link);
4207 RoutingModel*
const model_;
4208 std::unique_ptr<RouteConstructor> route_constructor_;
4209 const bool check_assignment_;
4210 int64_t num_indices_;
4211 std::vector<Link> links_;
4216 bool check_assignment) {
4217 return model->solver()->RevAlloc(
new SweepBuilder(
model, check_assignment));
4226 class AllUnperformed :
public DecisionBuilder {
4229 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
4230 ~AllUnperformed()
override {}
4231 Decision* Next(Solver*
const solver)
override {
4234 model_->CostVar()->FreezeQueue();
4235 for (
int i = 0; i < model_->Size(); ++i) {
4236 if (!model_->IsStart(i)) {
4237 model_->ActiveVar(i)->SetValue(0);
4240 model_->CostVar()->UnfreezeQueue();
4245 RoutingModel*
const model_;
4250 return model->solver()->RevAlloc(
new AllUnperformed(
model));
4255 class GuidedSlackFinalizer :
public DecisionBuilder {
4257 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel*
model,
4258 std::function<int64_t(int64_t)> initializer);
4259 Decision* Next(Solver* solver)
override;
4262 int64_t SelectValue(int64_t
index);
4263 int64_t ChooseVariable();
4265 const RoutingDimension*
const dimension_;
4266 RoutingModel*
const model_;
4267 const std::function<int64_t(int64_t)> initializer_;
4268 RevArray<bool> is_initialized_;
4269 std::vector<int64_t> initial_values_;
4270 Rev<int64_t> current_index_;
4271 Rev<int64_t> current_route_;
4272 RevArray<int64_t> last_delta_used_;
4277 GuidedSlackFinalizer::GuidedSlackFinalizer(
4278 const RoutingDimension* dimension, RoutingModel*
model,
4279 std::function<int64_t(int64_t)> initializer)
4282 initializer_(std::move(initializer)),
4283 is_initialized_(dimension->slacks().size(), false),
4284 initial_values_(dimension->slacks().size(),
4285 std::numeric_limits<int64_t>::
min()),
4286 current_index_(model_->Start(0)),
4288 last_delta_used_(dimension->slacks().size(), 0) {}
4290 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4291 CHECK_EQ(solver, model_->solver());
4292 const int node_idx = ChooseVariable();
4293 CHECK(node_idx == -1 ||
4294 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
4295 if (node_idx != -1) {
4296 if (!is_initialized_[node_idx]) {
4297 initial_values_[node_idx] = initializer_(node_idx);
4298 is_initialized_.SetValue(solver, node_idx,
true);
4300 const int64_t
value = SelectValue(node_idx);
4301 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
4302 return solver->MakeAssignVariableValue(slack_variable,
value);
4307 int64_t GuidedSlackFinalizer::SelectValue(int64_t
index) {
4308 const IntVar*
const slack_variable = dimension_->SlackVar(
index);
4309 const int64_t center = initial_values_[
index];
4310 const int64_t max_delta =
4311 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4317 while (std::abs(
delta) < max_delta &&
4318 !slack_variable->Contains(center +
delta)) {
4325 last_delta_used_.SetValue(model_->solver(),
index,
delta);
4326 return center +
delta;
4329 int64_t GuidedSlackFinalizer::ChooseVariable() {
4330 int64_t int_current_node = current_index_.Value();
4331 int64_t int_current_route = current_route_.Value();
4333 while (int_current_route < model_->vehicles()) {
4334 while (!model_->IsEnd(int_current_node) &&
4335 dimension_->SlackVar(int_current_node)->Bound()) {
4336 int_current_node = model_->NextVar(int_current_node)->Value();
4338 if (!model_->IsEnd(int_current_node)) {
4341 int_current_route += 1;
4342 if (int_current_route < model_->vehicles()) {
4343 int_current_node = model_->Start(int_current_route);
4347 CHECK(int_current_route == model_->vehicles() ||
4348 !dimension_->SlackVar(int_current_node)->Bound());
4349 current_index_.SetValue(model_->solver(), int_current_node);
4350 current_route_.SetValue(model_->solver(), int_current_route);
4351 if (int_current_route < model_->vehicles()) {
4352 return int_current_node;
4361 std::function<int64_t(int64_t)> initializer) {
4362 return solver_->RevAlloc(
4363 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
4366 int64_t RoutingDimension::ShortestTransitionSlack(int64_t node)
const {
4368 CHECK(!model_->IsEnd(node));
4372 const int64_t
next = model_->NextVar(node)->Value();
4373 if (model_->IsEnd(
next)) {
4374 return SlackVar(node)->Min();
4376 const int64_t next_next = model_->NextVar(
next)->Value();
4377 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4378 CHECK_EQ(serving_vehicle, model_->VehicleVar(
next)->Value());
4380 model_->StateDependentTransitCallback(
4381 state_dependent_class_evaluators_
4382 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
4385 const int64_t next_cumul_min = CumulVar(
next)->Min();
4386 const int64_t next_cumul_max = CumulVar(
next)->Max();
4387 const int64_t optimal_next_cumul =
4388 transit_from_next.transit_plus_identity->RangeMinArgument(
4389 next_cumul_min, next_cumul_max + 1);
4391 DCHECK_LE(next_cumul_min, optimal_next_cumul);
4392 DCHECK_LE(optimal_next_cumul, next_cumul_max);
4397 const int64_t current_cumul = CumulVar(node)->Value();
4398 const int64_t current_state_independent_transit = model_->TransitCallback(
4399 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
4400 const int64_t current_state_dependent_transit =
4402 ->StateDependentTransitCallback(
4403 state_dependent_class_evaluators_
4404 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4406 .transit->Query(current_cumul);
4407 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4408 current_state_independent_transit -
4409 current_state_dependent_transit;
4410 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4411 CHECK_LE(optimal_slack, SlackVar(node)->Max());
4412 return optimal_slack;
4418 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4424 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
4426 const std::vector<IntVar*> variables_;
4428 int64_t current_step_;
4435 int64_t current_direction_;
4440 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4441 : variables_(std::move(variables)),
4444 current_direction_(0) {}
4446 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
4448 static const int64_t sings[] = {1, -1};
4449 for (; 1 <= current_step_; current_step_ /= 2) {
4450 for (; current_direction_ < 2 * variables_.size();) {
4451 const int64_t variable_idx = current_direction_ / 2;
4452 IntVar*
const variable = variables_[variable_idx];
4453 const int64_t sign_index = current_direction_ % 2;
4454 const int64_t sign = sings[sign_index];
4455 const int64_t offset = sign * current_step_;
4456 const int64_t new_value = center_->
Value(variable) + offset;
4457 ++current_direction_;
4458 if (variable->Contains(new_value)) {
4459 delta->Add(variable);
4460 delta->SetValue(variable, new_value);
4464 current_direction_ = 0;
4469 void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
4470 CHECK(assignment !=
nullptr);
4471 current_step_ = FindMaxDistanceToDomain(assignment);
4472 center_ = assignment;
4475 int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
4476 const Assignment* assignment) {
4478 for (
const IntVar*
const var : variables_) {
4487 std::vector<IntVar*> variables) {
4488 return std::unique_ptr<LocalSearchOperator>(
4489 new GreedyDescentLSOperator(std::move(variables)));
4494 CHECK(dimension !=
nullptr);
4496 std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t
index) {
4502 solver_->MakeSolveOnce(guided_finalizer);
4503 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
4504 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
4505 start_cumuls[vehicle_idx] = dimension->
CumulVar(starts_[vehicle_idx]);
4508 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
4510 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
4512 Assignment*
const first_solution = solver_->MakeAssignment();
4513 first_solution->
Add(start_cumuls);
4514 for (
IntVar*
const cumul : start_cumuls) {
4515 first_solution->
SetValue(cumul, cumul->Min());
4518 solver_->MakeLocalSearchPhase(first_solution,
parameters);
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
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 CapSub(int64_t x, int64_t y)
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
void UpdateWithType(int type)
int64_t Value(const IntVar *const var) const
void set_insert_after(int insert_after)
The base class for all local search operators.
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
CheapestAdditionFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
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.
#define CHECK_GE(val1, val2)
static constexpr Value PATH_MOST_CONSTRAINED_ARC
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
std::unique_ptr< SavingsContainer< Saving > > savings_container_
int delivery_to_insert() const
~SavingsFilteredHeuristic() override
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
int64_t GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
void SetValue(const IntVar *const var, int64_t value)
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
#define CHECK_GT(val1, val2)
void set_value(int64_t value)
#define VLOG(verboselevel)
Filter-based heuristic dedicated to routing.
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle, int64_t bucket)
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.
IntVarElement * Add(IntVar *const var)
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
int pickup_to_insert() const
RoutingModel * model() const
virtual int64_t Min() const =0
std::string DebugString() const override
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
Dimensions represent quantities accumulated at nodes along the routes.
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
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.
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
#define DCHECK_GT(val1, val2)
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
std::function< int64_t(int64_t)> penalty_evaluator_
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...
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
const E & Element(const V *const var) const
static constexpr Value PATH_CHEAPEST_ARC
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
A DecisionBuilder is responsible for creating the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
int node_to_insert() const
const Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
void ReinjectSkippedSavingsEndingAt(int64_t node)
#define CHECK_LT(val1, val2)
void set_vehicle(int vehicle)
bool StopSearch() override
Returns true if the search must be stopped.
virtual void Start(const Assignment *assignment)=0
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Generic filter-based heuristic applied to IntVars.
bool HasCompatibleVehicleOfType(int type, const std::function< bool(int)> &vehicle_is_compatible) const
Searches a compatible vehicle of the given type; returns false if none was found.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
void ResetSolution()
Resets the data members for a new solution.
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...
const std::vector< IntVar * > cumuls_
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
void SetMatchingAlgorithm(MatchingAlgorithm matching)
static const int64_t kint64max
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
int64_t CapAdd(int64_t x, int64_t y)
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
#define DCHECK_NE(val1, val2)
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void set_pickup_insert_after(int pickup_insert_after)
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
int pickup_insert_after() const
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
#define CHECK_LE(val1, val2)
std::pair< int64_t, int64_t > Saving
void InitializeContainer(int64_t size, int64_t saving_neighbors)
virtual void ResetVehicleIndices()
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
bool VehicleIsEmpty(int vehicle) const
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
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,...
IntContainer * MutableIntVarContainer()
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
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...
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
std::function< int64_t(int64_t, int64_t)> evaluator_
void AppendInsertionPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int vehicle, std::vector< NodeInsertion > *node_insertions)
Helper method to the ComputeEvaluatorSortedPositions* methods.
std::pair< int, int > link
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
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 ...
An Assignment is a variable -> domains mapping, used to report solutions to the user.
virtual uint64_t Size() const =0
This method returns the number of values in the domain of the variable.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
The class IntVar is a subset of IntExpr.
void Update(bool update_best_saving, int type=-1)
#define DCHECK_GE(val1, val2)
std::vector< std::deque< int > > vehicles_per_vehicle_class
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
Assignment *const assignment_
void STLClearObject(T *obj)
#define CHECK_EQ(val1, val2)
const std::vector< IntVar * > vars_
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...
FirstSolutionStrategy_Value
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
RoutingIndexPairs IndexPairs
void NoteChangedPriority(T *val)
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
#define DCHECK(condition)
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.
std::pair< StartEndValue, int > Seed
NodeEntry(int node_to_insert, int insert_after, int vehicle, int64_t bucket)
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
void ReinjectSkippedSavingsStartingAt(int64_t node)
bool Contains(const T *val) const
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
int vehicles() const
Returns the number of vehicle routes in the model.
RoutingIndexPair IndexPair
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 ArrangeIndices(std::vector< int64_t > *indices)
SavingsFilteredHeuristic(RoutingModel *model, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
static const int kUnassigned
#define DCHECK_EQ(val1, val2)
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
A Decision represents a choice point in the search tree.
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.
#define DCHECK_LE(val1, val2)
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...
virtual void SetVehicleIndex(int64_t node, int vehicle)
static constexpr Value PARALLEL_CHEAPEST_INSERTION
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
bool operator<(const NodeEntry &other) const
Collection of objects used to extend the Constraint Solver library.
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.
int delivery_insert_after() const
void set_value(int64_t value)
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
SweepArranger(const std::vector< std::pair< int64_t, int64_t >> &points)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
int64_t Size() const
Returns the number of next variables in the model.
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
void set_vehicle(int vehicle)
virtual void BuildRoutesFromSavings()=0
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
virtual bool InitializeSolution()
Virtual method to initialize the solution.
What follows is relevant for models with time/state dependent transits.
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
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...
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
const IntContainer & IntVarContainer() const
Assignment *const BuildSolution()
Builds a solution.
virtual IntVarIterator * MakeDomainIterator(bool reversible) const =0
Creates a domain iterator.
virtual int64_t Max() const =0
bool operator<(const PairEntry &other) const
#define DCHECK_LT(val1, val2)
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
int64_t number_of_rejects() const
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
int64_t Start(int vehicle) const
Model inspection.