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"
63class LocalSearchPhaseParameters;
66ABSL_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();
334bool IntVarFilteredHeuristic::FilterAccept() {
335 if (!filter_manager_)
return true;
337 return filter_manager_->
Accept(monitor, delta_, empty_,
349bool 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)) {}
492std::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) {
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;
543template <
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 {
606void 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);
753void 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(
831bool 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];
838bool 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());
933bool 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;
956bool 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();
974 const int64_t pickup = entry->pickup_to_insert();
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) {
1039 if (Contains(pickup)) return true;
1043 pair_indices_to_insert.end());
1048bool 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 =
1069 model()->GetFixedCostOfVehicle(entry_vehicle);
1070 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
1071 delivery](
int vehicle) {
1072 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1076 DCHECK(VehicleIsEmpty(vehicle));
1077 const int64_t
end =
model()->End(vehicle);
1078 InsertBetween(pickup,
model()->Start(vehicle),
end);
1079 InsertBetween(delivery, pickup,
end);
1086 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1087 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
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) {
1121 DCHECK(VehicleIsEmpty(new_empty_vehicle));
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) {
1132 DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
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);
1150bool 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 =
1169 vehicles.empty() || vehicles.size() ==
model()->vehicles();
1171 while (!priority_queue.
IsEmpty()) {
1172 NodeEntry*
const node_entry = priority_queue.
Top();
1173 if (StopSearchAndCleanup(&priority_queue)) {
1176 const int64_t node_to_insert = node_entry->node_to_insert();
1177 if (Contains(node_to_insert)) {
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();
1206 InsertBetween(node_to_insert, insert_after,
Value(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());
1231bool 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 =
1250 model()->GetFixedCostOfVehicle(entry_vehicle);
1251 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1252 node_to_insert](
int vehicle) {
1253 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1257 DCHECK(VehicleIsEmpty(vehicle));
1258 InsertBetween(node_to_insert,
model()->Start(vehicle),
1259 model()->End(vehicle));
1266 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1267 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
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) {
1306 DCHECK(VehicleIsEmpty(new_empty_vehicle));
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) {
1319 DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
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);
1334bool 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 =
1347 ComputeStartEndDistanceForVehicles(unused_vehicles);
1348 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1350 InitializePriorityQueue(&start_end_distances_per_node, &first_node_queue);
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,
1365void 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++) {
1378 if (!VehicleIsEmpty(vehicle)) {
1379 (*is_vehicle_used)[vehicle] =
true;
1380 used_vehicles->insert(vehicle);
1382 (*is_vehicle_used)[vehicle] =
false;
1383 unused_vehicles->push_back(vehicle);
1388void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1389 if (gci_params_.farthest_seeds_ratio <= 0)
return;
1391 const int num_seeds =
static_cast<int>(
1392 std::ceil(gci_params_.farthest_seeds_ratio *
model()->vehicles()));
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 =
1399 ComputeStartEndDistanceForVehicles(unused_vehicles);
1403 std::priority_queue<Seed> farthest_node_queue;
1404 InitializePriorityQueue(&start_end_distances_per_node, &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(
1420 [
this](
int vehicle) {
return !VehicleIsEmpty(vehicle); });
1423template <
class Queue>
1424int 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()) {
1428 if (StopSearch())
return -1;
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];
1437 if (Contains(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);
1449 InsertBetween(seed_node,
start,
end);
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();
1473bool 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());
1486 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1487 model()->GetPickupAndDeliveryPairs();
1488 for (
int index : pair_indices) {
1489 const RoutingModel::IndexPair& index_pair = pickup_delivery_pairs[
index];
1490 for (int64_t pickup : index_pair.first) {
1491 if (Contains(pickup))
continue;
1492 for (int64_t delivery : index_pair.second) {
1493 if (Contains(delivery))
continue;
1494 if (StopSearchAndCleanup(priority_queue))
return false;
1499 if (gci_params_.add_unperformed_entries &&
1500 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1501 GetUnperformedValue(pickup) !=
1503 GetUnperformedValue(delivery) !=
1505 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1509 InitializeInsertionEntriesPerformingPair(
1510 pickup, delivery, priority_queue, pickup_to_entries,
1511 delivery_to_entries);
1518void 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) {
1528 if (!gci_params_.use_neighbors_ratio_for_initialization) {
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) {
1536 if (VehicleIsEmpty(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;
1551 AppendInsertionPositionsAfter(delivery, pickup,
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);
1573 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
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];
1585 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1589 if (VehicleIsEmpty(vehicle) &&
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) {
1625 if (VehicleIsEmpty(vehicle)) {
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);
1644bool 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);
1670bool 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);
1707 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
1708 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1709 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1710 model()->GetPickupAndDeliveryPairs();
1711 for (
int pair_index : pair_indices) {
1712 if (StopSearchAndCleanup(priority_queue))
return false;
1713 const RoutingModel::IndexPair& index_pair =
1714 pickup_delivery_pairs[pair_index];
1715 for (int64_t pickup : index_pair.first) {
1716 if (Contains(pickup) ||
1717 !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
1720 for (int64_t delivery : index_pair.second) {
1721 if (Contains(delivery)) {
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);
1745bool 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);
1782 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
1783 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1784 model()->GetPickupAndDeliveryPairs();
1785 for (
int pair_index : pair_indices) {
1786 if (StopSearchAndCleanup(priority_queue))
return false;
1787 const RoutingModel::IndexPair& index_pair =
1788 pickup_delivery_pairs[pair_index];
1789 for (int64_t delivery : index_pair.second) {
1790 if (Contains(delivery) ||
1791 !IsNeighborForCostClass(cost_class, delivery_insert_after,
1795 for (int64_t pickup : index_pair.first) {
1796 if (Contains(pickup)) {
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);
1815void 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);
1831void 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 {
1840 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
1841 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
1842 if (!pickup_vehicle_var->Contains(vehicle) ||
1843 !delivery_vehicle_var->Contains(vehicle)) {
1844 if (vehicle == -1 || !VehicleIsEmpty(vehicle))
return;
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)
1868 :
CapAdd(GetUnperformedValue(pickup),
1869 GetUnperformedValue(delivery)));
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);
1887void 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()));
1903GlobalCheapestInsertionFilteredHeuristic::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);
1908 const int64_t pickup_value = GetInsertionCostForNodeAtPosition(
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);
1915 const int64_t delivery_value = GetInsertionCostForNodeAtPosition(
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)
1920 ?
CapAdd(GetUnperformedValue(pickup), GetUnperformedValue(delivery))
1922 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
1925bool 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 =
1936 vehicles.empty() ?
model()->vehicles() : vehicles.size();
1937 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
1939 for (
int node :
nodes) {
1940 if (Contains(node)) {
1943 if (StopSearchAndCleanup(priority_queue))
return false;
1945 if (gci_params_.add_unperformed_entries &&
1947 AddNodeEntry(node, -1, -1, all_vehicles, priority_queue,
nullptr);
1950 InitializeInsertionEntriesPerformingNode(node, vehicles, priority_queue,
1951 position_to_node_entries);
1956void 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 =
1965 vehicles.empty() ?
model()->vehicles() : vehicles.size();
1966 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
1968 if (!gci_params_.use_neighbors_ratio_for_initialization) {
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);
1974 if (all_vehicles && VehicleIsEmpty(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) {
1997 return (
model()->GetCostClassIndexOfVehicle(v).value() == cost_class) &&
1998 (all_vehicles || vehicles.contains(v));
2000 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
2002 for (
const int64_t insert_after :
2003 GetNeighborsOfNodeForCostClass(cost_class, node)) {
2004 if (!Contains(insert_after)) {
2007 const int vehicle = node_index_to_vehicle_[insert_after];
2008 if (vehicle == -1 ||
2009 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
2012 if (all_vehicles && VehicleIsEmpty(vehicle) &&
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);
2025bool 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();
2039 if (Contains(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);
2049 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
2050 for (
int node_to_insert :
nodes) {
2051 if (StopSearchAndCleanup(priority_queue))
return false;
2052 if (!Contains(node_to_insert) &&
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);
2062void 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);
2074void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2075 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2077 std::vector<NodeEntries>* node_entries)
const {
2078 const int64_t node_penalty = GetUnperformedValue(node);
2079 const int64_t penalty_shift =
2080 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2083 const IntVar*
const vehicle_var =
model()->VehicleVar(node);
2084 if (!vehicle_var->Contains(vehicle)) {
2085 if (vehicle == -1 || !VehicleIsEmpty(vehicle))
return;
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);
2113 const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
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);
2131void 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();
2137 const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
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)
2141 ? GetUnperformedValue(node)
2144 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
2152LocalCheapestInsertionFilteredHeuristic::
2153 LocalCheapestInsertionFilteredHeuristic(
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) {
2260void 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) {
2272 std::sort(sorted_insertions->begin(), sorted_insertions->end());
2276void 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(
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;
2427bool 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;
2450int64_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;
2467void 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)) {}
2492int64_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);
2500void 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);
2556template <
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_);
2726 Update(
true, 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;
2760 const int64_t before_node = savings_db_->GetBeforeNodeFromSaving(saving);
2761 const int64_t after_node = savings_db_->GetAfterNodeFromSaving(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)) {
2807 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(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,
2896 Saving* next_saving) {
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;
2903 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
2904 *next_saving = saving;
2905 found_saving =
true;
2907 costs_and_savings.pop_back();
2909 return found_saving;
2912 const SavingsFilteredHeuristic*
const savings_db_;
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) {
2991 ->GetCompatibleVehicleOfType(
2992 type, vehicle_is_compatible,
2993 [](
int) {
return false; })
2997void 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]) {
3001 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
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());
3026bool 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 =
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 =
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 =
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);
3138int64_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);
3181void 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) {
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)) {
3280 SetValue(before_before_node, before_node);
3283 before_node = before_before_node;
3300void 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++) {
3318 vehicle_of_first_or_last_node_[node] = vehicle;
3319 first_node_on_route_[vehicle] = node;
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) {
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) {
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;
3451void 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;
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] =
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;
3583 for (
int i = 1; i < path.size() - 1 && prev !=
end; ++i) {
3585 int next = indices[path[i]];
3613struct SweepIndexSortAngle {
3614 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3615 return (node1.angle < node2.angle);
3617} SweepIndexAngleComparator;
3619struct 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);
3698class 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_;
4161class 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));
4226class 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));
4255class 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_;
4277GuidedSlackFinalizer::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) {}
4290Decision* 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);
4307int64_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;
4329int64_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)));
4366int64_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 =
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_;
4440GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4441 : variables_(
std::move(variables)),
4444 current_direction_(0) {}
4446bool 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;
4469void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
4470 CHECK(assignment !=
nullptr);
4471 current_step_ = FindMaxDistanceToDomain(assignment);
4472 center_ = assignment;
4475int64_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);
const std::vector< IntVar * > vars_
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
void NoteChangedPriority(T *val)
bool Contains(const T *val) const
const E & Element(const V *const var) const
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
An Assignment is a variable -> domains mapping, used to report solutions to the user.
void SetValue(const IntVar *const var, int64_t value)
int64_t Value(const IntVar *const var) const
IntContainer * MutableIntVarContainer()
IntVarElement * Add(IntVar *const var)
const IntContainer & IntVarContainer() const
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(const std::vector< int > &vehicles)
Computes and returns the distance of each uninserted node to every vehicle in "vehicles" as a std::ve...
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
void InitializePriorityQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, Queue *priority_queue)
Initializes the priority_queue by inserting the best entry corresponding to each node,...
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
Returns the cost of inserting 'node_to_insert' between 'insert_after' and 'insert_before' on the 'veh...
int64_t GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
std::function< int64_t(int64_t)> penalty_evaluator_
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor)
Inserts 'node' just after 'predecessor', and just before 'successor', resulting in the following subs...
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.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
std::vector< NodeIndex > TravelingSalesmanPath()
void SetMatchingAlgorithm(MatchingAlgorithm matching)
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
static constexpr Value PATH_MOST_CONSTRAINED_ARC
static constexpr Value PARALLEL_CHEAPEST_INSERTION
static constexpr Value PATH_CHEAPEST_ARC
bool operator<(const NodeEntry &other) const
void set_vehicle(int vehicle)
NodeEntry(int node_to_insert, int insert_after, int vehicle, int64_t bucket)
void set_insert_after(int insert_after)
void set_value(int64_t value)
int node_to_insert() const
bool operator<(const PairEntry &other) const
void set_vehicle(int vehicle)
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle, int64_t bucket)
int pickup_insert_after() const
void set_pickup_insert_after(int pickup_insert_after)
int delivery_insert_after() const
int delivery_to_insert() const
void set_value(int64_t value)
int pickup_to_insert() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
virtual int64_t Min() const =0
virtual int64_t Max() const =0
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
std::string DebugString() const override
int64_t number_of_rejects() const
Generic filter-based heuristic applied to IntVars.
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
Assignment *const assignment_
void ResetSolution()
Resets the data members for a new solution.
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
virtual bool InitializeSolution()
Virtual method to initialize the solution.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
Assignment *const BuildSolution()
Builds a solution.
The class IntVar is a subset of IntExpr.
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
virtual uint64_t Size() const =0
This method returns the number of values in the domain of the variable.
virtual IntVarIterator * MakeDomainIterator(bool reversible) const =0
Creates a domain iterator.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
The base class for all local search operators.
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
virtual int64_t RangeMinArgument(int64_t from, int64_t to) const =0
Dimensions represent quantities accumulated at nodes along the routes.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
Filter-based heuristic dedicated to routing.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
virtual void SetVehicleIndex(int64_t node, int vehicle)
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
RoutingModel * model() const
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
bool StopSearch() override
Returns true if the search must be stopped.
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
virtual void ResetVehicleIndices()
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
bool VehicleIsEmpty(int vehicle) 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.
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
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 ...
RoutingIndexPair IndexPair
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
int64_t Size() const
Returns the number of next variables in the model.
RoutingIndexPairs IndexPairs
int64_t Start(int vehicle) const
Model inspection.
int vehicles() const
Returns the number of vehicle routes in the model.
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64_t(int64_t)> initializer)
The next few members are in the public section only for testing purposes.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
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.
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...
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
void ReinjectSkippedSavingsEndingAt(int64_t node)
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
void UpdateWithType(int type)
void ReinjectSkippedSavingsStartingAt(int64_t node)
void InitializeContainer(int64_t size, int64_t saving_neighbors)
void Update(bool update_best_saving, int type=-1)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
SavingsFilteredHeuristic(RoutingModel *model, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
std::pair< int64_t, int64_t > Saving
std::unique_ptr< SavingsContainer< Saving > > savings_container_
~SavingsFilteredHeuristic() override
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
Finds the best available vehicle of type "type" to start a new route to serve the arc before_node-->a...
virtual void BuildRoutesFromSavings()=0
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
SweepArranger(const std::vector< std::pair< int64_t, int64_t > > &points)
void ArrangeIndices(std::vector< int64_t > *indices)
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
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.
std::pair< int, int > GetCompatibleVehicleOfType(int type, std::function< bool(int)> vehicle_is_compatible, std::function< bool(int)> stop_and_return_vehicle)
Searches for the best compatible vehicle of the given type, i.e.
void Reset(const std::function< bool(int)> &store_vehicle)
Resets the vehicles stored, storing only vehicles from the vehicle_type_container_ for which store_ve...
const std::vector< IntVar * > cumuls_
static const int64_t kint64max
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
void STLClearObject(T *obj)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
FirstSolutionStrategy_Value
int64_t CapSub(int64_t x, int64_t y)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
static const int kUnassigned
std::pair< int, int > link
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
std::function< int64_t(int64_t, int64_t)> evaluator_
std::optional< int64_t > end
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
What follows is relevant for models with time/state dependent transits.
RangeMinMaxIndexFunction * transit_plus_identity
f(x)
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
std::vector< std::deque< int > > vehicles_per_vehicle_class
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.