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) {
185 return FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
187 if (has_single_vehicle_node) {
188 return FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC;
190 return FirstSolutionStrategy::PATH_CHEAPEST_ARC;
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,
233 const std::vector<IntVar*>& secondary_vars,
235 : assignment_(solver->MakeAssignment()),
238 base_vars_size_(vars.size()),
239 delta_(solver->MakeAssignment()),
240 is_in_delta_(
vars_.size(), false),
241 empty_(solver->MakeAssignment()),
242 filter_manager_(filter_manager),
243 objective_upper_bound_(
std::numeric_limits<int64_t>::
max()),
244 number_of_decisions_(0),
245 number_of_rejects_(0) {
246 if (!secondary_vars.empty()) {
247 vars_.insert(vars_.end(), secondary_vars.begin(), secondary_vars.end());
250 delta_indices_.reserve(vars_.size());
254 number_of_decisions_ = 0;
255 number_of_rejects_ = 0;
274 const std::function<int64_t(int64_t)>& next_accessor) {
278 if (!InitializeSolution()) {
282 for (
int v = 0; v < model_->
vehicles(); v++) {
283 int64_t node = model_->
Start(v);
284 while (!model_->
IsEnd(node)) {
285 const int64_t
next = next_accessor(node);
308 ++number_of_decisions_;
309 const bool accept = FilterAccept();
311 if (filter_manager_ !=
nullptr) {
320 objective_upper_bound_);
326 const int delta_size = delta_container.
Size();
329 for (
int i = 0; i < delta_size; ++i) {
339 ++number_of_rejects_;
342 for (
const int delta_index : delta_indices_) {
343 is_in_delta_[delta_index] =
false;
346 delta_indices_.clear();
347 return accept ? std::optional<int64_t>{objective_upper_bound_} : std::nullopt;
356bool IntVarFilteredHeuristic::FilterAccept() {
357 if (!filter_manager_)
return true;
359 return filter_manager_->
Accept(monitor, delta_, empty_,
361 objective_upper_bound_);
368 bool omit_secondary_vars)
371 omit_secondary_vars ||
model->CostsAreHomogeneousAcrossVehicles()
373 :
model->VehicleVars(),
377bool RoutingFilteredHeuristic::InitializeSolution() {
389 start_chain_ends_.resize(
model()->vehicles());
390 end_chain_starts_.resize(
model()->vehicles());
393 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
395 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
404 start_chain_ends_[vehicle] = node;
407 std::vector<int64_t> starts(
Size() +
model()->vehicles(), -1);
408 std::vector<int64_t> ends(
Size() +
model()->vehicles(), -1);
414 std::vector<bool> touched(
Size(),
false);
415 for (
int node = 0; node <
Size(); ++node) {
417 while (!
model()->
IsEnd(current) && !touched[current]) {
418 touched[current] =
true;
419 IntVar*
const next_var =
Var(current);
420 if (next_var->Bound()) {
421 current = next_var->Value();
426 starts[ends[current]] = starts[node];
427 ends[starts[node]] = ends[current];
432 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
433 end_chain_starts_[vehicle] = starts[
model()->
End(vehicle)];
434 int64_t node = start_chain_ends_[vehicle];
435 if (!
model()->IsEnd(node)) {
443 while (!
model()->IsEnd(node)) {
464 node, 1, [
this, node](
int alternate) {
465 if (node != alternate && !
Contains(alternate)) {
484 std::vector<bool> to_make_unperformed(
Size(),
false);
485 for (
const auto& [pickups, deliveries] :
486 model()->GetPickupAndDeliveryPairs()) {
487 int64_t performed_pickup = -1;
488 for (int64_t pickup : pickups) {
490 performed_pickup = pickup;
494 int64_t performed_delivery = -1;
495 for (int64_t delivery : deliveries) {
497 performed_delivery = delivery;
501 if ((performed_pickup == -1) != (performed_delivery == -1)) {
502 if (performed_pickup != -1) {
503 to_make_unperformed[performed_pickup] =
true;
505 if (performed_delivery != -1) {
506 to_make_unperformed[performed_delivery] =
true;
514 const int64_t next_of_next =
Value(
next);
526 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
527 std::function<int64_t(int64_t)> penalty_evaluator,
530 evaluator != nullptr),
532 penalty_evaluator_(
std::move(penalty_evaluator)) {}
534std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
536 const std::vector<int>& vehicles) {
537 const absl::flat_hash_set<int> vehicle_set(vehicles.begin(), vehicles.end());
538 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
541 for (
int node = 0; node <
model()->
Size(); node++) {
543 std::vector<StartEndValue>& start_end_distances =
544 start_end_distances_per_node[node];
546 const int64_t num_allowed_vehicles = vehicle_var->
Size();
548 const auto add_distance = [
this, node, num_allowed_vehicles,
549 &start_end_distances](
int vehicle) {
556 model()->GetArcCostForVehicle(node,
end, vehicle));
557 start_end_distances.push_back({num_allowed_vehicles,
distance, vehicle});
562 if (num_allowed_vehicles < vehicles.size()) {
563 std::unique_ptr<IntVarIterator> it(
566 if (vehicle < 0 || !vehicle_set.contains(vehicle))
continue;
567 add_distance(vehicle);
570 for (
const int vehicle : vehicles) {
571 if (!vehicle_var->
Contains(vehicle))
continue;
572 add_distance(vehicle);
577 std::sort(start_end_distances.begin(), start_end_distances.end(),
579 return second < first;
582 return start_end_distances_per_node;
585template <
class Queue>
587 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
588 Queue* priority_queue) {
590 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
592 for (
int node = 0; node < num_nodes; node++) {
594 std::vector<StartEndValue>& start_end_distances =
595 (*start_end_distances_per_node)[node];
596 if (start_end_distances.empty()) {
600 const StartEndValue& start_end_value = start_end_distances.back();
601 priority_queue->push(std::make_pair(start_end_value, node));
602 start_end_distances.pop_back();
621 int64_t node_to_insert, int64_t
start, int64_t next_after_start,
622 int vehicle, std::vector<NodeInsertion>* node_insertions) {
623 DCHECK(node_insertions !=
nullptr);
624 int64_t insert_after =
start;
625 while (!
model()->IsEnd(insert_after)) {
626 const int64_t insert_before =
627 (insert_after ==
start) ? next_after_start :
Value(insert_after);
629 InsertBetween(node_to_insert, insert_after, insert_before, vehicle);
630 std::optional<int64_t> insertion_cost =
Evaluate(
false);
631 if (insertion_cost.has_value()) {
632 node_insertions->push_back({insert_after, vehicle, *insertion_cost});
635 node_insertions->push_back(
636 {insert_after, vehicle,
638 insert_before, vehicle)});
640 insert_after = insert_before;
645 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
649 evaluator_(node_to_insert, insert_before, vehicle)),
650 evaluator_(insert_after, insert_before, vehicle));
654 int64_t node_to_insert)
const {
663void SortAndExtractPairSeconds(std::vector<std::pair<int64_t, T>>* pairs,
664 std::vector<T>* sorted_seconds) {
665 CHECK(pairs !=
nullptr);
666 CHECK(sorted_seconds !=
nullptr);
667 std::sort(pairs->begin(), pairs->end());
668 sorted_seconds->reserve(pairs->size());
669 for (
const std::pair<int64_t, T>& p : *pairs) {
670 sorted_seconds->push_back(p.second);
683 : value_(
std::numeric_limits<int64_t>::
max()),
695 if (bucket_ != other.bucket_) {
696 return bucket_ > other.bucket_;
700 if (value_ != other.value_) {
701 return value_ > other.value_;
703 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
704 return vehicle_ == -1;
706 return std::tie(pickup_insert_after_, pickup_to_insert_,
707 delivery_insert_after_, delivery_to_insert_, vehicle_) >
708 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
709 other.delivery_insert_after_, other.delivery_to_insert_,
728 const int pickup_to_insert_;
729 int pickup_insert_after_;
730 const int delivery_to_insert_;
731 const int delivery_insert_after_;
733 const int64_t bucket_;
740 : value_(
std::numeric_limits<int64_t>::
max()),
748 if (bucket_ != other.bucket_) {
749 return bucket_ > other.bucket_;
751 if (value_ != other.value_) {
752 return value_ > other.value_;
754 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
755 return vehicle_ == -1;
757 return std::tie(insert_after_, node_to_insert_, vehicle_) >
758 std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
772 const int node_to_insert_;
775 const int64_t bucket_;
783 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
784 std::function<int64_t(int64_t)> penalty_evaluator,
788 std::move(penalty_evaluator),
791 node_index_to_vehicle_(
model->Size(), -1),
792 empty_vehicle_type_curator_(nullptr) {
797 if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
806 std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
810void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
812 !node_index_to_neighbors_by_cost_class_.empty()) {
818 const int64_t num_neighbors = NumNeighbors();
821 DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
823 const RoutingModel& routing_model = *
model();
824 const int64_t size = routing_model.Size();
825 node_index_to_neighbors_by_cost_class_.resize(size);
826 const int num_cost_classes = routing_model.GetCostClassesCount();
827 for (int64_t node_index = 0; node_index < size; node_index++) {
828 node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
829 for (
int cc = 0; cc < num_cost_classes; cc++) {
830 node_index_to_neighbors_by_cost_class_[node_index][cc] =
831 absl::make_unique<SparseBitset<int64_t>>(size);
835 for (int64_t node_index = 0; node_index < size; ++node_index) {
836 DCHECK(!routing_model.IsEnd(node_index));
837 if (routing_model.IsStart(node_index)) {
844 for (
int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
845 if (!routing_model.HasVehicleWithCostClassIndex(
846 RoutingCostClassIndex(cost_class))) {
850 std::vector<std::pair< int64_t, int64_t>>
852 costed_after_nodes.reserve(size);
853 for (
int after_node = 0; after_node < size; ++after_node) {
854 if (after_node != node_index && !routing_model.IsStart(after_node)) {
855 costed_after_nodes.push_back(
856 std::make_pair(routing_model.GetArcCostForClass(
857 node_index, after_node, cost_class),
861 std::nth_element(costed_after_nodes.begin(),
862 costed_after_nodes.begin() + num_neighbors - 1,
863 costed_after_nodes.end());
864 costed_after_nodes.resize(num_neighbors);
866 for (
auto [
cost, neighbor] : costed_after_nodes) {
867 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
871 DCHECK(!routing_model.IsEnd(neighbor) &&
872 !routing_model.IsStart(neighbor));
873 node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
877 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
878 const int64_t vehicle_start = routing_model.Start(vehicle);
879 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
881 node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
888bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
889 int cost_class, int64_t node_index, int64_t neighbor_index)
const {
891 (*node_index_to_neighbors_by_cost_class_[node_index]
892 [cost_class])[neighbor_index];
895bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
896 std::vector<bool> node_is_visited(
model()->
Size(), -1);
899 node =
Value(node)) {
900 if (node_index_to_vehicle_[node] != v) {
903 node_is_visited[node] =
true;
907 for (
int node = 0; node <
model()->
Size(); node++) {
908 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
917 ComputeNeighborhoods();
918 if (empty_vehicle_type_curator_ ==
nullptr) {
919 empty_vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
920 model()->GetVehicleTypeContainer());
923 empty_vehicle_type_curator_->Reset(
928 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
929 absl::flat_hash_map<int, std::map<int64_t, std::vector<int>>>
930 vehicle_to_pair_nodes;
933 int pickup_vehicle = -1;
934 for (int64_t pickup : index_pair.first) {
936 pickup_vehicle = node_index_to_vehicle_[pickup];
940 int delivery_vehicle = -1;
941 for (int64_t delivery : index_pair.second) {
943 delivery_vehicle = node_index_to_vehicle_[delivery];
947 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
948 pairs_to_insert_by_bucket[GetBucketOfPair(index_pair)].push_back(
index);
950 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
951 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle][1];
952 for (int64_t delivery : index_pair.second) {
953 pair_nodes.push_back(delivery);
956 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
957 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle][1];
958 for (int64_t pickup : index_pair.first) {
959 pair_nodes.push_back(pickup);
963 for (
const auto& [vehicle,
nodes] : vehicle_to_pair_nodes) {
964 if (!InsertNodesOnRoutes(
nodes, {vehicle}))
return false;
967 if (!InsertPairsAndNodesByRequirementTopologicalOrder())
return false;
971 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
972 std::map<int64_t, std::vector<int>> nodes_by_bucket;
973 for (
int node = 0; node <
model()->
Size(); ++node) {
976 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
979 InsertFarthestNodesAsSeeds();
981 if (!SequentialInsertNodes(nodes_by_bucket))
return false;
982 }
else if (!InsertNodesOnRoutes(nodes_by_bucket, {})) {
986 DCHECK(CheckVehicleIndices());
990bool GlobalCheapestInsertionFilteredHeuristic::
991 InsertPairsAndNodesByRequirementTopologicalOrder() {
994 for (
const std::vector<int>& types :
995 model()->GetTopologicallySortedVisitTypes()) {
996 for (
int type : types) {
997 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
998 for (
int index :
model()->GetPairIndicesOfType(type)) {
999 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pairs[
index])]
1002 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
1003 std::map<int64_t, std::vector<int>> nodes_by_bucket;
1004 for (
int node :
model()->GetSingleNodesOfType(type)) {
1005 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
1007 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
1013bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
1014 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket) {
1016 std::vector<PairEntries> pickup_to_entries;
1017 std::vector<PairEntries> delivery_to_entries;
1018 std::vector<int> pair_indices_to_insert;
1019 for (
const auto& [bucket, pair_indices] : pair_indices_by_bucket) {
1020 pair_indices_to_insert.insert(pair_indices_to_insert.end(),
1021 pair_indices.begin(), pair_indices.end());
1022 if (!InitializePairPositions(pair_indices_to_insert, &priority_queue,
1023 &pickup_to_entries, &delivery_to_entries)) {
1026 while (!priority_queue.
IsEmpty()) {
1027 if (StopSearchAndCleanup(&priority_queue)) {
1030 PairEntry*
const entry = priority_queue.
Top();
1031 const int64_t pickup = entry->pickup_to_insert();
1032 const int64_t delivery = entry->delivery_to_insert();
1034 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1035 &delivery_to_entries);
1039 const int entry_vehicle = entry->vehicle();
1040 if (entry_vehicle == -1) {
1045 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1046 &delivery_to_entries);
1052 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle)) {
1053 if (!InsertPairEntryUsingEmptyVehicleTypeCurator(
1054 pair_indices_to_insert, entry, &priority_queue,
1055 &pickup_to_entries, &delivery_to_entries)) {
1063 const int64_t pickup_insert_after = entry->pickup_insert_after();
1064 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1065 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
1067 const int64_t delivery_insert_after = entry->delivery_insert_after();
1068 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1069 ? pickup_insert_before
1070 :
Value(delivery_insert_after);
1071 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
1073 if (!UpdateAfterPairInsertion(
1074 pair_indices_to_insert, entry_vehicle, pickup,
1075 pickup_insert_after, delivery, delivery_insert_after,
1076 &priority_queue, &pickup_to_entries, &delivery_to_entries)) {
1080 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1081 &delivery_to_entries);
1088 pair_indices_to_insert.erase(
1090 pair_indices_to_insert.begin(), pair_indices_to_insert.end(),
1091 [
this, &pickup_delivery_pairs](
int pair_index) {
1095 for (int64_t pickup : pickup_delivery_pairs[pair_index].first) {
1096 if (Contains(pickup)) return true;
1100 pair_indices_to_insert.end());
1105bool GlobalCheapestInsertionFilteredHeuristic::
1106 InsertPairEntryUsingEmptyVehicleTypeCurator(
1107 const std::vector<int>& pair_indices,
1108 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1110 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1112 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1114 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1115 delivery_to_entries) {
1116 const int entry_vehicle = pair_entry->vehicle();
1117 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle));
1123 const int64_t pickup = pair_entry->pickup_to_insert();
1124 const int64_t delivery = pair_entry->delivery_to_insert();
1125 const int64_t entry_fixed_cost =
1126 model()->GetFixedCostOfVehicle(entry_vehicle);
1127 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
1128 delivery](
int vehicle) {
1129 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1133 DCHECK(VehicleIsEmpty(vehicle));
1134 const int64_t
end =
model()->End(vehicle);
1135 InsertBetween(pickup,
model()->Start(vehicle),
end, vehicle);
1136 InsertBetween(delivery, pickup,
end, vehicle);
1137 return Evaluate(
true).has_value();
1143 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1144 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1146 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1147 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1148 empty_vehicle_type_curator_->Type(entry_vehicle),
1149 vehicle_is_compatible, stop_and_return_vehicle);
1150 if (compatible_vehicle >= 0) {
1152 const int64_t vehicle_start =
model()->Start(compatible_vehicle);
1153 const int num_previous_vehicle_entries =
1154 pickup_to_entries->at(vehicle_start).size() +
1155 delivery_to_entries->at(vehicle_start).size();
1156 if (!UpdateAfterPairInsertion(
1157 pair_indices, compatible_vehicle, pickup, vehicle_start, delivery,
1158 pickup, priority_queue, pickup_to_entries, delivery_to_entries)) {
1161 if (compatible_vehicle != entry_vehicle) {
1167 DCHECK_EQ(num_previous_vehicle_entries, 0);
1173 const int new_empty_vehicle =
1174 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1175 empty_vehicle_type_curator_->Type(compatible_vehicle));
1177 if (new_empty_vehicle >= 0) {
1178 DCHECK(VehicleIsEmpty(new_empty_vehicle));
1181 UpdatePairPositions(pair_indices, new_empty_vehicle,
1182 model()->Start(new_empty_vehicle), priority_queue,
1183 pickup_to_entries, delivery_to_entries);
1185 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1189 DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
1192 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1193 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1194 pair_entry->set_pickup_insert_after(
1195 model()->Start(next_fixed_cost_empty_vehicle));
1196 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1197 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1198 UpdatePairEntry(pair_entry, priority_queue);
1200 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1201 delivery_to_entries);
1207bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1208 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
1209 const absl::flat_hash_set<int>& vehicles) {
1211 std::vector<NodeEntries> position_to_node_entries;
1212 std::vector<int> nodes_to_insert;
1213 for (
const auto& [bucket,
nodes] : nodes_by_bucket) {
1214 nodes_to_insert.insert(nodes_to_insert.end(),
nodes.begin(),
nodes.end());
1215 if (!InitializePositions(nodes_to_insert, vehicles, &priority_queue,
1216 &position_to_node_entries)) {
1225 const bool all_vehicles =
1226 vehicles.empty() || vehicles.size() ==
model()->vehicles();
1228 while (!priority_queue.
IsEmpty()) {
1229 NodeEntry*
const node_entry = priority_queue.
Top();
1230 if (StopSearchAndCleanup(&priority_queue)) {
1233 const int64_t node_to_insert = node_entry->node_to_insert();
1234 if (Contains(node_to_insert)) {
1235 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1239 const int entry_vehicle = node_entry->vehicle();
1240 if (entry_vehicle == -1) {
1243 SetValue(node_to_insert, node_to_insert);
1244 if (!Evaluate(
true).has_value()) {
1245 DeleteNodeEntry(node_entry, &priority_queue,
1246 &position_to_node_entries);
1252 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles)) {
1254 if (!InsertNodeEntryUsingEmptyVehicleTypeCurator(
1255 nodes_to_insert, all_vehicles, node_entry, &priority_queue,
1256 &position_to_node_entries)) {
1262 const int64_t insert_after = node_entry->insert_after();
1263 InsertBetween(node_to_insert, insert_after,
Value(insert_after));
1264 if (Evaluate(
true).has_value()) {
1265 if (!UpdatePositions(nodes_to_insert, entry_vehicle, node_to_insert,
1266 all_vehicles, &priority_queue,
1267 &position_to_node_entries) ||
1268 !UpdatePositions(nodes_to_insert, entry_vehicle, insert_after,
1269 all_vehicles, &priority_queue,
1270 &position_to_node_entries)) {
1273 SetVehicleIndex(node_to_insert, entry_vehicle);
1275 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1280 nodes_to_insert.erase(
1281 std::remove_if(nodes_to_insert.begin(), nodes_to_insert.end(),
1282 [
this](
int node) { return Contains(node); }),
1283 nodes_to_insert.end());
1288bool GlobalCheapestInsertionFilteredHeuristic::
1289 InsertNodeEntryUsingEmptyVehicleTypeCurator(
1290 const std::vector<int>&
nodes,
bool all_vehicles,
1291 GlobalCheapestInsertionFilteredHeuristic::NodeEntry*
const node_entry,
1293 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1295 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1296 position_to_node_entries) {
1297 const int entry_vehicle = node_entry->vehicle();
1298 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles));
1305 const int64_t node_to_insert = node_entry->node_to_insert();
1306 const int64_t entry_fixed_cost =
1307 model()->GetFixedCostOfVehicle(entry_vehicle);
1308 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1309 node_to_insert](
int vehicle) {
1310 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1314 DCHECK(VehicleIsEmpty(vehicle));
1315 InsertBetween(node_to_insert,
model()->Start(vehicle),
1316 model()->End(vehicle), vehicle);
1317 return Evaluate(
true).has_value();
1323 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1324 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1326 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1327 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1328 empty_vehicle_type_curator_->Type(entry_vehicle),
1329 vehicle_is_compatible, stop_and_return_vehicle);
1330 if (compatible_vehicle >= 0) {
1332 if (!UpdatePositions(
nodes, compatible_vehicle, node_to_insert,
1333 all_vehicles, priority_queue,
1334 position_to_node_entries)) {
1337 const int64_t compatible_start =
model()->Start(compatible_vehicle);
1338 const bool no_prior_entries_for_this_vehicle =
1339 position_to_node_entries->at(compatible_start).empty();
1340 if (!UpdatePositions(
nodes, compatible_vehicle, compatible_start,
1341 all_vehicles, priority_queue,
1342 position_to_node_entries)) {
1345 SetVehicleIndex(node_to_insert, compatible_vehicle);
1346 if (compatible_vehicle != entry_vehicle) {
1352 DCHECK(no_prior_entries_for_this_vehicle);
1358 const int new_empty_vehicle =
1359 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1360 empty_vehicle_type_curator_->Type(compatible_vehicle));
1362 if (new_empty_vehicle >= 0) {
1363 DCHECK(VehicleIsEmpty(new_empty_vehicle));
1366 if (!UpdatePositions(
nodes, new_empty_vehicle,
1367 model()->Start(new_empty_vehicle), all_vehicles,
1368 priority_queue, position_to_node_entries)) {
1372 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1376 DCHECK(VehicleIsEmpty(next_fixed_cost_empty_vehicle));
1379 position_to_node_entries->at(node_entry->insert_after()).erase(node_entry);
1380 node_entry->set_insert_after(
model()->Start(next_fixed_cost_empty_vehicle));
1381 position_to_node_entries->at(node_entry->insert_after()).insert(node_entry);
1382 node_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1383 UpdateNodeEntry(node_entry, priority_queue);
1385 DeleteNodeEntry(node_entry, priority_queue, position_to_node_entries);
1391bool GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1392 const std::map<int64_t, std::vector<int>>& nodes_by_bucket) {
1393 std::vector<bool> is_vehicle_used;
1394 absl::flat_hash_set<int> used_vehicles;
1395 std::vector<int> unused_vehicles;
1397 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1398 if (!used_vehicles.empty() &&
1399 !InsertNodesOnRoutes(nodes_by_bucket, used_vehicles)) {
1403 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1404 ComputeStartEndDistanceForVehicles(unused_vehicles);
1405 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1407 InitializePriorityQueue(&start_end_distances_per_node, &first_node_queue);
1409 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1412 while (vehicle >= 0) {
1413 if (!InsertNodesOnRoutes(nodes_by_bucket, {vehicle})) {
1416 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1422void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1423 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1424 absl::flat_hash_set<int>* used_vehicles) {
1425 is_vehicle_used->clear();
1426 is_vehicle_used->resize(
model()->vehicles());
1428 used_vehicles->clear();
1429 used_vehicles->reserve(
model()->vehicles());
1431 unused_vehicles->clear();
1432 unused_vehicles->reserve(
model()->vehicles());
1434 for (
int vehicle = 0; vehicle <
model()->vehicles(); vehicle++) {
1435 if (!VehicleIsEmpty(vehicle)) {
1436 (*is_vehicle_used)[vehicle] =
true;
1437 used_vehicles->insert(vehicle);
1439 (*is_vehicle_used)[vehicle] =
false;
1440 unused_vehicles->push_back(vehicle);
1445void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1446 if (gci_params_.farthest_seeds_ratio <= 0)
return;
1448 const int num_seeds =
static_cast<int>(
1449 std::ceil(gci_params_.farthest_seeds_ratio *
model()->vehicles()));
1451 std::vector<bool> is_vehicle_used;
1452 absl::flat_hash_set<int> used_vehicles;
1453 std::vector<int> unused_vehicles;
1454 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1455 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1456 ComputeStartEndDistanceForVehicles(unused_vehicles);
1460 std::priority_queue<Seed> farthest_node_queue;
1461 InitializePriorityQueue(&start_end_distances_per_node, &farthest_node_queue);
1463 int inserted_seeds = 0;
1464 while (inserted_seeds++ < num_seeds) {
1465 if (InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1466 &is_vehicle_used) < 0) {
1475 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1476 empty_vehicle_type_curator_->Update(
1477 [
this](
int vehicle) {
return !VehicleIsEmpty(vehicle); });
1480template <
class Queue>
1481int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1482 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1483 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
1484 while (!priority_queue->empty()) {
1485 if (StopSearch())
return -1;
1486 const Seed& seed = priority_queue->top();
1488 const int seed_node = seed.second;
1489 const int seed_vehicle = seed.first.vehicle;
1491 std::vector<StartEndValue>& other_start_end_values =
1492 (*start_end_distances_per_node)[seed_node];
1494 if (Contains(seed_node)) {
1497 priority_queue->pop();
1498 other_start_end_values.clear();
1501 if (!(*is_vehicle_used)[seed_vehicle]) {
1503 const int64_t
start =
model()->Start(seed_vehicle);
1504 const int64_t
end =
model()->End(seed_vehicle);
1506 InsertBetween(seed_node,
start,
end, seed_vehicle);
1507 if (Evaluate(
true).has_value()) {
1508 priority_queue->pop();
1509 (*is_vehicle_used)[seed_vehicle] =
true;
1510 other_start_end_values.clear();
1511 SetVehicleIndex(seed_node, seed_vehicle);
1512 return seed_vehicle;
1519 priority_queue->pop();
1520 if (!other_start_end_values.empty()) {
1521 const StartEndValue& next_seed_value = other_start_end_values.back();
1522 priority_queue->push(std::make_pair(next_seed_value, seed_node));
1523 other_start_end_values.pop_back();
1530bool GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1531 const std::vector<int>& pair_indices,
1533 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1534 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1536 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1537 delivery_to_entries) {
1538 priority_queue->Clear();
1539 pickup_to_entries->clear();
1540 pickup_to_entries->resize(
model()->Size());
1541 delivery_to_entries->clear();
1542 delivery_to_entries->resize(
model()->Size());
1543 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1544 model()->GetPickupAndDeliveryPairs();
1545 for (
int index : pair_indices) {
1546 const RoutingModel::IndexPair& index_pair = pickup_delivery_pairs[
index];
1547 for (int64_t pickup : index_pair.first) {
1548 if (Contains(pickup))
continue;
1549 for (int64_t delivery : index_pair.second) {
1550 if (Contains(delivery))
continue;
1551 if (StopSearchAndCleanup(priority_queue))
return false;
1556 if (gci_params_.add_unperformed_entries &&
1557 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1558 GetUnperformedValue(pickup) !=
1560 GetUnperformedValue(delivery) !=
1562 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1566 InitializeInsertionEntriesPerformingPair(
1567 pickup, delivery, priority_queue, pickup_to_entries,
1568 delivery_to_entries);
1575void GlobalCheapestInsertionFilteredHeuristic::
1576 InitializeInsertionEntriesPerformingPair(
1577 int64_t pickup, int64_t delivery,
1579 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1581 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1583 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1584 delivery_to_entries) {
1585 if (!gci_params_.use_neighbors_ratio_for_initialization) {
1586 struct PairInsertion {
1587 int64_t insert_pickup_after;
1588 int64_t insert_delivery_after;
1591 std::vector<PairInsertion> pair_insertions;
1592 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
1593 if (VehicleIsEmpty(vehicle) &&
1594 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1595 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1600 const int64_t
start =
model()->Start(vehicle);
1601 std::vector<NodeInsertion> pickup_insertions;
1603 &pickup_insertions);
1604 for (
const auto [insert_pickup_after, pickup_vehicle,
1605 unused_pickup_value] : pickup_insertions) {
1607 std::vector<NodeInsertion> delivery_insertions;
1608 AppendInsertionPositionsAfter(delivery, pickup,
1609 Value(insert_pickup_after), vehicle,
1610 &delivery_insertions);
1611 for (
const auto [insert_delivery_after, delivery_vehicle,
1612 unused_delivery_value] : delivery_insertions) {
1613 pair_insertions.push_back(
1614 {insert_pickup_after, insert_delivery_after, vehicle});
1618 for (
const auto& [insert_pickup_after, insert_delivery_after, vehicle] :
1620 DCHECK_NE(insert_pickup_after, insert_delivery_after);
1621 AddPairEntry(pickup, insert_pickup_after, delivery, insert_delivery_after,
1622 vehicle, priority_queue, pickup_to_entries,
1623 delivery_to_entries);
1630 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
1632 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1633 existing_insertion_positions;
1635 for (
const int64_t pickup_insert_after :
1636 GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
1637 if (!Contains(pickup_insert_after)) {
1640 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1642 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1646 if (VehicleIsEmpty(vehicle) &&
1647 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1648 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1654 int64_t delivery_insert_after = pickup;
1655 while (!
model()->IsEnd(delivery_insert_after)) {
1656 const std::pair<int64_t, int64_t> insertion_position = {
1657 pickup_insert_after, delivery_insert_after};
1658 DCHECK(!existing_insertion_positions.contains(insertion_position));
1659 existing_insertion_positions.insert(insertion_position);
1661 AddPairEntry(pickup, pickup_insert_after, delivery,
1662 delivery_insert_after, vehicle, priority_queue,
1663 pickup_to_entries, delivery_to_entries);
1664 delivery_insert_after = (delivery_insert_after == pickup)
1665 ?
Value(pickup_insert_after)
1666 :
Value(delivery_insert_after);
1671 for (
const int64_t delivery_insert_after :
1672 GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
1673 if (!Contains(delivery_insert_after)) {
1676 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1678 model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
1682 if (VehicleIsEmpty(vehicle)) {
1687 int64_t pickup_insert_after =
model()->Start(vehicle);
1688 while (pickup_insert_after != delivery_insert_after) {
1689 if (!existing_insertion_positions.contains(
1690 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1691 AddPairEntry(pickup, pickup_insert_after, delivery,
1692 delivery_insert_after, vehicle, priority_queue,
1693 pickup_to_entries, delivery_to_entries);
1695 pickup_insert_after =
Value(pickup_insert_after);
1701bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1702 const std::vector<int>& pair_indices,
int vehicle, int64_t pickup,
1703 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1705 std::vector<PairEntries>* pickup_to_entries,
1706 std::vector<PairEntries>* delivery_to_entries) {
1707 if (!UpdatePairPositions(pair_indices, vehicle, pickup_position,
1708 priority_queue, pickup_to_entries,
1709 delivery_to_entries) ||
1710 !UpdatePairPositions(pair_indices, vehicle, pickup, priority_queue,
1711 pickup_to_entries, delivery_to_entries) ||
1712 !UpdatePairPositions(pair_indices, vehicle, delivery, priority_queue,
1713 pickup_to_entries, delivery_to_entries)) {
1716 if (delivery_position != pickup &&
1717 !UpdatePairPositions(pair_indices, vehicle, delivery_position,
1718 priority_queue, pickup_to_entries,
1719 delivery_to_entries)) {
1722 SetVehicleIndex(pickup, vehicle);
1723 SetVehicleIndex(delivery, vehicle);
1727bool GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
1728 const std::vector<int>& pair_indices,
int vehicle,
1729 int64_t pickup_insert_after,
1731 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1732 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1734 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1735 delivery_to_entries) {
1738 using Pair = std::pair<int64_t, int64_t>;
1739 using Insertion = std::pair<Pair, int64_t>;
1740 absl::flat_hash_set<Insertion> existing_insertions;
1741 std::vector<PairEntry*> to_remove;
1742 for (PairEntry*
const pair_entry :
1743 pickup_to_entries->at(pickup_insert_after)) {
1745 DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
1746 if (Contains(pair_entry->pickup_to_insert()) ||
1747 Contains(pair_entry->delivery_to_insert())) {
1748 to_remove.push_back(pair_entry);
1750 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1751 .contains(pair_entry));
1752 UpdatePairEntry(pair_entry, priority_queue);
1753 existing_insertions.insert(
1754 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1755 pair_entry->delivery_insert_after()});
1758 for (PairEntry*
const pair_entry : to_remove) {
1759 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1760 delivery_to_entries);
1764 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
1765 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1766 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1767 model()->GetPickupAndDeliveryPairs();
1768 for (
int pair_index : pair_indices) {
1769 if (StopSearchAndCleanup(priority_queue))
return false;
1770 const RoutingModel::IndexPair& index_pair =
1771 pickup_delivery_pairs[pair_index];
1772 for (int64_t pickup : index_pair.first) {
1773 if (Contains(pickup) ||
1774 !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
1777 for (int64_t delivery : index_pair.second) {
1778 if (Contains(delivery)) {
1781 int64_t delivery_insert_after = pickup;
1782 while (!
model()->IsEnd(delivery_insert_after)) {
1783 const Insertion insertion = {{pickup, delivery},
1784 delivery_insert_after};
1785 if (!existing_insertions.contains(insertion)) {
1786 AddPairEntry(pickup, pickup_insert_after, delivery,
1787 delivery_insert_after, vehicle, priority_queue,
1788 pickup_to_entries, delivery_to_entries);
1790 if (delivery_insert_after == pickup) {
1791 delivery_insert_after = pickup_insert_before;
1793 delivery_insert_after =
Value(delivery_insert_after);
1802bool GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
1803 const std::vector<int>& pair_indices,
int vehicle,
1804 int64_t delivery_insert_after,
1806 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1807 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1809 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1810 delivery_to_entries) {
1813 using Pair = std::pair<int64_t, int64_t>;
1814 using Insertion = std::pair<Pair, int64_t>;
1815 absl::flat_hash_set<Insertion> existing_insertions;
1816 std::vector<PairEntry*> to_remove;
1817 for (PairEntry*
const pair_entry :
1818 delivery_to_entries->at(delivery_insert_after)) {
1820 DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
1821 if (Contains(pair_entry->pickup_to_insert()) ||
1822 Contains(pair_entry->delivery_to_insert())) {
1823 to_remove.push_back(pair_entry);
1825 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1826 .contains(pair_entry));
1827 existing_insertions.insert(
1828 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1829 pair_entry->pickup_insert_after()});
1830 UpdatePairEntry(pair_entry, priority_queue);
1833 for (PairEntry*
const pair_entry : to_remove) {
1834 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1835 delivery_to_entries);
1839 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
1840 const RoutingModel::IndexPairs& pickup_delivery_pairs =
1841 model()->GetPickupAndDeliveryPairs();
1842 for (
int pair_index : pair_indices) {
1843 if (StopSearchAndCleanup(priority_queue))
return false;
1844 const RoutingModel::IndexPair& index_pair =
1845 pickup_delivery_pairs[pair_index];
1846 for (int64_t delivery : index_pair.second) {
1847 if (Contains(delivery) ||
1848 !IsNeighborForCostClass(cost_class, delivery_insert_after,
1852 for (int64_t pickup : index_pair.first) {
1853 if (Contains(pickup)) {
1856 int64_t pickup_insert_after =
model()->Start(vehicle);
1857 while (pickup_insert_after != delivery_insert_after) {
1858 const Insertion insertion = {{pickup, delivery}, pickup_insert_after};
1859 if (!existing_insertions.contains(insertion)) {
1860 AddPairEntry(pickup, pickup_insert_after, delivery,
1861 delivery_insert_after, vehicle, priority_queue,
1862 pickup_to_entries, delivery_to_entries);
1864 pickup_insert_after =
Value(pickup_insert_after);
1872void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1873 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1875 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1876 std::vector<PairEntries>* pickup_to_entries,
1877 std::vector<PairEntries>* delivery_to_entries) {
1878 priority_queue->
Remove(entry);
1879 if (entry->pickup_insert_after() != -1) {
1880 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1882 if (entry->delivery_insert_after() != -1) {
1883 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1888void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1889 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1890 int64_t delivery_insert_after,
int vehicle,
1892 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1893 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1895 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1896 delivery_entries)
const {
1897 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
1898 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
1899 if (!pickup_vehicle_var->Contains(vehicle) ||
1900 !delivery_vehicle_var->Contains(vehicle)) {
1901 if (vehicle == -1 || !VehicleIsEmpty(vehicle))
return;
1904 const auto vehicle_is_compatible = [pickup_vehicle_var,
1905 delivery_vehicle_var](
int vehicle) {
1906 return pickup_vehicle_var->Contains(vehicle) &&
1907 delivery_vehicle_var->Contains(vehicle);
1909 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
1910 empty_vehicle_type_curator_->Type(vehicle),
1911 vehicle_is_compatible)) {
1915 const int num_allowed_vehicles =
1916 std::min(pickup_vehicle_var->Size(), delivery_vehicle_var->Size());
1917 if (pickup_insert_after == -1) {
1920 PairEntry* pair_entry =
1921 new PairEntry(pickup, -1, delivery, -1, -1, num_allowed_vehicles);
1922 pair_entry->set_value(
1923 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1925 :
CapAdd(GetUnperformedValue(pickup),
1926 GetUnperformedValue(delivery)));
1927 priority_queue->
Add(pair_entry);
1931 PairEntry*
const pair_entry =
1932 new PairEntry(pickup, pickup_insert_after, delivery,
1933 delivery_insert_after, vehicle, num_allowed_vehicles);
1934 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1935 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1939 pickup_entries->at(pickup_insert_after).insert(pair_entry);
1940 delivery_entries->at(delivery_insert_after).insert(pair_entry);
1941 priority_queue->
Add(pair_entry);
1944void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1945 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1947 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1949 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1950 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1951 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1952 pair_entry->vehicle()));
1960GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1961 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1962 int64_t delivery_insert_after,
int vehicle)
const {
1964 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1965 const int64_t pickup_value = GetInsertionCostForNodeAtPosition(
1966 pickup, pickup_insert_after, pickup_insert_before, vehicle);
1969 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1970 ? pickup_insert_before
1971 :
Value(delivery_insert_after);
1972 const int64_t delivery_value = GetInsertionCostForNodeAtPosition(
1973 delivery, delivery_insert_after, delivery_insert_before, vehicle);
1975 const int64_t penalty_shift =
1976 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1977 ?
CapAdd(GetUnperformedValue(pickup), GetUnperformedValue(delivery))
1979 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
1982bool GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1983 const std::vector<int>&
nodes,
const absl::flat_hash_set<int>& vehicles,
1985 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1986 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1987 position_to_node_entries) {
1988 priority_queue->
Clear();
1989 position_to_node_entries->clear();
1990 position_to_node_entries->resize(
model()->Size());
1992 const int num_vehicles =
1993 vehicles.empty() ?
model()->vehicles() : vehicles.size();
1994 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
1996 for (
int node :
nodes) {
1997 if (Contains(node)) {
2000 if (StopSearchAndCleanup(priority_queue))
return false;
2002 if (gci_params_.add_unperformed_entries &&
2004 AddNodeEntry(node, -1, -1, all_vehicles, priority_queue,
nullptr);
2007 InitializeInsertionEntriesPerformingNode(node, vehicles, priority_queue,
2008 position_to_node_entries);
2013void GlobalCheapestInsertionFilteredHeuristic::
2014 InitializeInsertionEntriesPerformingNode(
2015 int64_t node,
const absl::flat_hash_set<int>& vehicles,
2017 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
2019 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
2020 position_to_node_entries) {
2021 const int num_vehicles =
2022 vehicles.empty() ?
model()->vehicles() : vehicles.size();
2023 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
2025 if (!gci_params_.use_neighbors_ratio_for_initialization) {
2026 auto vehicles_it = vehicles.begin();
2027 for (
int v = 0; v < num_vehicles; v++) {
2028 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
2030 const int64_t
start =
model()->Start(vehicle);
2031 if (all_vehicles && VehicleIsEmpty(vehicle) &&
2032 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
2033 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
2038 std::vector<NodeInsertion> insertions;
2041 for (
const auto [insert_after, insertion_vehicle,
value] : insertions) {
2043 AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
2044 position_to_node_entries);
2052 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
2053 int v,
int cost_class) {
2054 return (
model()->GetCostClassIndexOfVehicle(v).value() == cost_class) &&
2055 (all_vehicles || vehicles.contains(v));
2057 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
2059 for (
const int64_t insert_after :
2060 GetNeighborsOfNodeForCostClass(cost_class, node)) {
2061 if (!Contains(insert_after)) {
2064 const int vehicle = node_index_to_vehicle_[insert_after];
2065 if (vehicle == -1 ||
2066 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
2069 if (all_vehicles && VehicleIsEmpty(vehicle) &&
2070 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
2071 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
2076 AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
2077 position_to_node_entries);
2082bool GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
2083 const std::vector<int>&
nodes,
int vehicle, int64_t insert_after,
2086 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
2087 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
2091 std::vector<NodeEntry*> to_remove;
2092 absl::flat_hash_set<int> existing_insertions;
2093 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
2094 DCHECK_EQ(node_entry->insert_after(), insert_after);
2095 const int64_t node_to_insert = node_entry->node_to_insert();
2096 if (Contains(node_to_insert)) {
2097 to_remove.push_back(node_entry);
2099 UpdateNodeEntry(node_entry, priority_queue);
2100 existing_insertions.insert(node_to_insert);
2103 for (NodeEntry*
const node_entry : to_remove) {
2104 DeleteNodeEntry(node_entry, priority_queue, node_entries);
2106 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
2107 for (
int node_to_insert :
nodes) {
2108 if (StopSearchAndCleanup(priority_queue))
return false;
2109 if (!Contains(node_to_insert) &&
2110 !existing_insertions.contains(node_to_insert) &&
2111 IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
2112 AddNodeEntry(node_to_insert, insert_after, vehicle, all_vehicles,
2113 priority_queue, node_entries);
2119void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
2120 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
2122 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
2123 std::vector<NodeEntries>* node_entries) {
2124 priority_queue->
Remove(entry);
2125 if (entry->insert_after() != -1) {
2126 node_entries->at(entry->insert_after()).erase(entry);
2131void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2132 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2134 std::vector<NodeEntries>* node_entries)
const {
2135 const int64_t node_penalty = GetUnperformedValue(node);
2136 const int64_t penalty_shift =
2137 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2140 const IntVar*
const vehicle_var =
model()->VehicleVar(node);
2141 if (!vehicle_var->Contains(vehicle)) {
2142 if (vehicle == -1 || !VehicleIsEmpty(vehicle))
return;
2145 const auto vehicle_is_compatible = [vehicle_var](
int vehicle) {
2146 return vehicle_var->Contains(vehicle);
2148 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2149 empty_vehicle_type_curator_->Type(vehicle),
2150 vehicle_is_compatible)) {
2154 const int num_allowed_vehicles = vehicle_var->Size();
2155 if (insert_after == -1) {
2157 if (!all_vehicles) {
2163 NodeEntry*
const node_entry =
2164 new NodeEntry(node, -1, -1, num_allowed_vehicles);
2165 node_entry->set_value(
CapSub(node_penalty, penalty_shift));
2166 priority_queue->
Add(node_entry);
2170 const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
2171 node, insert_after,
Value(insert_after), vehicle);
2172 if (!all_vehicles && insertion_cost > node_penalty) {
2179 NodeEntry*
const node_entry =
2180 new NodeEntry(node, insert_after, vehicle, num_allowed_vehicles);
2181 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
2184 node_entries->at(insert_after).insert(node_entry);
2185 priority_queue->
Add(node_entry);
2188void GlobalCheapestInsertionFilteredHeuristic::UpdateNodeEntry(
2189 NodeEntry*
const node_entry,
2191 const int64_t node = node_entry->node_to_insert();
2192 const int64_t insert_after = node_entry->insert_after();
2194 const int64_t insertion_cost = GetInsertionCostForNodeAtPosition(
2195 node, insert_after,
Value(insert_after), node_entry->vehicle());
2196 const int64_t penalty_shift =
2197 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2198 ? GetUnperformedValue(node)
2201 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
2209LocalCheapestInsertionFilteredHeuristic::
2210 LocalCheapestInsertionFilteredHeuristic(
2212 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
2213 bool evaluate_pickup_delivery_costs_independently,
2217 evaluate_pickup_delivery_costs_independently_(
2218 evaluate_pickup_delivery_costs_independently) {
2220 !evaluate_pickup_delivery_costs_independently_);
2222 std::iota(std::begin(all_vehicles),
std::end(all_vehicles), 0);
2224 start_end_distances_per_node_ =
2230 std::vector<bool> visited(
model()->
Size(),
false);
2235 struct PairDomainSize {
2236 uint64_t domain_size;
2239 bool operator<(
const PairDomainSize& other)
const {
2240 return std::tie(domain_size, pair_index) <
2241 std::tie(other.domain_size, other.pair_index);
2244 std::vector<PairDomainSize> pair_domain_sizes;
2245 for (
int pair_index = 0; pair_index < index_pairs.size(); ++pair_index) {
2247 for (int64_t pickup : index_pairs[pair_index].first) {
2250 for (int64_t delivery : index_pairs[pair_index].second) {
2254 pair_domain_sizes.push_back({domain_size, pair_index});
2256 std::sort(pair_domain_sizes.begin(), pair_domain_sizes.end());
2257 const auto insert_pair = [
this](int64_t pickup, int64_t insert_pickup_after,
2259 int64_t insert_delivery_after,
int vehicle) {
2260 const int64_t insert_pickup_before =
Value(insert_pickup_after);
2261 InsertBetween(pickup, insert_pickup_after, insert_pickup_before, vehicle);
2262 DCHECK_NE(insert_delivery_after, insert_pickup_after);
2263 const int64_t insert_delivery_before = (insert_delivery_after == pickup)
2264 ? insert_pickup_before
2265 :
Value(insert_delivery_after);
2266 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
2270 for (
const auto [unused_domain_size, pair_index] : pair_domain_sizes) {
2271 const auto& index_pair = index_pairs[pair_index];
2272 bool pair_inserted =
false;
2273 for (int64_t pickup : index_pair.first) {
2274 if (pair_inserted ||
Contains(pickup))
break;
2275 std::vector<NodeInsertion> pickup_insertion_positions;
2276 for (int64_t delivery : index_pair.second) {
2279 if (
Contains(delivery)) pair_inserted =
true;
2280 if (pair_inserted)
break;
2282 visited[pickup] =
true;
2283 visited[delivery] =
true;
2284 if (evaluate_pickup_delivery_costs_independently_) {
2286 if (pickup_insertion_positions.empty()) {
2287 pickup_insertion_positions =
2288 ComputeEvaluatorSortedPositions(pickup);
2290 for (
const auto [insert_pickup_after, pickup_vehicle,
2291 unused_pickup_value] : pickup_insertion_positions) {
2292 for (
const auto [insert_delivery_after, unused_delivery_vehicle,
2293 unused_delivery_value] :
2294 ComputeEvaluatorSortedPositionsOnRouteAfter(
2295 delivery, pickup,
Value(insert_pickup_after),
2297 if (insert_pair(pickup, insert_pickup_after, delivery,
2298 insert_delivery_after, pickup_vehicle)) {
2299 pair_inserted =
true;
2303 if (pair_inserted)
break;
2306 for (
const auto [insert_pickup_after, insert_delivery_after,
2307 unused_value, vehicle] :
2308 ComputeEvaluatorSortedPairPositions(pickup, delivery)) {
2309 if (insert_pair(pickup, insert_pickup_after, delivery,
2310 insert_delivery_after, vehicle)) {
2311 pair_inserted =
true;
2320 std::priority_queue<Seed> node_queue;
2324 while (!node_queue.empty()) {
2325 const int node = node_queue.top().second;
2327 if (
Contains(node) || visited[node])
continue;
2328 for (
const auto [insert_after, vehicle, unused_value] :
2329 ComputeEvaluatorSortedPositions(node)) {
2341std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
2342LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2345 std::vector<NodeInsertion> sorted_insertions;
2348 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2351 &sorted_insertions);
2353 std::sort(sorted_insertions.begin(), sorted_insertions.end());
2355 return sorted_insertions;
2358std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
2359LocalCheapestInsertionFilteredHeuristic::
2360 ComputeEvaluatorSortedPositionsOnRouteAfter(int64_t node, int64_t
start,
2361 int64_t next_after_start,
2364 std::vector<NodeInsertion> sorted_insertions;
2368 &sorted_insertions);
2369 std::sort(sorted_insertions.begin(), sorted_insertions.end());
2371 return sorted_insertions;
2374std::vector<LocalCheapestInsertionFilteredHeuristic::PickupDeliveryInsertion>
2375LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions(
2376 int64_t pickup, int64_t delivery) {
2377 std::vector<PickupDeliveryInsertion> sorted_pickup_delivery_insertions;
2381 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2382 int64_t insert_pickup_after =
model()->
Start(vehicle);
2383 while (!
model()->IsEnd(insert_pickup_after)) {
2384 const int64_t insert_pickup_before =
Value(insert_pickup_after);
2385 int64_t insert_delivery_after = pickup;
2386 while (!
model()->IsEnd(insert_delivery_after)) {
2387 const int64_t insert_delivery_before =
2388 insert_delivery_after == pickup ? insert_pickup_before
2389 :
Value(insert_delivery_after);
2391 InsertBetween(pickup, insert_pickup_after, insert_pickup_before,
2393 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
2395 std::optional<int64_t> insertion_cost =
Evaluate(
false);
2396 if (insertion_cost.has_value()) {
2397 sorted_pickup_delivery_insertions.push_back(
2398 {insert_pickup_after, insert_delivery_after, *insertion_cost,
2402 sorted_pickup_delivery_insertions.push_back(
2403 {insert_pickup_after, insert_delivery_after,
2405 pickup, insert_pickup_after, insert_pickup_before,
2408 delivery, insert_delivery_after,
2409 insert_delivery_before, vehicle)),
2412 insert_delivery_after = insert_delivery_before;
2414 insert_pickup_after = insert_pickup_before;
2417 std::sort(sorted_pickup_delivery_insertions.begin(),
2418 sorted_pickup_delivery_insertions.end());
2419 return sorted_pickup_delivery_insertions;
2431 std::vector<std::vector<int64_t>> deliveries(
Size());
2432 std::vector<std::vector<int64_t>> pickups(
Size());
2434 for (
int first : pair.first) {
2435 for (
int second : pair.second) {
2436 deliveries[first].push_back(second);
2437 pickups[second].push_back(first);
2444 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
2445 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2446 sorted_vehicles[vehicle] = vehicle;
2448 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
2449 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
2451 for (
const int vehicle : sorted_vehicles) {
2453 bool extend_route =
true;
2458 while (extend_route) {
2459 extend_route =
false;
2461 int64_t
index = last_node;
2470 std::vector<int64_t> neighbors;
2472 std::unique_ptr<IntVarIterator> it(
2473 model()->Nexts()[
index]->MakeDomainIterator(
false));
2475 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
2478 for (
int i = 0; !found && i < neighbors.size(); ++i) {
2482 next = FindTopSuccessor(
index, neighbors);
2485 SortSuccessors(
index, &neighbors);
2486 ABSL_FALLTHROUGH_INTENDED;
2488 next = neighbors[i];
2495 bool contains_pickups =
false;
2496 for (int64_t pickup : pickups[
next]) {
2498 contains_pickups =
true;
2502 if (!contains_pickups) {
2506 std::vector<int64_t> next_deliveries;
2507 if (
next < deliveries.size()) {
2508 next_deliveries = GetPossibleNextsFromIterator(
2511 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
2512 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
2517 delivery = FindTopSuccessor(
next, next_deliveries);
2520 SortSuccessors(
next, &next_deliveries);
2521 ABSL_FALLTHROUGH_INTENDED;
2523 delivery = next_deliveries[j];
2541 if (
model()->IsEnd(
end) && last_node != delivery) {
2542 last_node = delivery;
2543 extend_route =
true;
2558bool CheapestAdditionFilteredHeuristic::
2559 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
2560 int vehicle2)
const {
2561 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2562 builder_.GetStartChainEnd(vehicle1));
2563 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
2564 builder_.GetStartChainEnd(vehicle2));
2565 if (has_partial_route1 == has_partial_route2) {
2566 return vehicle2 < vehicle1;
2568 return has_partial_route2 < has_partial_route1;
2581int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2582 int64_t node,
const std::vector<int64_t>& successors) {
2584 int64_t best_successor = -1;
2585 for (int64_t successor : successors) {
2586 const int64_t evaluation = (successor >= 0)
2587 ? evaluator_(node, successor)
2589 if (evaluation < best_evaluation ||
2590 (evaluation == best_evaluation && successor > best_successor)) {
2591 best_evaluation = evaluation;
2592 best_successor = successor;
2595 return best_successor;
2598void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2599 int64_t node, std::vector<int64_t>* successors) {
2600 std::vector<std::pair<int64_t, int64_t>> values;
2601 values.reserve(successors->size());
2602 for (int64_t successor : *successors) {
2605 values.push_back({evaluator_(node, successor), -successor});
2607 std::sort(values.begin(), values.end());
2608 successors->clear();
2609 for (
auto value : values) {
2610 successors->push_back(-
value.second);
2621 comparator_(
std::move(comparator)) {}
2623int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2624 int64_t node,
const std::vector<int64_t>& successors) {
2625 return *std::min_element(successors.begin(), successors.end(),
2626 [
this, node](
int successor1,
int successor2) {
2627 return comparator_(node, successor1, successor2);
2631void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2632 int64_t node, std::vector<int64_t>* successors) {
2633 std::sort(successors->begin(), successors->end(),
2634 [
this, node](
int successor1,
int successor2) {
2635 return comparator_(node, successor1, successor2);
2687template <
typename Saving>
2692 : savings_db_(savings_db),
2693 index_in_sorted_savings_(0),
2694 vehicle_types_(vehicle_types),
2695 single_vehicle_type_(vehicle_types == 1),
2696 using_incoming_reinjected_saving_(false),
2701 sorted_savings_per_vehicle_type_.clear();
2702 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
2703 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2704 savings.reserve(size * saving_neighbors);
2707 sorted_savings_.clear();
2708 costs_and_savings_per_arc_.clear();
2709 arc_indices_per_before_node_.clear();
2711 if (!single_vehicle_type_) {
2712 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
2713 arc_indices_per_before_node_.resize(size);
2714 for (
int before_node = 0; before_node < size; before_node++) {
2715 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
2718 skipped_savings_starting_at_.clear();
2719 skipped_savings_starting_at_.resize(size);
2720 skipped_savings_ending_at_.clear();
2721 skipped_savings_ending_at_.resize(size);
2722 incoming_reinjected_savings_ =
nullptr;
2723 outgoing_reinjected_savings_ =
nullptr;
2724 incoming_new_reinjected_savings_ =
nullptr;
2725 outgoing_new_reinjected_savings_ =
nullptr;
2729 int64_t before_node, int64_t after_node,
int vehicle_type) {
2730 CHECK(!sorted_savings_per_vehicle_type_.empty())
2731 <<
"Container not initialized!";
2732 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
2733 UpdateArcIndicesCostsAndSavings(before_node, after_node,
2734 {total_cost, saving});
2738 CHECK(!sorted_) <<
"Container already sorted!";
2740 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2741 std::sort(savings.begin(), savings.end());
2744 if (single_vehicle_type_) {
2745 const auto& savings = sorted_savings_per_vehicle_type_[0];
2746 sorted_savings_.resize(savings.size());
2747 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
2748 [](
const Saving& saving) {
2749 return SavingAndArc({saving, -1});
2755 sorted_savings_.reserve(vehicle_types_ *
2756 costs_and_savings_per_arc_.size());
2758 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
2760 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2761 costs_and_savings_per_arc_[arc_index];
2762 DCHECK(!costs_and_savings.empty());
2765 costs_and_savings.begin(), costs_and_savings.end(),
2766 [](
const std::pair<int64_t, Saving>& cs1,
2767 const std::pair<int64_t, Saving>& cs2) { return cs1 > cs2; });
2772 const int64_t
cost = costs_and_savings.back().first;
2773 while (!costs_and_savings.empty() &&
2774 costs_and_savings.back().first ==
cost) {
2775 sorted_savings_.push_back(
2776 {costs_and_savings.back().second, arc_index});
2777 costs_and_savings.pop_back();
2780 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2781 next_saving_type_and_index_for_arc_.clear();
2782 next_saving_type_and_index_for_arc_.resize(
2783 costs_and_savings_per_arc_.size(), {-1, -1});
2786 index_in_sorted_savings_ = 0;
2791 return index_in_sorted_savings_ < sorted_savings_.size() ||
2792 HasReinjectedSavings();
2796 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
2798 <<
"Update() should be called between two calls to GetSaving() !";
2802 if (HasReinjectedSavings()) {
2803 if (incoming_reinjected_savings_ !=
nullptr &&
2804 outgoing_reinjected_savings_ !=
nullptr) {
2806 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
2807 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
2808 if (incoming_saving < outgoing_saving) {
2809 current_saving_ = incoming_saving;
2810 using_incoming_reinjected_saving_ =
true;
2812 current_saving_ = outgoing_saving;
2813 using_incoming_reinjected_saving_ =
false;
2816 if (incoming_reinjected_savings_ !=
nullptr) {
2817 current_saving_ = incoming_reinjected_savings_->front();
2818 using_incoming_reinjected_saving_ =
true;
2820 if (outgoing_reinjected_savings_ !=
nullptr) {
2821 current_saving_ = outgoing_reinjected_savings_->front();
2822 using_incoming_reinjected_saving_ =
false;
2826 current_saving_ = sorted_savings_[index_in_sorted_savings_];
2828 return current_saving_.saving;
2831 void Update(
bool update_best_saving,
int type = -1) {
2832 CHECK(to_update_) <<
"Container already up to date!";
2833 if (update_best_saving) {
2834 const int64_t arc_index = current_saving_.arc_index;
2835 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
2837 if (!HasReinjectedSavings()) {
2838 index_in_sorted_savings_++;
2840 if (index_in_sorted_savings_ == sorted_savings_.size()) {
2841 sorted_savings_.swap(next_savings_);
2843 index_in_sorted_savings_ = 0;
2845 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2846 next_saving_type_and_index_for_arc_.clear();
2847 next_saving_type_and_index_for_arc_.resize(
2848 costs_and_savings_per_arc_.size(), {-1, -1});
2851 UpdateReinjectedSavings();
2856 CHECK(!single_vehicle_type_);
2857 Update(
true, type);
2861 CHECK(sorted_) <<
"Savings not sorted yet!";
2863 return sorted_savings_per_vehicle_type_[type];
2867 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
2868 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
2872 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
2873 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
2877 struct SavingAndArc {
2881 bool operator<(
const SavingAndArc& other)
const {
2882 return std::tie(saving, arc_index) <
2883 std::tie(other.saving, other.arc_index);
2889 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
2890 const Saving& saving = saving_and_arc.saving;
2891 const int64_t before_node = savings_db_->GetBeforeNodeFromSaving(saving);
2892 const int64_t after_node = savings_db_->GetAfterNodeFromSaving(saving);
2893 if (!savings_db_->Contains(before_node)) {
2894 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
2896 if (!savings_db_->Contains(after_node)) {
2897 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
2911 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
2912 if (single_vehicle_type_) {
2915 SkipSavingForArc(current_saving_);
2919 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
2920 const int previous_index = type_and_index.second;
2921 const int previous_type = type_and_index.first;
2922 bool next_saving_added =
false;
2925 if (previous_index >= 0) {
2928 if (type == -1 || previous_type == type) {
2931 next_saving_added =
true;
2932 next_saving = next_savings_[previous_index].saving;
2936 if (!next_saving_added &&
2937 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
2938 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
2939 if (previous_index >= 0) {
2941 next_savings_[previous_index] = {next_saving, arc_index};
2944 type_and_index.second = next_savings_.size();
2945 next_savings_.push_back({next_saving, arc_index});
2947 next_saving_added =
true;
2953 SkipSavingForArc(current_saving_);
2957 if (next_saving_added) {
2958 SkipSavingForArc({next_saving, arc_index});
2963 void UpdateReinjectedSavings() {
2964 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
2965 &incoming_reinjected_savings_,
2966 using_incoming_reinjected_saving_);
2967 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
2968 &outgoing_reinjected_savings_,
2969 !using_incoming_reinjected_saving_);
2970 incoming_new_reinjected_savings_ =
nullptr;
2971 outgoing_new_reinjected_savings_ =
nullptr;
2974 void UpdateGivenReinjectedSavings(
2975 std::deque<SavingAndArc>* new_reinjected_savings,
2976 std::deque<SavingAndArc>** reinjected_savings,
2977 bool using_reinjected_savings) {
2978 if (new_reinjected_savings ==
nullptr) {
2980 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
2981 CHECK(!(*reinjected_savings)->empty());
2982 (*reinjected_savings)->pop_front();
2983 if ((*reinjected_savings)->empty()) {
2984 *reinjected_savings =
nullptr;
2993 if (*reinjected_savings !=
nullptr) {
2994 (*reinjected_savings)->clear();
2996 *reinjected_savings =
nullptr;
2997 if (!new_reinjected_savings->empty()) {
2998 *reinjected_savings = new_reinjected_savings;
3002 bool HasReinjectedSavings() {
3003 return outgoing_reinjected_savings_ !=
nullptr ||
3004 incoming_reinjected_savings_ !=
nullptr;
3007 void UpdateArcIndicesCostsAndSavings(
3008 int64_t before_node, int64_t after_node,
3009 const std::pair<int64_t, Saving>& cost_and_saving) {
3010 if (single_vehicle_type_) {
3013 absl::flat_hash_map<int, int>& arc_indices =
3014 arc_indices_per_before_node_[before_node];
3015 const auto& arc_inserted = arc_indices.insert(
3016 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
3017 const int index = arc_inserted.first->second;
3018 if (arc_inserted.second) {
3019 costs_and_savings_per_arc_.push_back({cost_and_saving});
3022 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
3026 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
3027 Saving* next_saving) {
3028 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
3029 costs_and_savings_per_arc_[arc_index];
3031 bool found_saving =
false;
3032 while (!costs_and_savings.empty() && !found_saving) {
3033 const Saving& saving = costs_and_savings.back().second;
3034 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
3035 *next_saving = saving;
3036 found_saving =
true;
3038 costs_and_savings.pop_back();
3040 return found_saving;
3043 const SavingsFilteredHeuristic*
const savings_db_;
3044 int64_t index_in_sorted_savings_;
3045 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
3046 std::vector<SavingAndArc> sorted_savings_;
3047 std::vector<SavingAndArc> next_savings_;
3048 std::vector<std::pair< int,
int>>
3049 next_saving_type_and_index_for_arc_;
3050 SavingAndArc current_saving_;
3051 std::vector<std::vector<std::pair< int64_t, Saving>>>
3052 costs_and_savings_per_arc_;
3053 std::vector<absl::flat_hash_map< int,
int>>
3054 arc_indices_per_before_node_;
3055 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
3056 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
3057 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
3058 std::deque<SavingAndArc>* incoming_reinjected_savings_;
3059 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
3060 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
3061 const int vehicle_types_;
3062 const bool single_vehicle_type_;
3063 bool using_incoming_reinjected_saving_;
3074 vehicle_type_curator_(nullptr),
3081 size_squared_ = size * size;
3089 model()->GetVehicleTypeContainer());
3094 if (!ComputeSavings())
return false;
3099 if (!
Evaluate(
true).has_value())
return false;
3105 int type, int64_t before_node, int64_t after_node) {
3106 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
3122 ->GetCompatibleVehicleOfType(
3123 type, vehicle_is_compatible,
3124 [](
int) {
return false; })
3128void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
3129 std::vector<std::vector<int64_t>>* adjacency_lists) {
3130 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
3131 for (int64_t neighbor : (*adjacency_lists)[node]) {
3132 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
3135 (*adjacency_lists)[neighbor].push_back(node);
3138 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
3139 adjacency_lists->begin(), [](std::vector<int64_t> vec) {
3140 std::sort(vec.begin(), vec.end());
3141 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
3157bool SavingsFilteredHeuristic::ComputeSavings() {
3161 std::vector<int64_t> uncontained_non_start_end_nodes;
3162 uncontained_non_start_end_nodes.reserve(size);
3163 for (
int node = 0; node < size; node++) {
3165 uncontained_non_start_end_nodes.push_back(node);
3169 const int64_t saving_neighbors =
3170 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
3171 static_cast<int64_t
>(uncontained_non_start_end_nodes.size()));
3174 absl::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
3177 std::vector<std::vector<int64_t>> adjacency_lists(size);
3179 for (
int type = 0; type < num_vehicle_types; ++type) {
3186 const int64_t cost_class =
3194 for (
int before_node : uncontained_non_start_end_nodes) {
3195 std::vector<std::pair< int64_t, int64_t>>
3197 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
3199 for (
int after_node : uncontained_non_start_end_nodes) {
3200 if (after_node != before_node) {
3201 costed_after_nodes.push_back(std::make_pair(
3202 model()->GetArcCostForClass(before_node, after_node, cost_class),
3206 if (saving_neighbors < costed_after_nodes.size()) {
3207 std::nth_element(costed_after_nodes.begin(),
3208 costed_after_nodes.begin() + saving_neighbors,
3209 costed_after_nodes.end());
3210 costed_after_nodes.resize(saving_neighbors);
3212 adjacency_lists[before_node].resize(costed_after_nodes.size());
3213 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
3214 adjacency_lists[before_node].begin(),
3215 [](std::pair<int64_t, int64_t> cost_and_node) {
3216 return cost_and_node.second;
3220 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
3225 for (
int before_node : uncontained_non_start_end_nodes) {
3226 const int64_t before_to_end_cost =
3228 const int64_t start_to_before_cost =
3232 for (int64_t after_node : adjacency_lists[before_node]) {
3233 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
3234 before_node == after_node ||
Contains(after_node)) {
3237 const int64_t arc_cost =
3239 const int64_t start_to_after_cost =
3242 const int64_t after_to_end_cost =
3245 const double weighted_arc_cost_fp =
3247 const int64_t weighted_arc_cost =
3249 ?
static_cast<int64_t
>(weighted_arc_cost_fp)
3250 :
std::numeric_limits<int64_t>::
max();
3251 const int64_t saving_value =
CapSub(
3252 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
3255 BuildSaving(-saving_value, type, before_node, after_node);
3257 const int64_t total_cost =
3258 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
3269int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
3270 int num_vehicle_types)
const {
3273 const int64_t num_neighbors_with_ratio =
3280 const double max_memory_usage_in_savings_unit =
3298 if (num_vehicle_types > 1) {
3299 multiplicative_factor += 1.5;
3301 const double num_savings =
3302 max_memory_usage_in_savings_unit / multiplicative_factor;
3303 const int64_t num_neighbors_with_memory_restriction =
3304 std::max(1.0, num_savings / (num_vehicle_types * size));
3306 return std::min(num_neighbors_with_ratio,
3307 num_neighbors_with_memory_restriction);
3312void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3318 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
3319 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
3320 for (
int type = 0; type < vehicle_types; type++) {
3321 const int vehicle_type_offset = type * size;
3322 const std::vector<Saving>& sorted_savings_for_type =
3324 for (
const Saving& saving : sorted_savings_for_type) {
3327 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3329 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3340 const bool nodes_not_contained =
3343 bool committed =
false;
3345 if (nodes_not_contained) {
3358 const int saving_offset = type * size;
3360 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
3362 out_savings_ptr[saving_offset + before_node].size()) {
3365 int before_before_node = -1;
3366 int after_after_node = -1;
3367 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
3368 const Saving& in_saving =
3369 *(in_savings_ptr[saving_offset + after_node][in_index]);
3371 out_savings_ptr[saving_offset + before_node].size()) {
3372 const Saving& out_saving =
3373 *(out_savings_ptr[saving_offset + before_node][out_index]);
3388 *(out_savings_ptr[saving_offset + before_node][out_index]));
3391 if (after_after_node != -1) {
3395 SetValue(after_node, after_after_node);
3399 after_node = after_after_node;
3409 if (!
Contains(before_before_node)) {
3411 SetValue(before_before_node, before_node);
3414 before_node = before_before_node;
3431void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3437 first_node_on_route_.resize(vehicles, -1);
3438 last_node_on_route_.resize(vehicles, -1);
3439 vehicle_of_first_or_last_node_.resize(size, -1);
3441 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
3449 vehicle_of_first_or_last_node_[node] = vehicle;
3450 first_node_on_route_[vehicle] = node;
3457 vehicle_of_first_or_last_node_[node] = vehicle;
3458 last_node_on_route_[vehicle] = node;
3471 bool committed =
false;
3479 vehicle_of_first_or_last_node_[before_node] = vehicle;
3480 vehicle_of_first_or_last_node_[after_node] = vehicle;
3481 first_node_on_route_[vehicle] = before_node;
3482 last_node_on_route_[vehicle] = after_node;
3494 const int v1 = vehicle_of_first_or_last_node_[before_node];
3495 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3497 const int v2 = vehicle_of_first_or_last_node_[after_node];
3498 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3500 if (before_node == last_node && after_node == first_node && v1 != v2 &&
3509 MergeRoutes(v1, v2, before_node, after_node);
3514 const int vehicle = vehicle_of_first_or_last_node_[before_node];
3515 const int64_t last_node =
3516 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3518 if (before_node == last_node) {
3523 if (type != route_type) {
3534 if (first_node_on_route_[vehicle] != before_node) {
3537 vehicle_of_first_or_last_node_[before_node] = -1;
3539 vehicle_of_first_or_last_node_[after_node] = vehicle;
3540 last_node_on_route_[vehicle] = after_node;
3547 const int vehicle = vehicle_of_first_or_last_node_[after_node];
3548 const int64_t first_node =
3549 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3551 if (after_node == first_node) {
3556 if (type != route_type) {
3567 if (last_node_on_route_[vehicle] != after_node) {
3570 vehicle_of_first_or_last_node_[after_node] = -1;
3572 vehicle_of_first_or_last_node_[before_node] = vehicle;
3573 first_node_on_route_[vehicle] = before_node;
3582void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
3584 int64_t before_node,
3585 int64_t after_node) {
3587 const int64_t new_first_node = first_node_on_route_[first_vehicle];
3588 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
3590 const int64_t new_last_node = last_node_on_route_[second_vehicle];
3591 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
3595 int used_vehicle = first_vehicle;
3596 int unused_vehicle = second_vehicle;
3597 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
3598 model()->GetFixedCostOfVehicle(second_vehicle)) {
3599 used_vehicle = second_vehicle;
3600 unused_vehicle = first_vehicle;
3605 if (used_vehicle == first_vehicle) {
3610 bool committed =
Evaluate(
true).has_value();
3612 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
3613 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
3615 std::swap(used_vehicle, unused_vehicle);
3618 if (used_vehicle == first_vehicle) {
3623 committed =
Evaluate(
true).has_value();
3629 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
3630 model()->GetFixedCostOfVehicle(unused_vehicle));
3633 first_node_on_route_[unused_vehicle] = -1;
3634 last_node_on_route_[unused_vehicle] = -1;
3635 vehicle_of_first_or_last_node_[before_node] = -1;
3636 vehicle_of_first_or_last_node_[after_node] = -1;
3637 first_node_on_route_[used_vehicle] = new_first_node;
3638 last_node_on_route_[used_vehicle] = new_last_node;
3639 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
3640 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
3648 bool use_minimum_matching)
3650 use_minimum_matching_(use_minimum_matching) {}
3660 std::vector<int> indices(1, 0);
3661 for (
int i = 1; i < size; ++i) {
3662 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
3663 indices.push_back(i);
3667 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
3668 std::vector<bool> class_covered(num_cost_classes,
false);
3669 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3670 const int64_t cost_class =
3672 if (!class_covered[cost_class]) {
3673 class_covered[cost_class] =
true;
3676 auto cost = [
this, &indices,
start,
end, cost_class](
int from,
int to) {
3679 const int from_index = (from == 0) ?
start : indices[from];
3680 const int to_index = (to == 0) ?
end : indices[to];
3681 const int64_t
cost =
3689 using Cost =
decltype(
cost);
3691 indices.size(),
cost);
3692 if (use_minimum_matching_) {
3695 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
3697 if (christofides_solver.
Solve()) {
3698 path_per_cost_class[cost_class] =
3704 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3705 const int64_t cost_class =
3707 const std::vector<int>& path = path_per_cost_class[cost_class];
3708 if (path.empty())
continue;
3714 for (
int i = 1; i < path.size() - 1 && prev !=
end; ++i) {
3716 int next = indices[path[i]];
3744struct SweepIndexSortAngle {
3745 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3746 return (node1.angle < node2.angle);
3748} SweepIndexAngleComparator;
3750struct SweepIndexSortDistance {
3751 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3752 return (node1.distance < node2.distance);
3754} SweepIndexDistanceComparator;
3758 const std::vector<std::pair<int64_t, int64_t>>& points)
3759 : coordinates_(2 * points.size(), 0), sectors_(1) {
3760 for (int64_t i = 0; i < points.size(); ++i) {
3761 coordinates_[2 * i] = points[i].first;
3762 coordinates_[2 * i + 1] = points[i].second;
3769 const double pi_rad = 3.14159265;
3771 const int x0 = coordinates_[0];
3772 const int y0 = coordinates_[1];
3774 std::vector<SweepIndex> sweep_indices;
3775 for (int64_t
index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3777 const int x = coordinates_[2 *
index];
3778 const int y = coordinates_[2 *
index + 1];
3779 const double x_delta = x - x0;
3780 const double y_delta = y - y0;
3781 double square_distance = x_delta * x_delta + y_delta * y_delta;
3782 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3784 SweepIndex sweep_index(
index,
angle, square_distance);
3785 sweep_indices.push_back(sweep_index);
3787 std::sort(sweep_indices.begin(), sweep_indices.end(),
3788 SweepIndexDistanceComparator);
3790 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
3791 for (
int sector = 0; sector < sectors_; ++sector) {
3792 std::vector<SweepIndex> cluster;
3793 std::vector<SweepIndex>::iterator begin =
3794 sweep_indices.begin() + sector * size;
3795 std::vector<SweepIndex>::iterator
end =
3796 sector == sectors_ - 1 ? sweep_indices.end()
3797 : sweep_indices.begin() + (sector + 1) * size;
3798 std::sort(begin,
end, SweepIndexAngleComparator);
3800 for (
const SweepIndex& sweep_index : sweep_indices) {
3801 indices->push_back(sweep_index.index);
3829class RouteConstructor {
3831 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
3832 bool check_assignment, int64_t num_indices,
3833 const std::vector<Link>& links_list)
3834 : assignment_(assignment),
3836 check_assignment_(check_assignment),
3837 solver_(model_->solver()),
3838 num_indices_(num_indices),
3839 links_list_(links_list),
3840 nexts_(model_->Nexts()),
3841 in_route_(num_indices_, -1),
3843 index_to_chain_index_(num_indices, -1),
3844 index_to_vehicle_class_index_(num_indices, -1) {
3846 const std::vector<std::string> dimension_names =
3847 model_->GetAllDimensionNames();
3848 dimensions_.assign(dimension_names.size(),
nullptr);
3849 for (
int i = 0; i < dimension_names.size(); ++i) {
3850 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
3853 cumuls_.resize(dimensions_.size());
3854 for (std::vector<int64_t>& cumuls :
cumuls_) {
3855 cumuls.resize(num_indices_);
3857 new_possible_cumuls_.resize(dimensions_.size());
3860 ~RouteConstructor() {}
3863 model_->solver()->TopPeriodicCheck();
3866 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
3867 std::vector<int> route(1,
index);
3868 routes_.push_back(route);
3869 in_route_[
index] = routes_.size() - 1;
3873 for (
const Link&
link : links_list_) {
3874 model_->solver()->TopPeriodicCheck();
3875 const int index1 =
link.link.first;
3876 const int index2 =
link.link.second;
3882 if (index_to_vehicle_class_index_[index1] < 0) {
3883 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3884 ++dimension_index) {
3885 cumuls_[dimension_index][index1] =
3886 std::max(dimensions_[dimension_index]->GetTransitValue(
3888 dimensions_[dimension_index]->CumulVar(index1)->Min());
3891 if (index_to_vehicle_class_index_[index2] < 0) {
3892 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3893 ++dimension_index) {
3894 cumuls_[dimension_index][index2] =
3895 std::max(dimensions_[dimension_index]->GetTransitValue(
3897 dimensions_[dimension_index]->CumulVar(index2)->Min());
3901 const int route_index1 = in_route_[index1];
3902 const int route_index2 = in_route_[index2];
3904 route_index1 >= 0 && route_index2 >= 0 &&
3905 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
3908 if (Merge(merge, route_index1, route_index2)) {
3914 model_->solver()->TopPeriodicCheck();
3918 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3919 if (!deleted_chains_.contains(chain_index)) {
3920 final_chains_.push_back(chains_[chain_index]);
3923 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
3924 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
3925 if (!deleted_routes_.contains(route_index)) {
3926 final_routes_.push_back(routes_[route_index]);
3929 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
3931 const int extra_vehicles =
std::max(
3932 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
3934 int chain_index = 0;
3935 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
3937 if (chain_index - extra_vehicles >= model_->vehicles()) {
3940 const int start = final_chains_[chain_index].head;
3941 const int end = final_chains_[chain_index].tail;
3943 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3944 assignment_->SetValue(
3945 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
start);
3946 assignment_->Add(nexts_[
end]);
3947 assignment_->SetValue(nexts_[
end],
3948 model_->End(chain_index - extra_vehicles));
3952 for (
int route_index = 0; route_index < final_routes_.size();
3954 if (chain_index - extra_vehicles >= model_->vehicles()) {
3957 DCHECK_LT(route_index, final_routes_.size());
3958 const int head = final_routes_[route_index].front();
3959 const int tail = final_routes_[route_index].back();
3960 if (
head ==
tail && head < model_->Size()) {
3962 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3963 assignment_->SetValue(
3964 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
3965 assignment_->Add(nexts_[
tail]);
3966 assignment_->SetValue(nexts_[
tail],
3967 model_->End(chain_index - extra_vehicles));
3975 if (!assignment_->Contains(
next)) {
3976 assignment_->Add(
next);
3985 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
3988 bool operator()(
const std::vector<int>& route1,
3989 const std::vector<int>& route2)
const {
3990 return (route1.size() < route2.size());
4001 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
4002 return (chain1.nodes < chain2.nodes);
4006 bool Head(
int node)
const {
4007 return (node == routes_[in_route_[node]].front());
4010 bool Tail(
int node)
const {
4011 return (node == routes_[in_route_[node]].back());
4014 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
4015 int dimension_index) {
4016 const RoutingDimension& dimension = *dimensions_[dimension_index];
4017 std::vector<int>::const_iterator it = route.begin();
4018 int64_t cumul = route_cumul;
4019 while (it != route.end()) {
4020 const int previous = *it;
4021 const int64_t cumul_previous = cumul;
4025 if (it == route.end()) {
4028 const int next = *it;
4029 int64_t available_from_previous =
4030 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
4031 int64_t available_cumul_next =
4034 const int64_t slack = available_cumul_next - available_from_previous;
4035 if (slack > dimension.SlackVar(previous)->Max()) {
4036 available_cumul_next =
4037 available_from_previous + dimension.SlackVar(previous)->Max();
4040 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
4043 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
4046 cumul = available_cumul_next;
4051 bool CheckRouteConnection(
const std::vector<int>& route1,
4052 const std::vector<int>& route2,
int dimension_index,
4054 const int tail1 = route1.back();
4055 const int head2 = route2.front();
4056 const int tail2 = route2.back();
4057 const RoutingDimension& dimension = *dimensions_[dimension_index];
4058 int non_depot_node = -1;
4059 for (
int node = 0; node < num_indices_; ++node) {
4060 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
4061 non_depot_node = node;
4066 const int64_t depot_threshold =
4067 std::max(dimension.SlackVar(non_depot_node)->Max(),
4068 dimension.CumulVar(non_depot_node)->Max());
4070 int64_t available_from_tail1 =
cumuls_[dimension_index][tail1] +
4071 dimension.GetTransitValue(tail1, head2, 0);
4072 int64_t new_available_cumul_head2 =
4075 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
4076 if (slack > dimension.SlackVar(tail1)->Max()) {
4077 new_available_cumul_head2 =
4078 available_from_tail1 + dimension.SlackVar(tail1)->Max();
4081 bool feasible_route =
true;
4082 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
4085 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
4090 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
4091 const int64_t new_possible_cumul_tail2 =
4092 new_possible_cumuls_[dimension_index].contains(tail2)
4093 ? new_possible_cumuls_[dimension_index][tail2]
4094 :
cumuls_[dimension_index][tail2];
4096 if (!feasible_route || (new_possible_cumul_tail2 +
4097 dimension.GetTransitValue(tail2,
end_depot, 0) >
4104 bool FeasibleMerge(
const std::vector<int>& route1,
4105 const std::vector<int>& route2,
int node1,
int node2,
4108 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
4113 if (!((index_to_vehicle_class_index_[node1] == -1 &&
4114 index_to_vehicle_class_index_[node2] == -1) ||
4116 index_to_vehicle_class_index_[node2] == -1) ||
4117 (index_to_vehicle_class_index_[node1] == -1 &&
4126 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4127 ++dimension_index) {
4128 new_possible_cumuls_[dimension_index].clear();
4129 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
4138 bool CheckTempAssignment(Assignment*
const temp_assignment,
4139 int new_chain_index,
int old_chain_index,
int head1,
4140 int tail1,
int head2,
int tail2) {
4143 if (new_chain_index >= model_->vehicles())
return false;
4144 const int start = head1;
4145 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
4146 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
4148 temp_assignment->Add(nexts_[tail1]);
4149 temp_assignment->SetValue(nexts_[tail1], head2);
4150 temp_assignment->Add(nexts_[tail2]);
4151 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
4152 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4153 if ((chain_index != new_chain_index) &&
4154 (chain_index != old_chain_index) &&
4155 (!deleted_chains_.contains(chain_index))) {
4156 const int start = chains_[chain_index].head;
4157 const int end = chains_[chain_index].tail;
4158 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
4159 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
4161 temp_assignment->Add(nexts_[
end]);
4162 temp_assignment->SetValue(nexts_[
end], model_->End(chain_index));
4165 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
4168 bool UpdateAssignment(
const std::vector<int>& route1,
4169 const std::vector<int>& route2) {
4170 bool feasible =
true;
4171 const int head1 = route1.front();
4172 const int tail1 = route1.back();
4173 const int head2 = route2.front();
4174 const int tail2 = route2.back();
4175 const int chain_index1 = index_to_chain_index_[head1];
4176 const int chain_index2 = index_to_chain_index_[head2];
4177 if (chain_index1 < 0 && chain_index2 < 0) {
4178 const int chain_index = chains_.size();
4179 if (check_assignment_) {
4180 Assignment*
const temp_assignment =
4181 solver_->MakeAssignment(assignment_);
4182 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
4183 tail1, head2, tail2);
4190 index_to_chain_index_[head1] = chain_index;
4191 index_to_chain_index_[tail2] = chain_index;
4192 chains_.push_back(chain);
4194 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
4195 if (check_assignment_) {
4196 Assignment*
const temp_assignment =
4197 solver_->MakeAssignment(assignment_);
4199 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4200 head1, tail1, head2, tail2);
4203 index_to_chain_index_[tail2] = chain_index1;
4204 chains_[chain_index1].head = head1;
4205 chains_[chain_index1].tail = tail2;
4206 ++chains_[chain_index1].nodes;
4208 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
4209 if (check_assignment_) {
4210 Assignment*
const temp_assignment =
4211 solver_->MakeAssignment(assignment_);
4213 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
4214 head1, tail1, head2, tail2);
4217 index_to_chain_index_[head1] = chain_index2;
4218 chains_[chain_index2].head = head1;
4219 chains_[chain_index2].tail = tail2;
4220 ++chains_[chain_index2].nodes;
4223 if (check_assignment_) {
4224 Assignment*
const temp_assignment =
4225 solver_->MakeAssignment(assignment_);
4227 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4228 head1, tail1, head2, tail2);
4231 index_to_chain_index_[tail2] = chain_index1;
4232 chains_[chain_index1].head = head1;
4233 chains_[chain_index1].tail = tail2;
4234 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
4235 deleted_chains_.insert(chain_index2);
4239 assignment_->Add(nexts_[tail1]);
4240 assignment_->SetValue(nexts_[tail1], head2);
4245 bool Merge(
bool merge,
int index1,
int index2) {
4247 if (UpdateAssignment(routes_[index1], routes_[index2])) {
4249 for (
const int node : routes_[index2]) {
4250 in_route_[node] = index1;
4251 routes_[index1].push_back(node);
4253 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4254 ++dimension_index) {
4255 for (
const std::pair<int, int64_t> new_possible_cumul :
4256 new_possible_cumuls_[dimension_index]) {
4257 cumuls_[dimension_index][new_possible_cumul.first] =
4258 new_possible_cumul.second;
4261 deleted_routes_.insert(index2);
4268 Assignment*
const assignment_;
4269 RoutingModel*
const model_;
4270 const bool check_assignment_;
4271 Solver*
const solver_;
4272 const int64_t num_indices_;
4273 const std::vector<Link> links_list_;
4274 std::vector<IntVar*> nexts_;
4275 std::vector<const RoutingDimension*> dimensions_;
4276 std::vector<std::vector<int64_t>>
cumuls_;
4277 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
4278 std::vector<std::vector<int>> routes_;
4279 std::vector<int> in_route_;
4280 absl::flat_hash_set<int> deleted_routes_;
4281 std::vector<std::vector<int>> final_routes_;
4282 std::vector<Chain> chains_;
4283 absl::flat_hash_set<int> deleted_chains_;
4284 std::vector<Chain> final_chains_;
4285 std::vector<int> index_to_chain_index_;
4286 std::vector<int> index_to_vehicle_class_index_;
4292class SweepBuilder :
public DecisionBuilder {
4294 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
4295 : model_(
model), check_assignment_(check_assignment) {}
4296 ~SweepBuilder()
override {}
4298 Decision* Next(Solver*
const solver)
override {
4303 Assignment*
const assignment = solver->MakeAssignment();
4304 route_constructor_ = absl::make_unique<RouteConstructor>(
4305 assignment, model_, check_assignment_, num_indices_, links_);
4307 route_constructor_->Construct();
4308 route_constructor_.reset(
nullptr);
4310 assignment->Restore();
4317 const int depot = model_->GetDepot();
4318 num_indices_ = model_->Size() + model_->vehicles();
4319 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4320 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4321 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4323 std::vector<int64_t> indices;
4324 model_->sweep_arranger()->ArrangeIndices(&indices);
4325 for (
int i = 0; i < indices.size() - 1; ++i) {
4326 const int64_t first = indices[i];
4327 const int64_t second = indices[i + 1];
4328 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4329 (model_->IsStart(second) || !model_->IsEnd(second))) {
4330 if (first != depot && second != depot) {
4331 Link
link(std::make_pair(first, second), 0, 0, depot, depot);
4332 links_.push_back(
link);
4338 RoutingModel*
const model_;
4339 std::unique_ptr<RouteConstructor> route_constructor_;
4340 const bool check_assignment_;
4341 int64_t num_indices_;
4342 std::vector<Link> links_;
4347 bool check_assignment) {
4348 return model->solver()->RevAlloc(
new SweepBuilder(
model, check_assignment));
4357class AllUnperformed :
public DecisionBuilder {
4360 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
4361 ~AllUnperformed()
override {}
4362 Decision* Next(Solver*
const solver)
override {
4365 model_->CostVar()->FreezeQueue();
4366 for (
int i = 0; i < model_->Size(); ++i) {
4367 if (!model_->IsStart(i)) {
4368 model_->ActiveVar(i)->SetValue(0);
4371 model_->CostVar()->UnfreezeQueue();
4376 RoutingModel*
const model_;
4381 return model->solver()->RevAlloc(
new AllUnperformed(
model));
4386class GuidedSlackFinalizer :
public DecisionBuilder {
4388 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel*
model,
4389 std::function<int64_t(int64_t)> initializer);
4390 Decision* Next(Solver* solver)
override;
4393 int64_t SelectValue(int64_t
index);
4394 int64_t ChooseVariable();
4396 const RoutingDimension*
const dimension_;
4397 RoutingModel*
const model_;
4398 const std::function<int64_t(int64_t)> initializer_;
4399 RevArray<bool> is_initialized_;
4400 std::vector<int64_t> initial_values_;
4401 Rev<int64_t> current_index_;
4402 Rev<int64_t> current_route_;
4403 RevArray<int64_t> last_delta_used_;
4408GuidedSlackFinalizer::GuidedSlackFinalizer(
4409 const RoutingDimension* dimension, RoutingModel*
model,
4410 std::function<int64_t(int64_t)> initializer)
4413 initializer_(
std::move(initializer)),
4414 is_initialized_(dimension->slacks().size(), false),
4415 initial_values_(dimension->slacks().size(),
4416 std::numeric_limits<int64_t>::
min()),
4417 current_index_(model_->Start(0)),
4419 last_delta_used_(dimension->slacks().size(), 0) {}
4421Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4422 CHECK_EQ(solver, model_->solver());
4423 const int node_idx = ChooseVariable();
4424 CHECK(node_idx == -1 ||
4425 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
4426 if (node_idx != -1) {
4427 if (!is_initialized_[node_idx]) {
4428 initial_values_[node_idx] = initializer_(node_idx);
4429 is_initialized_.SetValue(solver, node_idx,
true);
4431 const int64_t
value = SelectValue(node_idx);
4432 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
4433 return solver->MakeAssignVariableValue(slack_variable,
value);
4438int64_t GuidedSlackFinalizer::SelectValue(int64_t
index) {
4439 const IntVar*
const slack_variable = dimension_->SlackVar(
index);
4440 const int64_t center = initial_values_[
index];
4441 const int64_t max_delta =
4442 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4448 while (std::abs(
delta) < max_delta &&
4449 !slack_variable->Contains(center +
delta)) {
4456 last_delta_used_.SetValue(model_->solver(),
index,
delta);
4457 return center +
delta;
4460int64_t GuidedSlackFinalizer::ChooseVariable() {
4461 int64_t int_current_node = current_index_.Value();
4462 int64_t int_current_route = current_route_.Value();
4464 while (int_current_route < model_->vehicles()) {
4465 while (!model_->IsEnd(int_current_node) &&
4466 dimension_->SlackVar(int_current_node)->Bound()) {
4467 int_current_node = model_->NextVar(int_current_node)->Value();
4469 if (!model_->IsEnd(int_current_node)) {
4472 int_current_route += 1;
4473 if (int_current_route < model_->vehicles()) {
4474 int_current_node = model_->Start(int_current_route);
4478 CHECK(int_current_route == model_->vehicles() ||
4479 !dimension_->SlackVar(int_current_node)->Bound());
4480 current_index_.SetValue(model_->solver(), int_current_node);
4481 current_route_.SetValue(model_->solver(), int_current_route);
4482 if (int_current_route < model_->vehicles()) {
4483 return int_current_node;
4492 std::function<int64_t(int64_t)> initializer) {
4493 return solver_->RevAlloc(
4494 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
4497int64_t RoutingDimension::ShortestTransitionSlack(int64_t node)
const {
4499 CHECK(!model_->IsEnd(node));
4503 const int64_t
next = model_->NextVar(node)->Value();
4504 if (model_->IsEnd(
next)) {
4505 return SlackVar(node)->Min();
4507 const int64_t next_next = model_->NextVar(
next)->Value();
4508 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4509 CHECK_EQ(serving_vehicle, model_->VehicleVar(
next)->Value());
4511 model_->StateDependentTransitCallback(
4512 state_dependent_class_evaluators_
4513 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
4516 const int64_t next_cumul_min = CumulVar(
next)->Min();
4517 const int64_t next_cumul_max = CumulVar(
next)->Max();
4518 const int64_t optimal_next_cumul =
4520 next_cumul_min, next_cumul_max + 1);
4522 DCHECK_LE(next_cumul_min, optimal_next_cumul);
4523 DCHECK_LE(optimal_next_cumul, next_cumul_max);
4528 const int64_t current_cumul = CumulVar(node)->Value();
4529 const int64_t current_state_independent_transit = model_->TransitCallback(
4530 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
4531 const int64_t current_state_dependent_transit =
4533 ->StateDependentTransitCallback(
4534 state_dependent_class_evaluators_
4535 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4537 .transit->Query(current_cumul);
4538 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4539 current_state_independent_transit -
4540 current_state_dependent_transit;
4541 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4542 CHECK_LE(optimal_slack, SlackVar(node)->Max());
4543 return optimal_slack;
4549 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4555 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
4557 const std::vector<IntVar*> variables_;
4559 int64_t current_step_;
4566 int64_t current_direction_;
4571GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4572 : variables_(
std::move(variables)),
4575 current_direction_(0) {}
4577bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
4579 static const int64_t sings[] = {1, -1};
4580 for (; 1 <= current_step_; current_step_ /= 2) {
4581 for (; current_direction_ < 2 * variables_.size();) {
4582 const int64_t variable_idx = current_direction_ / 2;
4583 IntVar*
const variable = variables_[variable_idx];
4584 const int64_t sign_index = current_direction_ % 2;
4585 const int64_t sign = sings[sign_index];
4586 const int64_t offset = sign * current_step_;
4587 const int64_t new_value = center_->
Value(variable) + offset;
4588 ++current_direction_;
4589 if (variable->Contains(new_value)) {
4590 delta->Add(variable);
4591 delta->SetValue(variable, new_value);
4595 current_direction_ = 0;
4600void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
4601 CHECK(assignment !=
nullptr);
4602 current_step_ = FindMaxDistanceToDomain(assignment);
4603 center_ = assignment;
4606int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
4607 const Assignment* assignment) {
4609 for (
const IntVar*
const var : variables_) {
4618 std::vector<IntVar*> variables) {
4619 return std::unique_ptr<LocalSearchOperator>(
4620 new GreedyDescentLSOperator(std::move(variables)));
4625 CHECK(dimension !=
nullptr);
4627 std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t
index) {
4633 solver_->MakeSolveOnce(guided_finalizer);
4634 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
4635 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
4636 start_cumuls[vehicle_idx] = dimension->
CumulVar(starts_[vehicle_idx]);
4639 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
4641 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
4643 Assignment*
const first_solution = solver_->MakeAssignment();
4644 first_solution->
Add(start_cumuls);
4645 for (
IntVar*
const cumul : start_cumuls) {
4646 first_solution->
SetValue(cumul, cumul->Min());
4649 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, int vehicle=-1)
Inserts 'node' just after 'predecessor', and just before 'successor' on the route of 'vehicle',...
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.
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.
int64_t SecondaryVarIndex(int64_t index) const
Returns the index of a secondary var.
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.
Assignment *const assignment_
void ResetSolution()
Resets the data members for a new solution.
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
bool HasSecondaryVars() const
Returns true if there are secondary variables.
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.
bool IsSecondaryVar(int64_t index) const
Returns true if 'index' is a secondary variable index.
std::optional< int64_t > Evaluate(bool commit)
Evaluates the modifications to the current solution.
Assignment *const BuildSolution()
Builds a solution.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, LocalSearchFilterManager *filter_manager)
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 ...
int64_t GetAcceptedObjectiveValue() const
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
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool omit_secondary_vars=true)
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.
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_
#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)
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.