39 #include "absl/base/attributes.h"
40 #include "absl/container/flat_hash_map.h"
41 #include "absl/container/flat_hash_set.h"
42 #include "absl/flags/flag.h"
43 #include "absl/strings/str_cat.h"
44 #include "absl/strings/string_view.h"
54 #include "ortools/constraint_solver/routing_parameters.pb.h"
62 class LocalSearchPhaseParameters;
65 ABSL_FLAG(
bool, routing_shift_insertion_cost_by_penalty,
true,
66 "Shift insertion costs by the penalty of the inserted node(s).");
69 "The number of sectors the space is divided into before it is sweeped"
77 const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
79 sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
80 const std::vector<std::deque<int>>& all_vehicles_per_class =
82 vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
84 for (
int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
85 std::set<VehicleClassEntry>& stored_class_entries =
86 sorted_vehicle_classes_per_type_[type];
87 stored_class_entries.clear();
90 std::vector<int>& stored_vehicles =
92 stored_vehicles.clear();
94 if (store_vehicle(vehicle)) {
95 stored_vehicles.push_back(vehicle);
98 if (!stored_vehicles.empty()) {
99 stored_class_entries.insert(class_entry);
106 const std::function<
bool(
int)>& remove_vehicle) {
107 for (std::set<VehicleClassEntry>& class_entries :
108 sorted_vehicle_classes_per_type_) {
109 auto class_entry_it = class_entries.begin();
110 while (class_entry_it != class_entries.end()) {
112 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
113 vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
114 [&remove_vehicle](
int vehicle) {
115 return remove_vehicle(vehicle);
118 if (vehicles.empty()) {
119 class_entry_it = class_entries.erase(class_entry_it);
128 int type,
const std::function<
bool(
int)>& vehicle_is_compatible)
const {
130 sorted_vehicle_classes_per_type_[type]) {
132 vehicles_per_vehicle_class_[vehicle_class_entry.vehicle_class]) {
133 if (vehicle_is_compatible(vehicle))
return true;
140 int type,
const std::function<
bool(
int)>& vehicle_is_compatible,
141 const std::function<
bool(
int)>& stop_and_return_vehicle) {
142 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
143 sorted_vehicle_classes_per_type_[type];
144 auto vehicle_class_it = sorted_classes.begin();
146 while (vehicle_class_it != sorted_classes.end()) {
148 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
149 DCHECK(!vehicles.empty());
151 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
153 const int vehicle = *vehicle_it;
154 if (vehicle_is_compatible(vehicle)) {
155 vehicles.erase(vehicle_it);
156 if (vehicles.empty()) {
157 sorted_classes.erase(vehicle_class_it);
159 return {vehicle, -1};
161 if (stop_and_return_vehicle(vehicle)) {
162 return {-1, vehicle};
181 bool has_pickup_deliveries,
bool has_node_precedences,
182 bool has_single_vehicle_node) {
183 if (has_pickup_deliveries || has_node_precedences) {
184 return FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
186 if (has_single_vehicle_node) {
187 return FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC;
189 return FirstSolutionStrategy::PATH_CHEAPEST_ARC;
193 const int64_t size =
model.Size();
194 const int num_vehicles =
model.vehicles();
202 std::vector<int64_t> starts(size + num_vehicles, -1);
203 std::vector<int64_t> ends(size + num_vehicles, -1);
204 for (
int node = 0; node < size + num_vehicles; ++node) {
209 std::vector<bool> touched(size,
false);
210 for (
int node = 0; node < size; ++node) {
212 while (!
model.IsEnd(current) && !touched[current]) {
213 touched[current] =
true;
215 if (next_var->
Bound()) {
216 current = next_var->
Value();
221 starts[ends[current]] = starts[node];
222 ends[starts[node]] = ends[current];
226 std::vector<int64_t> end_chain_starts(num_vehicles);
227 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
228 end_chain_starts[vehicle] = starts[
model.End(vehicle)];
230 return end_chain_starts;
238 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
239 : heuristic_(std::move(heuristic)) {}
242 Assignment*
const assignment = heuristic_->BuildSolution();
243 if (assignment !=
nullptr) {
244 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
245 VLOG(2) <<
"Number of rejected decisions: "
246 << heuristic_->number_of_rejects();
255 return heuristic_->number_of_decisions();
259 return heuristic_->number_of_rejects();
263 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
264 heuristic_->DebugString(),
")");
272 Solver* solver,
const std::vector<IntVar*>& vars,
273 const std::vector<IntVar*>& secondary_vars,
275 : assignment_(solver->MakeAssignment()),
278 base_vars_size_(vars.size()),
279 delta_(solver->MakeAssignment()),
280 is_in_delta_(
vars_.size(), false),
281 empty_(solver->MakeAssignment()),
282 filter_manager_(filter_manager),
283 objective_upper_bound_(std::numeric_limits<int64_t>::
max()),
284 number_of_decisions_(0),
285 number_of_rejects_(0) {
286 if (!secondary_vars.empty()) {
287 vars_.insert(vars_.end(), secondary_vars.begin(), secondary_vars.end());
290 delta_indices_.reserve(vars_.size());
294 number_of_decisions_ = 0;
295 number_of_rejects_ = 0;
317 const std::function<int64_t(int64_t)>& next_accessor) {
324 if (!InitializeSolution()) {
328 for (
int v = 0; v < model_->
vehicles(); v++) {
329 int64_t node = model_->
Start(v);
330 while (!model_->
IsEnd(node)) {
331 const int64_t
next = next_accessor(node);
332 DCHECK_NE(
next, node);
354 ++number_of_decisions_;
355 const bool accept = FilterAccept();
357 if (filter_manager_ !=
nullptr) {
366 objective_upper_bound_);
372 const int delta_size = delta_container.
Size();
375 for (
int i = 0; i < delta_size; ++i) {
378 DCHECK_EQ(
var, vars_[delta_indices_[i]]);
385 ++number_of_rejects_;
388 for (
const int delta_index : delta_indices_) {
389 is_in_delta_[delta_index] =
false;
392 delta_indices_.clear();
393 return accept ? std::optional<int64_t>{objective_upper_bound_} : std::nullopt;
402 bool IntVarFilteredHeuristic::FilterAccept() {
403 if (!filter_manager_)
return true;
405 return filter_manager_->
Accept(monitor, delta_, empty_,
407 objective_upper_bound_);
417 omit_secondary_vars ||
model->CostsAreHomogeneousAcrossVehicles()
419 :
model->VehicleVars(),
422 stop_search_(std::move(stop_search)) {}
424 bool RoutingFilteredHeuristic::InitializeSolution() {
429 start_chain_ends_.resize(
model()->vehicles());
430 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
432 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
441 start_chain_ends_[vehicle] = node;
448 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
449 int64_t node = start_chain_ends_[vehicle];
451 int64_t
next = end_chain_starts_[vehicle];
458 while (!
model()->IsEnd(node)) {
479 node, 1, [
this, node](
int alternate) {
480 if (node != alternate && !
Contains(alternate)) {
500 std::vector<bool> to_make_unperformed(
Size(),
false);
501 for (
const auto& [pickups, deliveries] :
502 model()->GetPickupAndDeliveryPairs()) {
503 int64_t performed_pickup = -1;
504 for (int64_t pickup : pickups) {
506 performed_pickup = pickup;
510 int64_t performed_delivery = -1;
511 for (int64_t delivery : deliveries) {
513 performed_delivery = delivery;
517 if ((performed_pickup == -1) != (performed_delivery == -1)) {
518 if (performed_pickup != -1) {
519 to_make_unperformed[performed_pickup] =
true;
521 if (performed_delivery != -1) {
522 to_make_unperformed[performed_delivery] =
true;
530 const int64_t next_of_next =
Value(
next);
542 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
543 std::function<int64_t(int64_t)> penalty_evaluator,
546 evaluator != nullptr),
548 penalty_evaluator_(std::move(penalty_evaluator)) {}
550 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
552 const std::vector<int>& vehicles) {
554 const absl::flat_hash_set<int> vehicle_set(vehicles.begin(), vehicles.end());
555 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
558 for (
int node = 0; node <
model()->
Size(); node++) {
560 std::vector<StartEndValue>& start_end_distances =
561 start_end_distances_per_node[node];
563 const int64_t num_allowed_vehicles = vehicle_var->
Size();
565 const auto add_distance = [
this, node, num_allowed_vehicles,
566 &start_end_distances](
int vehicle) {
573 model()->GetArcCostForVehicle(node,
end, vehicle));
574 start_end_distances.push_back({num_allowed_vehicles,
distance, vehicle});
579 if (num_allowed_vehicles < vehicles.size()) {
580 std::unique_ptr<IntVarIterator> it(
583 if (vehicle < 0 || !vehicle_set.contains(vehicle))
continue;
584 add_distance(vehicle);
587 start_end_distances.reserve(vehicles.size());
588 for (
const int vehicle : vehicles) {
589 if (!vehicle_var->
Contains(vehicle))
continue;
590 add_distance(vehicle);
595 std::sort(start_end_distances.begin(), start_end_distances.end(),
597 return second < first;
600 return start_end_distances_per_node;
603 template <
class Queue>
605 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
606 Queue* priority_queue) {
608 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
610 for (
int node = 0; node < num_nodes; node++) {
612 std::vector<StartEndValue>& start_end_distances =
613 (*start_end_distances_per_node)[node];
614 if (start_end_distances.empty()) {
618 const StartEndValue& start_end_value = start_end_distances.back();
619 priority_queue->push(std::make_pair(start_end_value, node));
620 start_end_distances.pop_back();
639 int64_t node_to_insert, int64_t
start, int64_t next_after_start,
640 int vehicle,
bool ignore_cost,
641 std::vector<NodeInsertion>* node_insertions) {
642 DCHECK(node_insertions !=
nullptr);
643 int64_t insert_after =
start;
644 while (!
model()->IsEnd(insert_after)) {
645 const int64_t insert_before =
646 (insert_after ==
start) ? next_after_start :
Value(insert_after);
648 InsertBetween(node_to_insert, insert_after, insert_before, vehicle);
649 std::optional<int64_t> insertion_cost =
Evaluate(
false);
650 if (insertion_cost.has_value()) {
651 node_insertions->push_back({insert_after, vehicle, *insertion_cost});
654 node_insertions->push_back(
655 {insert_after, vehicle,
659 insert_before, vehicle)});
661 insert_after = insert_before;
666 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
670 evaluator_(node_to_insert, insert_before, vehicle)),
671 evaluator_(insert_after, insert_before, vehicle));
675 int64_t node_to_insert)
const {
687 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
688 std::function<int64_t(int64_t)> penalty_evaluator,
692 model, std::move(stop_search), std::move(evaluator),
693 std::move(penalty_evaluator), filter_manager),
695 node_index_to_vehicle_(
model->Size(), -1),
696 node_index_to_neighbors_by_cost_class_(nullptr),
697 empty_vehicle_type_curator_(nullptr) {
702 if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
713 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
714 std::vector<bool> node_is_visited(
model()->
Size(),
false);
717 node =
Value(node)) {
718 if (node_index_to_vehicle_[node] != v) {
721 node_is_visited[node] =
true;
725 for (
int node = 0; node <
model()->
Size(); node++) {
726 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
736 int num_neighbors = 0;
740 num_neighbors = NumNeighbors();
743 DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
745 node_index_to_neighbors_by_cost_class_ =
748 if (empty_vehicle_type_curator_ ==
nullptr) {
749 empty_vehicle_type_curator_ = std::make_unique<VehicleTypeCurator>(
750 model()->GetVehicleTypeContainer());
753 empty_vehicle_type_curator_->Reset(
758 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
759 absl::flat_hash_map<int, std::map<int64_t, std::vector<int>>>
760 vehicle_to_pair_nodes;
763 int pickup_vehicle = -1;
764 for (int64_t pickup : index_pair.first) {
766 pickup_vehicle = node_index_to_vehicle_[pickup];
770 int delivery_vehicle = -1;
771 for (int64_t delivery : index_pair.second) {
773 delivery_vehicle = node_index_to_vehicle_[delivery];
777 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
778 pairs_to_insert_by_bucket[GetBucketOfPair(index_pair)].push_back(
index);
780 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
781 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle][1];
782 for (int64_t delivery : index_pair.second) {
783 pair_nodes.push_back(delivery);
786 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
787 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle][1];
788 for (int64_t pickup : index_pair.first) {
789 pair_nodes.push_back(pickup);
794 const auto unperform_unassigned_and_check = [
this]() {
798 for (
const auto& [vehicle,
nodes] : vehicle_to_pair_nodes) {
799 if (!InsertNodesOnRoutes(
nodes, {vehicle})) {
800 return unperform_unassigned_and_check();
804 if (!InsertPairsAndNodesByRequirementTopologicalOrder()) {
805 return unperform_unassigned_and_check();
810 if (!InsertPairs(pairs_to_insert_by_bucket)) {
811 return unperform_unassigned_and_check();
813 std::map<int64_t, std::vector<int>> nodes_by_bucket;
814 for (
int node = 0; node <
model()->
Size(); ++node) {
817 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
820 InsertFarthestNodesAsSeeds();
822 if (!SequentialInsertNodes(nodes_by_bucket)) {
823 return unperform_unassigned_and_check();
825 }
else if (!InsertNodesOnRoutes(nodes_by_bucket, {})) {
826 return unperform_unassigned_and_check();
828 DCHECK(CheckVehicleIndices());
829 return unperform_unassigned_and_check();
832 bool GlobalCheapestInsertionFilteredHeuristic::
833 InsertPairsAndNodesByRequirementTopologicalOrder() {
836 for (
const std::vector<int>& types :
837 model()->GetTopologicallySortedVisitTypes()) {
838 for (
int type : types) {
839 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
840 for (
int index :
model()->GetPairIndicesOfType(type)) {
841 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pairs[
index])]
844 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
845 std::map<int64_t, std::vector<int>> nodes_by_bucket;
846 for (
int node :
model()->GetSingleNodesOfType(type)) {
847 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
849 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
855 bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
856 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket) {
858 std::vector<PairEntries> pickup_to_entries;
859 std::vector<PairEntries> delivery_to_entries;
862 auto pair_is_performed = [
this, &pickup_delivery_pairs](
int pair_index) {
863 for (int64_t pickup : pickup_delivery_pairs[pair_index].first) {
868 for (int64_t delivery : pickup_delivery_pairs[pair_index].second) {
875 absl::flat_hash_set<int> pair_indices_to_insert;
876 for (
const auto& [bucket, pair_indices] : pair_indices_by_bucket) {
877 for (
const int pair_index : pair_indices) {
878 if (!pair_is_performed(pair_index)) {
879 pair_indices_to_insert.insert(pair_index);
882 if (!InitializePairPositions(pair_indices_to_insert, &priority_queue,
883 &pickup_to_entries, &delivery_to_entries)) {
886 while (!priority_queue.
IsEmpty()) {
887 if (StopSearchAndCleanup(&priority_queue)) {
890 PairEntry*
const entry = priority_queue.
Top();
891 const int64_t pickup = entry->pickup_to_insert();
892 const int64_t delivery = entry->delivery_to_insert();
894 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
895 &delivery_to_entries);
899 const int entry_vehicle = entry->vehicle();
900 if (entry_vehicle == -1) {
905 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
906 &delivery_to_entries);
912 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle)) {
913 if (!InsertPairEntryUsingEmptyVehicleTypeCurator(
914 pair_indices_to_insert, entry, &priority_queue,
915 &pickup_to_entries, &delivery_to_entries)) {
923 const int64_t pickup_insert_after = entry->pickup_insert_after();
924 const int64_t pickup_insert_before =
Value(pickup_insert_after);
925 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
927 const int64_t delivery_insert_after = entry->delivery_insert_after();
928 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
929 ? pickup_insert_before
930 :
Value(delivery_insert_after);
931 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
933 if (!UpdateAfterPairInsertion(
934 pair_indices_to_insert, entry_vehicle, pickup,
935 pickup_insert_after, delivery, delivery_insert_after,
936 &priority_queue, &pickup_to_entries, &delivery_to_entries)) {
940 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
941 &delivery_to_entries);
946 for (
auto it = pair_indices_to_insert.begin(),
947 last = pair_indices_to_insert.end();
949 if (pair_is_performed(*it)) {
950 pair_indices_to_insert.erase(it++);
959 bool GlobalCheapestInsertionFilteredHeuristic::
960 InsertPairEntryUsingEmptyVehicleTypeCurator(
961 const absl::flat_hash_set<int>& pair_indices,
962 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
964 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
966 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
968 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
969 delivery_to_entries) {
970 const int entry_vehicle = pair_entry->vehicle();
971 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle));
977 const int64_t pickup = pair_entry->pickup_to_insert();
978 const int64_t delivery = pair_entry->delivery_to_insert();
979 const int64_t entry_fixed_cost =
981 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
982 delivery](
int vehicle) {
997 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1000 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1001 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1002 empty_vehicle_type_curator_->Type(entry_vehicle),
1003 vehicle_is_compatible, stop_and_return_vehicle);
1004 if (compatible_vehicle >= 0) {
1006 const int64_t vehicle_start =
model()->
Start(compatible_vehicle);
1007 const int num_previous_vehicle_entries =
1008 pickup_to_entries->at(vehicle_start).size() +
1009 delivery_to_entries->at(vehicle_start).size();
1010 if (!UpdateAfterPairInsertion(
1011 pair_indices, compatible_vehicle, pickup, vehicle_start, delivery,
1012 pickup, priority_queue, pickup_to_entries, delivery_to_entries)) {
1015 if (compatible_vehicle != entry_vehicle) {
1021 DCHECK_EQ(num_previous_vehicle_entries, 0);
1027 const int new_empty_vehicle =
1028 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1029 empty_vehicle_type_curator_->Type(compatible_vehicle));
1031 if (new_empty_vehicle >= 0) {
1038 const int64_t new_empty_vehicle_start =
model()->
Start(new_empty_vehicle);
1039 const std::vector<PairEntry*> to_remove(
1040 pickup_to_entries->at(new_empty_vehicle_start).begin(),
1041 pickup_to_entries->at(new_empty_vehicle_start).end());
1042 for (PairEntry* entry : to_remove) {
1043 DeletePairEntry(entry, priority_queue, pickup_to_entries,
1044 delivery_to_entries);
1046 if (!AddPairEntriesWithPickupAfter(
1047 pair_indices, new_empty_vehicle, new_empty_vehicle_start,
1049 pickup_to_entries, delivery_to_entries)) {
1053 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1060 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1061 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1062 pair_entry->set_pickup_insert_after(
1063 model()->Start(next_fixed_cost_empty_vehicle));
1064 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1065 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1066 UpdatePairEntry(pair_entry, priority_queue);
1068 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1069 delivery_to_entries);
1099 : entries_(num_nodes), touched_entries_(num_nodes) {}
1101 priority_queue_.
Clear();
1102 for (Entries& entries : entries_) entries.Clear();
1106 return priority_queue_.
IsEmpty() &&
1110 return insert_after >= entries_.size() ||
1111 entries_[insert_after].entries.empty();
1116 SortInsertions(&entries_[touched]);
1119 DCHECK(!priority_queue_.
IsEmpty());
1120 Entries* entries = priority_queue_.
Top();
1121 DCHECK(!entries->entries.empty());
1122 return entries->Top();
1127 Entries* top = priority_queue_.
Top();
1128 if (top->IncrementTop()) {
1131 priority_queue_.
Remove(top);
1136 if (
IsEmpty(insert_after))
return;
1137 Entries& entries = entries_[insert_after];
1138 if (priority_queue_.
Contains(&entries)) {
1139 priority_queue_.
Remove(&entries);
1144 int bucket, int64_t
value) {
1145 entries_[insert_after].entries.push_back(
1146 {
value, node, insert_after, vehicle, bucket});
1147 touched_entries_.
Set(insert_after);
1152 bool operator<(
const Entries& other)
const {
1153 DCHECK(!entries.empty());
1154 DCHECK(!other.entries.empty());
1155 return other.entries[other.top] < entries[top];
1162 void SetHeapIndex(
int index) { heap_index =
index; }
1163 int GetHeapIndex()
const {
return heap_index; }
1164 bool IncrementTop() {
1166 return top < entries.size();
1168 Entry*
Top() {
return &entries[top]; }
1170 std::vector<Entry> entries;
1172 int heap_index = -1;
1175 void SortInsertions(Entries* entries) {
1177 if (entries->entries.empty())
return;
1178 std::sort(entries->entries.begin(), entries->entries.end());
1179 if (!priority_queue_.
Contains(entries)) {
1180 priority_queue_.
Add(entries);
1187 std::vector<Entries> entries_;
1188 SparseBitset<int> touched_entries_;
1191 bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1192 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
1193 const absl::flat_hash_set<int>& vehicles) {
1194 NodeEntryQueue queue(
model()->Nexts().size());
1195 std::vector<bool> nodes_to_insert(
model()->
Size(),
false);
1196 for (
const auto& [bucket,
nodes] : nodes_by_bucket) {
1197 for (
int node :
nodes) nodes_to_insert[node] =
true;
1198 if (!InitializePositions(nodes_to_insert, vehicles, &queue)) {
1207 const bool all_vehicles =
1210 while (!queue.IsEmpty()) {
1211 const NodeEntryQueue::Entry* node_entry = queue.Top();
1213 const int64_t node_to_insert = node_entry->node_to_insert;
1219 const int entry_vehicle = node_entry->vehicle;
1220 if (entry_vehicle == -1) {
1221 DCHECK(all_vehicles);
1223 SetValue(node_to_insert, node_to_insert);
1231 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles)) {
1232 DCHECK(all_vehicles);
1233 if (!InsertNodeEntryUsingEmptyVehicleTypeCurator(
1234 nodes_to_insert, all_vehicles, &queue)) {
1240 const int64_t insert_after = node_entry->insert_after;
1243 if (!UpdateAfterNodeInsertion(nodes_to_insert, entry_vehicle,
1244 node_to_insert, insert_after,
1245 all_vehicles, &queue)) {
1254 for (
int node = 0; node < nodes_to_insert.size(); ++node) {
1255 if (
Contains(node)) nodes_to_insert[node] =
false;
1261 bool GlobalCheapestInsertionFilteredHeuristic::
1262 InsertNodeEntryUsingEmptyVehicleTypeCurator(
const std::vector<bool>&
nodes,
1264 NodeEntryQueue* queue) {
1265 const NodeEntryQueue::Entry* node_entry = queue->Top();
1266 const int entry_vehicle = node_entry->vehicle;
1267 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles));
1274 const int64_t node_to_insert = node_entry->node_to_insert;
1275 const int bucket = node_entry->bucket;
1276 const int64_t entry_fixed_cost =
1278 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1279 node_to_insert](
int vehicle) {
1286 model()->End(vehicle), vehicle);
1293 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1296 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1297 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1298 empty_vehicle_type_curator_->Type(entry_vehicle),
1299 vehicle_is_compatible, stop_and_return_vehicle);
1300 if (compatible_vehicle >= 0) {
1302 const int64_t compatible_start =
model()->
Start(compatible_vehicle);
1303 const bool no_prior_entries_for_this_vehicle =
1304 queue->IsEmpty(compatible_start);
1305 if (!UpdateAfterNodeInsertion(
nodes, compatible_vehicle, node_to_insert,
1306 compatible_start, all_vehicles, queue)) {
1309 if (compatible_vehicle != entry_vehicle) {
1315 DCHECK(no_prior_entries_for_this_vehicle);
1321 const int new_empty_vehicle =
1322 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1323 empty_vehicle_type_curator_->Type(compatible_vehicle));
1325 if (new_empty_vehicle >= 0) {
1332 const int64_t new_empty_vehicle_start =
model()->
Start(new_empty_vehicle);
1333 queue->ClearInsertions(new_empty_vehicle_start);
1334 if (!AddNodeEntriesAfter(
nodes, new_empty_vehicle,
1335 new_empty_vehicle_start, all_vehicles, queue)) {
1339 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1347 const int64_t insert_after =
model()->
Start(next_fixed_cost_empty_vehicle);
1349 node_to_insert, insert_after,
Value(insert_after),
1350 next_fixed_cost_empty_vehicle);
1351 const int64_t penalty_shift =
1352 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1355 queue->PushInsertion(node_to_insert, insert_after,
1356 next_fixed_cost_empty_vehicle, bucket,
1357 CapSub(insertion_cost, penalty_shift));
1365 bool GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1366 const std::map<int64_t, std::vector<int>>& nodes_by_bucket) {
1367 std::vector<bool> is_vehicle_used;
1368 absl::flat_hash_set<int> used_vehicles;
1369 std::vector<int> unused_vehicles;
1371 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1372 if (!used_vehicles.empty() &&
1373 !InsertNodesOnRoutes(nodes_by_bucket, used_vehicles)) {
1377 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1379 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1383 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1386 while (vehicle >= 0) {
1387 if (!InsertNodesOnRoutes(nodes_by_bucket, {vehicle})) {
1390 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1396 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1397 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1398 absl::flat_hash_set<int>* used_vehicles) {
1399 is_vehicle_used->clear();
1400 is_vehicle_used->resize(
model()->vehicles());
1402 used_vehicles->clear();
1403 used_vehicles->reserve(
model()->vehicles());
1405 unused_vehicles->clear();
1406 unused_vehicles->reserve(
model()->vehicles());
1408 for (
int vehicle = 0; vehicle <
model()->
vehicles(); vehicle++) {
1410 (*is_vehicle_used)[vehicle] =
true;
1411 used_vehicles->insert(vehicle);
1413 (*is_vehicle_used)[vehicle] =
false;
1414 unused_vehicles->push_back(vehicle);
1419 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1423 const int num_seeds =
static_cast<int>(
1426 std::vector<bool> is_vehicle_used;
1427 absl::flat_hash_set<int> used_vehicles;
1428 std::vector<int> unused_vehicles;
1429 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1430 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1435 std::priority_queue<Seed> farthest_node_queue;
1438 int inserted_seeds = 0;
1439 while (inserted_seeds++ < num_seeds) {
1440 if (InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1441 &is_vehicle_used) < 0) {
1450 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1451 empty_vehicle_type_curator_->Update(
1455 template <
class Queue>
1456 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1457 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1458 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
1459 while (!priority_queue->empty()) {
1461 const Seed& seed = priority_queue->top();
1463 const int seed_node = seed.second;
1464 const int seed_vehicle = seed.first.vehicle;
1466 std::vector<StartEndValue>& other_start_end_values =
1467 (*start_end_distances_per_node)[seed_node];
1472 priority_queue->pop();
1473 other_start_end_values.clear();
1476 if (!(*is_vehicle_used)[seed_vehicle]) {
1483 priority_queue->pop();
1484 (*is_vehicle_used)[seed_vehicle] =
true;
1485 other_start_end_values.clear();
1486 SetVehicleIndex(seed_node, seed_vehicle);
1487 return seed_vehicle;
1494 priority_queue->pop();
1495 if (!other_start_end_values.empty()) {
1496 const StartEndValue& next_seed_value = other_start_end_values.back();
1497 priority_queue->push(std::make_pair(next_seed_value, seed_node));
1498 other_start_end_values.pop_back();
1505 bool GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1506 const absl::flat_hash_set<int>& pair_indices,
1508 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1509 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1511 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1512 delivery_to_entries) {
1513 priority_queue->Clear();
1514 pickup_to_entries->clear();
1515 pickup_to_entries->resize(
model()->
Size());
1516 delivery_to_entries->clear();
1517 delivery_to_entries->resize(
model()->
Size());
1520 for (
int index : pair_indices) {
1522 for (int64_t pickup : index_pair.first) {
1524 for (int64_t delivery : index_pair.second) {
1526 if (StopSearchAndCleanup(priority_queue))
return false;
1532 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1537 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1541 InitializeInsertionEntriesPerformingPair(
1542 pickup, delivery, priority_queue, pickup_to_entries,
1543 delivery_to_entries);
1550 void GlobalCheapestInsertionFilteredHeuristic::
1551 InitializeInsertionEntriesPerformingPair(
1552 int64_t pickup, int64_t delivery,
1554 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1556 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1558 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1559 delivery_to_entries) {
1561 struct PairInsertion {
1562 int64_t insert_pickup_after;
1563 int64_t insert_delivery_after;
1566 std::vector<PairInsertion> pair_insertions;
1567 std::vector<NodeInsertion> pickup_insertions;
1568 std::vector<NodeInsertion> delivery_insertions;
1569 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
1571 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1572 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1578 pickup_insertions.clear();
1580 true, &pickup_insertions);
1581 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
1582 DCHECK(!
model()->IsEnd(pickup_insertion.insert_after));
1583 delivery_insertions.clear();
1585 delivery, pickup,
Value(pickup_insertion.insert_after), vehicle,
1586 true, &delivery_insertions);
1587 for (
const NodeInsertion& delivery_insertion : delivery_insertions) {
1588 pair_insertions.push_back({pickup_insertion.insert_after,
1589 delivery_insertion.insert_after, vehicle});
1593 for (
const auto& [insert_pickup_after, insert_delivery_after, vehicle] :
1595 DCHECK_NE(insert_pickup_after, insert_delivery_after);
1596 AddPairEntry(pickup, insert_pickup_after, delivery, insert_delivery_after,
1597 vehicle, priority_queue, pickup_to_entries,
1598 delivery_to_entries);
1607 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1608 existing_insertion_positions;
1610 for (
const int64_t pickup_insert_after :
1612 cost_class, pickup)) {
1613 if (!
Contains(pickup_insert_after)) {
1616 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1623 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1624 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1630 int64_t delivery_insert_after = pickup;
1631 while (!
model()->
IsEnd(delivery_insert_after)) {
1632 const std::pair<int64_t, int64_t> insertion_position = {
1633 pickup_insert_after, delivery_insert_after};
1634 DCHECK(!existing_insertion_positions.contains(insertion_position));
1635 existing_insertion_positions.insert(insertion_position);
1637 AddPairEntry(pickup, pickup_insert_after, delivery,
1638 delivery_insert_after, vehicle, priority_queue,
1639 pickup_to_entries, delivery_to_entries);
1640 delivery_insert_after = (delivery_insert_after == pickup)
1641 ?
Value(pickup_insert_after)
1642 :
Value(delivery_insert_after);
1647 for (
const int64_t delivery_insert_after :
1649 cost_class, delivery)) {
1650 if (!
Contains(delivery_insert_after)) {
1653 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1655 model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
1661 DCHECK_EQ(delivery_insert_after,
model()->Start(vehicle));
1664 int64_t pickup_insert_after =
model()->
Start(vehicle);
1665 while (pickup_insert_after != delivery_insert_after) {
1666 if (!existing_insertion_positions.contains(
1667 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1668 AddPairEntry(pickup, pickup_insert_after, delivery,
1669 delivery_insert_after, vehicle, priority_queue,
1670 pickup_to_entries, delivery_to_entries);
1672 pickup_insert_after =
Value(pickup_insert_after);
1678 bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1679 const absl::flat_hash_set<int>& pair_indices,
int vehicle, int64_t pickup,
1680 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1682 std::vector<PairEntries>* pickup_to_entries,
1683 std::vector<PairEntries>* delivery_to_entries) {
1686 const std::vector<PairEntry*> to_remove(
1687 delivery_to_entries->at(pickup).begin(),
1688 delivery_to_entries->at(pickup).end());
1689 for (PairEntry* pair_entry : to_remove) {
1690 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1691 delivery_to_entries);
1693 DCHECK(pickup_to_entries->at(pickup).empty());
1694 DCHECK(pickup_to_entries->at(delivery).empty());
1695 DCHECK(delivery_to_entries->at(pickup).empty());
1696 DCHECK(delivery_to_entries->at(delivery).empty());
1699 if (!UpdateExistingPairEntriesOnChain(pickup_position,
Value(pickup_position),
1700 priority_queue, pickup_to_entries,
1701 delivery_to_entries) ||
1702 !UpdateExistingPairEntriesOnChain(
1703 delivery_position,
Value(delivery_position), priority_queue,
1704 pickup_to_entries, delivery_to_entries)) {
1710 if (!AddPairEntriesAfter(pair_indices, vehicle, pickup,
1712 priority_queue, pickup_to_entries,
1713 delivery_to_entries) ||
1714 !AddPairEntriesAfter(pair_indices, vehicle, delivery,
1716 priority_queue, pickup_to_entries,
1717 delivery_to_entries)) {
1720 SetVehicleIndex(pickup, vehicle);
1721 SetVehicleIndex(delivery, vehicle);
1725 bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingPairEntriesOnChain(
1726 int64_t insert_after_start, int64_t insert_after_end,
1728 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1729 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1731 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1732 delivery_to_entries) {
1733 int64_t insert_after = insert_after_start;
1734 while (insert_after != insert_after_end) {
1735 DCHECK(!
model()->IsEnd(insert_after));
1738 std::vector<PairEntry*> to_remove;
1739 for (
const PairEntries* pair_entries :
1740 {&pickup_to_entries->at(insert_after),
1741 &delivery_to_entries->at(insert_after)}) {
1742 if (StopSearchAndCleanup(priority_queue))
return false;
1743 for (PairEntry*
const pair_entry : *pair_entries) {
1744 DCHECK(priority_queue->
Contains(pair_entry));
1745 if (
Contains(pair_entry->pickup_to_insert()) ||
1746 Contains(pair_entry->delivery_to_insert())) {
1747 to_remove.push_back(pair_entry);
1749 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1750 .contains(pair_entry));
1751 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1752 .contains(pair_entry));
1753 UpdatePairEntry(pair_entry, priority_queue);
1757 for (PairEntry*
const pair_entry : to_remove) {
1758 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1759 delivery_to_entries);
1761 insert_after =
Value(insert_after);
1766 bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithPickupAfter(
1767 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
1768 int64_t insert_after, int64_t skip_entries_inserting_delivery_after,
1770 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1771 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1773 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1774 delivery_to_entries) {
1776 const int64_t pickup_insert_before =
Value(insert_after);
1779 DCHECK(pickup_to_entries->at(insert_after).empty());
1780 for (
const int64_t pickup :
1782 cost_class, insert_after)) {
1783 if (StopSearchAndCleanup(priority_queue))
return false;
1785 for (
const std::pair<int, int>& index_pairs :
1786 model()->GetPickupIndexPairs(pickup)) {
1787 if (!pair_indices.contains(index_pairs.first))
continue;
1789 pickup_delivery_pairs[index_pairs.first];
1790 for (
const int64_t delivery : index_pair.second) {
1794 int64_t delivery_insert_after = pickup;
1795 while (!
model()->IsEnd(delivery_insert_after)) {
1796 if (delivery_insert_after != skip_entries_inserting_delivery_after) {
1797 AddPairEntry(pickup, insert_after, delivery, delivery_insert_after,
1798 vehicle, priority_queue, pickup_to_entries,
1799 delivery_to_entries);
1801 if (delivery_insert_after == pickup) {
1802 delivery_insert_after = pickup_insert_before;
1804 delivery_insert_after =
Value(delivery_insert_after);
1813 bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithDeliveryAfter(
1814 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
1815 int64_t insert_after,
1817 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1818 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1820 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1821 delivery_to_entries) {
1825 for (
const int64_t delivery :
1827 cost_class, insert_after)) {
1828 if (StopSearchAndCleanup(priority_queue))
return false;
1830 for (
const std::pair<int, int>& index_pairs :
1831 model()->GetDeliveryIndexPairs(delivery)) {
1832 if (!pair_indices.contains(index_pairs.first))
continue;
1834 pickup_delivery_pairs[index_pairs.first];
1835 for (
const int64_t pickup : index_pair.first) {
1837 int64_t pickup_insert_after =
model()->
Start(vehicle);
1838 while (pickup_insert_after != insert_after) {
1839 AddPairEntry(pickup, pickup_insert_after, delivery, insert_after,
1840 vehicle, priority_queue, pickup_to_entries,
1841 delivery_to_entries);
1842 pickup_insert_after =
Value(pickup_insert_after);
1850 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1851 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1853 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1854 std::vector<PairEntries>* pickup_to_entries,
1855 std::vector<PairEntries>* delivery_to_entries) {
1856 priority_queue->
Remove(entry);
1857 if (entry->pickup_insert_after() != -1) {
1858 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1860 if (entry->delivery_insert_after() != -1) {
1861 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1863 pair_entry_allocator_.FreeEntry(entry);
1866 void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1867 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1868 int64_t delivery_insert_after,
int vehicle,
1870 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1871 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1873 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1874 delivery_entries)
const {
1877 if (!pickup_vehicle_var->Contains(vehicle) ||
1878 !delivery_vehicle_var->Contains(vehicle)) {
1882 const auto vehicle_is_compatible = [pickup_vehicle_var,
1883 delivery_vehicle_var](
int vehicle) {
1884 return pickup_vehicle_var->
Contains(vehicle) &&
1885 delivery_vehicle_var->Contains(vehicle);
1887 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
1888 empty_vehicle_type_curator_->Type(vehicle),
1889 vehicle_is_compatible)) {
1893 const int num_allowed_vehicles =
1894 std::min(pickup_vehicle_var->Size(), delivery_vehicle_var->Size());
1895 if (pickup_insert_after == -1) {
1896 DCHECK_EQ(delivery_insert_after, -1);
1897 DCHECK_EQ(vehicle, -1);
1898 PairEntry* pair_entry = pair_entry_allocator_.NewEntry(
1899 pickup, -1, delivery, -1, -1, num_allowed_vehicles);
1900 pair_entry->set_value(
1901 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1905 priority_queue->
Add(pair_entry);
1909 PairEntry*
const pair_entry = pair_entry_allocator_.NewEntry(
1910 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle,
1911 num_allowed_vehicles);
1912 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1913 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1916 DCHECK(!priority_queue->
Contains(pair_entry));
1917 pickup_entries->at(pickup_insert_after).insert(pair_entry);
1918 delivery_entries->at(delivery_insert_after).insert(pair_entry);
1919 priority_queue->
Add(pair_entry);
1922 void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1923 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1925 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1927 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1928 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1929 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1930 pair_entry->vehicle()));
1933 DCHECK(priority_queue->
Contains(pair_entry));
1938 GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1939 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1940 int64_t delivery_insert_after,
int vehicle)
const {
1941 DCHECK_GE(pickup_insert_after, 0);
1942 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1944 pickup, pickup_insert_after, pickup_insert_before, vehicle);
1946 DCHECK_GE(delivery_insert_after, 0);
1947 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1948 ? pickup_insert_before
1949 :
Value(delivery_insert_after);
1951 delivery, delivery_insert_after, delivery_insert_before, vehicle);
1953 const int64_t penalty_shift =
1954 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1957 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
1960 bool GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1961 const std::vector<bool>&
nodes,
const absl::flat_hash_set<int>& vehicles,
1962 NodeEntryQueue* queue) {
1965 const int num_vehicles =
1967 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
1969 for (
int node = 0; node <
nodes.size(); node++) {
1977 AddNodeEntry(node, node, -1, all_vehicles, queue);
1980 InitializeInsertionEntriesPerformingNode(node, vehicles, queue);
1985 void GlobalCheapestInsertionFilteredHeuristic::
1986 InitializeInsertionEntriesPerformingNode(
1987 int64_t node,
const absl::flat_hash_set<int>& vehicles,
1988 NodeEntryQueue* queue) {
1989 const int num_vehicles =
1991 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
1994 auto vehicles_it = vehicles.begin();
1995 std::vector<NodeInsertion> insertions;
1996 for (
int v = 0; v < num_vehicles; v++) {
1997 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
2001 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
2002 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
2010 for (
const NodeInsertion& insertion : insertions) {
2011 DCHECK_EQ(insertion.vehicle, vehicle);
2012 AddNodeEntry(node, insertion.insert_after, vehicle, all_vehicles,
2021 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
2022 int v,
int cost_class) {
2024 (all_vehicles || vehicles.contains(v));
2028 for (
const int64_t insert_after :
2030 cost_class, node)) {
2034 const int vehicle = node_index_to_vehicle_[insert_after];
2035 if (vehicle == -1 ||
2036 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
2040 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
2041 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
2046 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2051 bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterNodeInsertion(
2052 const std::vector<bool>&
nodes,
int vehicle, int64_t node,
2053 int64_t insert_after,
bool all_vehicles, NodeEntryQueue* queue) {
2056 if (!UpdateExistingNodeEntriesOnChain(
nodes, vehicle, insert_after,
2057 Value(insert_after), all_vehicles,
2062 if (!AddNodeEntriesAfter(
nodes, vehicle, node, all_vehicles, queue)) {
2065 SetVehicleIndex(node, vehicle);
2069 bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingNodeEntriesOnChain(
2070 const std::vector<bool>&
nodes,
int vehicle, int64_t insert_after_start,
2071 int64_t insert_after_end,
bool all_vehicles, NodeEntryQueue* queue) {
2072 int64_t insert_after = insert_after_start;
2073 while (insert_after != insert_after_end) {
2074 DCHECK(!
model()->IsEnd(insert_after));
2075 AddNodeEntriesAfter(
nodes, vehicle, insert_after, all_vehicles, queue);
2076 insert_after =
Value(insert_after);
2081 bool GlobalCheapestInsertionFilteredHeuristic::AddNodeEntriesAfter(
2082 const std::vector<bool>&
nodes,
int vehicle, int64_t insert_after,
2083 bool all_vehicles, NodeEntryQueue* queue) {
2087 queue->ClearInsertions(insert_after);
2090 cost_class, insert_after)) {
2093 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2099 void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2100 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2101 NodeEntryQueue* queue)
const {
2103 const int64_t penalty_shift =
2104 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2108 if (!vehicle_var->Contains(vehicle)) {
2112 const auto vehicle_is_compatible = [vehicle_var](
int vehicle) {
2113 return vehicle_var->
Contains(vehicle);
2115 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2116 empty_vehicle_type_curator_->Type(vehicle),
2117 vehicle_is_compatible)) {
2121 const int num_allowed_vehicles = vehicle_var->Size();
2122 if (vehicle == -1) {
2123 DCHECK_EQ(node, insert_after);
2124 if (!all_vehicles) {
2130 queue->PushInsertion(node, node, -1, num_allowed_vehicles,
2131 CapSub(node_penalty, penalty_shift));
2136 node, insert_after,
Value(insert_after), vehicle);
2137 if (!all_vehicles && insertion_cost > node_penalty) {
2144 queue->PushInsertion(node, insert_after, vehicle, num_allowed_vehicles,
2145 CapSub(insertion_cost, penalty_shift));
2151 int pickup,
const std::vector<int>& path,
2152 const std::vector<bool>& node_is_pickup,
2153 const std::vector<bool>& node_is_delivery,
2154 std::vector<PickupDeliveryInsertion>& insertions) {
2155 const int num_nodes = path.size();
2156 DCHECK_GE(num_nodes, 2);
2157 const int kNoPrevIncrease = -1;
2158 const int kNoNextDecrease = num_nodes;
2160 prev_decrease_.resize(num_nodes - 1);
2161 prev_increase_.resize(num_nodes - 1);
2162 int prev_decrease = 0;
2163 int prev_increase = kNoPrevIncrease;
2164 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2165 if (node_is_delivery[path[pos]]) prev_decrease = pos;
2166 prev_decrease_[pos] = prev_decrease;
2167 if (node_is_pickup[path[pos]]) prev_increase = pos;
2168 prev_increase_[pos] = prev_increase;
2172 next_decrease_.resize(num_nodes - 1);
2173 next_increase_.resize(num_nodes - 1);
2174 int next_increase = num_nodes - 1;
2175 int next_decrease = kNoNextDecrease;
2176 for (
int pos = num_nodes - 2; pos >= 0; --pos) {
2177 next_decrease_[pos] = next_decrease;
2178 if (node_is_delivery[path[pos]]) next_decrease = pos;
2179 next_increase_[pos] = next_increase;
2180 if (node_is_pickup[path[pos]]) next_increase = pos;
2184 auto append = [pickup, num_nodes, &path, &insertions](
int pickup_pos,
2186 if (pickup_pos < 0 || num_nodes - 1 <= pickup_pos)
return;
2187 if (delivery_pos < 0 || num_nodes - 1 <= delivery_pos)
return;
2191 pickup_pos == delivery_pos ? pickup : path[delivery_pos];
2192 insertions.push_back(insertion);
2196 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2197 const bool is_after_decrease = prev_increase_[pos] < prev_decrease_[pos];
2198 const bool is_before_increase = next_increase_[pos] < next_decrease_[pos];
2199 if (is_after_decrease) {
2200 append(prev_increase_[pos], pos);
2201 if (is_before_increase) {
2202 append(pos, next_increase_[pos] - 1);
2203 append(pos, next_decrease_[pos] - 1);
2211 if (next_increase_[pos] - 1 != pos) {
2213 if (prev_decrease_[pos] != pos) append(prev_decrease_[pos], pos);
2217 append(pos, next_decrease_[pos] - 1);
2218 if (!is_before_increase && next_decrease_[pos] - 1 != pos) {
2227 if (prev_increase_[pos] != pos) append(prev_increase_[pos], pos);
2238 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
2239 RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy,
2242 std::move(evaluator), nullptr,
2244 update_start_end_distances_per_node_(true),
2245 pair_insertion_strategy_(pair_insertion_strategy) {
2247 pair_insertion_strategy_ ==
2248 RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR);
2253 if (update_start_end_distances_per_node_) {
2254 update_start_end_distances_per_node_ =
false;
2255 std::vector<int> all_vehicles(
model()->vehicles());
2256 std::iota(std::begin(all_vehicles),
std::end(all_vehicles), 0);
2257 start_end_distances_per_node_ =
2262 bool LocalCheapestInsertionFilteredHeuristic::InsertPair(
2263 int64_t pickup, int64_t insert_pickup_after, int64_t delivery,
2264 int64_t insert_delivery_after,
int vehicle) {
2265 const int64_t insert_pickup_before =
Value(insert_pickup_after);
2266 InsertBetween(pickup, insert_pickup_after, insert_pickup_before, vehicle);
2267 DCHECK_NE(insert_delivery_after, insert_pickup_after);
2268 const int64_t insert_delivery_before = (insert_delivery_after == pickup)
2269 ? insert_pickup_before
2270 :
Value(insert_delivery_after);
2271 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
2276 void LocalCheapestInsertionFilteredHeuristic::InsertBestPickupThenDelivery(
2278 for (int64_t pickup : index_pair.first) {
2279 std::vector<NodeInsertion> pickup_insertions =
2280 ComputeEvaluatorSortedPositions(pickup);
2281 for (int64_t delivery : index_pair.second) {
2283 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
2284 const int vehicle = pickup_insertion.vehicle;
2285 for (
const NodeInsertion& delivery_insertion :
2286 ComputeEvaluatorSortedPositionsOnRouteAfter(
2287 delivery, pickup,
Value(pickup_insertion.insert_after),
2289 if (InsertPair(pickup, pickup_insertion.insert_after, delivery,
2290 delivery_insertion.insert_after, vehicle)) {
2300 void LocalCheapestInsertionFilteredHeuristic::InsertBestPair(
2302 for (int64_t pickup : index_pair.first) {
2303 for (int64_t delivery : index_pair.second) {
2305 std::optional<std::vector<InsertionGenerator::PickupDeliveryInsertion>>
2306 sorted_pair_positions =
2307 ComputeEvaluatorSortedPairPositions(pickup, delivery);
2308 if (!sorted_pair_positions.has_value())
return;
2309 for (
const auto [insert_pickup_after, insert_delivery_after, unused_value,
2310 vehicle] : *sorted_pair_positions) {
2311 if (InsertPair(pickup, insert_pickup_after, delivery,
2312 insert_delivery_after, vehicle)) {
2321 void LocalCheapestInsertionFilteredHeuristic::InsertBestPairMultitour(
2323 const std::vector<bool>& node_is_pickup,
2324 const std::vector<bool>& node_is_delivery) {
2325 using Insertion = InsertionGenerator::PickupDeliveryInsertion;
2326 std::vector<Insertion> insertions;
2327 std::vector<int> path;
2330 auto fill_path = [&path,
this](
int vehicle) {
2335 path.push_back(node);
2337 path.push_back(
end);
2341 auto price_insertions = [
this](
int pickup,
int delivery,
2342 std::vector<Insertion>& insertions) {
2343 for (Insertion& insertion : insertions) {
2344 const int pickup_after = insertion.insert_pickup_after;
2345 const int pickup_before =
Value(insertion.insert_pickup_after);
2346 const int delivery_after = insertion.insert_delivery_after;
2347 const int delivery_before = insertion.insert_delivery_after == pickup
2349 :
Value(insertion.insert_delivery_after);
2352 InsertBetween(pickup, pickup_after, pickup_before, insertion.vehicle);
2355 std::optional<int64_t> insertion_cost =
Evaluate(
false);
2356 insertion.value = insertion_cost.value_or(
kint64max);
2359 pickup, pickup_after, pickup_before, insertion.vehicle);
2361 delivery, delivery_after, delivery_before, insertion.vehicle);
2362 insertion.value =
CapAdd(pickup_cost, delivery_cost);
2367 for (int64_t pickup : index_pair.first) {
2370 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2372 const int start_index = insertions.size();
2374 pickup, path, node_is_pickup, node_is_delivery, insertions);
2375 for (
int i = start_index; i < insertions.size(); ++i) {
2376 insertions[i].vehicle = vehicle;
2379 for (int64_t delivery : index_pair.second) {
2381 price_insertions(pickup, delivery, insertions);
2382 const auto end = std::partition(
2383 insertions.begin(), insertions.end(),
2384 [](
const Insertion& ins) { return ins.value != kint64max; });
2386 std::sort(insertions.begin(),
end);
2388 for (
int i = 0; i < num_insertions; ++i) {
2390 if (InsertPair(pickup, insertions[i].insert_pickup_after, delivery,
2391 insertions[i].insert_delivery_after,
2392 insertions[i].vehicle)) {
2400 void LocalCheapestInsertionFilteredHeuristic::SetIndexPairVisited(
2402 for (
const int64_t pickup : index_pair.first) {
2403 visited_[pickup] =
true;
2405 for (
const int64_t delivery : index_pair.second) {
2406 visited_[delivery] =
true;
2412 visited_.assign(
model()->
Size(),
false);
2418 struct PairDomainSize {
2419 uint64_t domain_size;
2422 bool operator<(
const PairDomainSize& other)
const {
2423 return std::tie(domain_size, pair_index) <
2424 std::tie(other.domain_size, other.pair_index);
2427 std::vector<PairDomainSize> pair_domain_sizes;
2428 for (
int pair_index = 0; pair_index < index_pairs.size(); ++pair_index) {
2429 bool pickup_is_contained =
false;
2431 for (int64_t pickup : index_pairs[pair_index].first) {
2433 pickup_is_contained |=
Contains(pickup);
2435 bool delivery_is_contained =
false;
2436 for (int64_t delivery : index_pairs[pair_index].second) {
2439 delivery_is_contained |=
Contains(delivery);
2441 if (pickup_is_contained && delivery_is_contained) {
2442 SetIndexPairVisited(index_pairs[pair_index]);
2443 }
else if (!pickup_is_contained && !delivery_is_contained) {
2444 pair_domain_sizes.push_back({domain_size, pair_index});
2447 std::sort(pair_domain_sizes.begin(), pair_domain_sizes.end());
2448 std::vector<bool> node_is_pickup, node_is_delivery;
2449 if (pair_insertion_strategy_ ==
2450 RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR_MULTITOUR) {
2452 node_is_pickup.resize(num_nodes,
false);
2453 node_is_delivery.resize(num_nodes,
false);
2454 for (
const auto& index_pair : index_pairs) {
2455 for (
const int pickup : index_pair.first) {
2456 node_is_pickup[pickup] =
true;
2458 for (
const int delivery : index_pair.second) {
2459 node_is_delivery[delivery] =
true;
2465 for (
const PairDomainSize& pair_domain_size : pair_domain_sizes) {
2466 const auto index_pair = index_pairs[pair_domain_size.pair_index];
2467 switch (pair_insertion_strategy_) {
2468 case RoutingSearchParameters::AUTOMATIC:
2469 case RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR:
2470 InsertBestPair(index_pair);
2472 case RoutingSearchParameters::BEST_PICKUP_THEN_BEST_DELIVERY:
2473 InsertBestPickupThenDelivery(index_pair);
2475 case RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR_MULTITOUR:
2476 InsertBestPairMultitour(index_pair, node_is_pickup, node_is_delivery);
2479 LOG(ERROR) <<
"Unknown pair insertion strategy value.";
2485 SetIndexPairVisited(index_pair);
2488 std::priority_queue<Seed> node_queue;
2492 while (!node_queue.empty()) {
2493 const int node = node_queue.top().second;
2495 if (
Contains(node) || visited_[node])
continue;
2497 ComputeEvaluatorSortedPositions(node)) {
2511 std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
2512 LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2515 std::vector<NodeInsertion> sorted_insertions;
2518 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2521 false, &sorted_insertions);
2523 std::sort(sorted_insertions.begin(), sorted_insertions.end());
2525 return sorted_insertions;
2528 std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
2529 LocalCheapestInsertionFilteredHeuristic::
2530 ComputeEvaluatorSortedPositionsOnRouteAfter(int64_t node, int64_t
start,
2531 int64_t next_after_start,
2534 std::vector<NodeInsertion> sorted_insertions;
2538 false, &sorted_insertions);
2539 std::sort(sorted_insertions.begin(), sorted_insertions.end());
2541 return sorted_insertions;
2544 std::optional<std::vector<InsertionGenerator::PickupDeliveryInsertion>>
2545 LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions(
2546 int64_t pickup, int64_t delivery) {
2547 std::vector<InsertionGenerator::PickupDeliveryInsertion>
2548 sorted_pickup_delivery_insertions;
2550 DCHECK_LT(pickup, size);
2551 DCHECK_LT(delivery, size);
2552 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2553 int64_t insert_pickup_after =
model()->
Start(vehicle);
2554 while (!
model()->IsEnd(insert_pickup_after)) {
2555 const int64_t insert_pickup_before =
Value(insert_pickup_after);
2556 int64_t insert_delivery_after = pickup;
2557 while (!
model()->IsEnd(insert_delivery_after)) {
2559 const int64_t insert_delivery_before =
2560 insert_delivery_after == pickup ? insert_pickup_before
2561 :
Value(insert_delivery_after);
2563 InsertBetween(pickup, insert_pickup_after, insert_pickup_before,
2565 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
2567 std::optional<int64_t> insertion_cost =
Evaluate(
false);
2568 if (insertion_cost.has_value()) {
2569 sorted_pickup_delivery_insertions.push_back(
2570 {insert_pickup_after, insert_delivery_after, *insertion_cost,
2574 sorted_pickup_delivery_insertions.push_back(
2575 {insert_pickup_after, insert_delivery_after,
2577 pickup, insert_pickup_after, insert_pickup_before,
2580 delivery, insert_delivery_after,
2581 insert_delivery_before, vehicle)),
2584 insert_delivery_after = insert_delivery_before;
2586 insert_pickup_after = insert_pickup_before;
2589 std::sort(sorted_pickup_delivery_insertions.begin(),
2590 sorted_pickup_delivery_insertions.end());
2591 return std::optional<
2592 std::vector<InsertionGenerator::PickupDeliveryInsertion>>{
2593 sorted_pickup_delivery_insertions};
2606 std::vector<std::vector<int64_t>> deliveries(
Size());
2607 std::vector<std::vector<int64_t>> pickups(
Size());
2609 for (
int first : pair.first) {
2610 for (
int second : pair.second) {
2611 deliveries[first].push_back(second);
2612 pickups[second].push_back(first);
2619 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
2620 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2621 sorted_vehicles[vehicle] = vehicle;
2623 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
2624 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
2626 for (
const int vehicle : sorted_vehicles) {
2628 bool extend_route =
true;
2633 while (extend_route) {
2634 extend_route =
false;
2636 int64_t
index = last_node;
2645 std::vector<int64_t> neighbors;
2647 std::unique_ptr<IntVarIterator> it(
2648 model()->Nexts()[
index]->MakeDomainIterator(
false));
2650 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
2653 for (
int i = 0; !found && i < neighbors.size(); ++i) {
2657 next = FindTopSuccessor(
index, neighbors);
2660 SortSuccessors(
index, &neighbors);
2661 ABSL_FALLTHROUGH_INTENDED;
2663 next = neighbors[i];
2670 bool contains_pickups =
false;
2671 for (int64_t pickup : pickups[
next]) {
2673 contains_pickups =
true;
2677 if (!contains_pickups) {
2681 std::vector<int64_t> next_deliveries;
2682 if (
next < deliveries.size()) {
2683 next_deliveries = GetPossibleNextsFromIterator(
2686 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
2687 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
2692 delivery = FindTopSuccessor(
next, next_deliveries);
2695 SortSuccessors(
next, &next_deliveries);
2696 ABSL_FALLTHROUGH_INTENDED;
2698 delivery = next_deliveries[j];
2716 if (
model()->IsEnd(
end) && last_node != delivery) {
2717 last_node = delivery;
2718 extend_route =
true;
2733 bool CheapestAdditionFilteredHeuristic::
2734 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
2735 int vehicle2)
const {
2736 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2737 builder_.GetStartChainEnd(vehicle1));
2738 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
2739 builder_.GetStartChainEnd(vehicle2));
2740 if (has_partial_route1 == has_partial_route2) {
2741 return vehicle2 < vehicle1;
2743 return has_partial_route2 < has_partial_route1;
2752 std::function<int64_t(int64_t, int64_t)> evaluator,
2758 int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2759 int64_t node,
const std::vector<int64_t>& successors) {
2761 int64_t best_successor = -1;
2762 for (int64_t successor : successors) {
2763 const int64_t evaluation = (successor >= 0)
2764 ? evaluator_(node, successor)
2766 if (evaluation < best_evaluation ||
2767 (evaluation == best_evaluation && successor > best_successor)) {
2768 best_evaluation = evaluation;
2769 best_successor = successor;
2772 return best_successor;
2775 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2776 int64_t node, std::vector<int64_t>* successors) {
2777 std::vector<std::pair<int64_t, int64_t>> values;
2778 values.reserve(successors->size());
2779 for (int64_t successor : *successors) {
2782 values.push_back({evaluator_(node, successor), -successor});
2784 std::sort(values.begin(), values.end());
2785 successors->clear();
2786 for (
auto value : values) {
2787 successors->push_back(-
value.second);
2800 comparator_(std::move(comparator)) {}
2802 int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2803 int64_t node,
const std::vector<int64_t>& successors) {
2804 return *std::min_element(successors.begin(), successors.end(),
2805 [
this, node](
int successor1,
int successor2) {
2806 return comparator_(node, successor1, successor2);
2810 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2811 int64_t node, std::vector<int64_t>* successors) {
2812 std::sort(successors->begin(), successors->end(),
2813 [
this, node](
int successor1,
int successor2) {
2814 return comparator_(node, successor1, successor2);
2866 template <
typename Saving>
2871 : savings_db_(savings_db),
2872 index_in_sorted_savings_(0),
2873 vehicle_types_(vehicle_types),
2874 single_vehicle_type_(vehicle_types == 1),
2875 using_incoming_reinjected_saving_(false),
2880 sorted_savings_per_vehicle_type_.clear();
2881 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
2882 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2883 savings.reserve(size * saving_neighbors);
2886 sorted_savings_.clear();
2887 costs_and_savings_per_arc_.clear();
2888 arc_indices_per_before_node_.clear();
2890 if (!single_vehicle_type_) {
2891 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
2892 arc_indices_per_before_node_.resize(size);
2893 for (
int before_node = 0; before_node < size; before_node++) {
2894 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
2897 skipped_savings_starting_at_.clear();
2898 skipped_savings_starting_at_.resize(size);
2899 skipped_savings_ending_at_.clear();
2900 skipped_savings_ending_at_.resize(size);
2901 incoming_reinjected_savings_ =
nullptr;
2902 outgoing_reinjected_savings_ =
nullptr;
2903 incoming_new_reinjected_savings_ =
nullptr;
2904 outgoing_new_reinjected_savings_ =
nullptr;
2908 int64_t before_node, int64_t after_node,
int vehicle_type) {
2909 CHECK(!sorted_savings_per_vehicle_type_.empty())
2910 <<
"Container not initialized!";
2911 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
2912 UpdateArcIndicesCostsAndSavings(before_node, after_node,
2913 {total_cost, saving});
2917 CHECK(!sorted_) <<
"Container already sorted!";
2919 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2920 std::sort(savings.begin(), savings.end());
2923 if (single_vehicle_type_) {
2924 const auto& savings = sorted_savings_per_vehicle_type_[0];
2925 sorted_savings_.resize(savings.size());
2926 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
2927 [](
const Saving& saving) {
2928 return SavingAndArc({saving, -1});
2934 sorted_savings_.reserve(vehicle_types_ *
2935 costs_and_savings_per_arc_.size());
2937 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
2939 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2940 costs_and_savings_per_arc_[arc_index];
2941 DCHECK(!costs_and_savings.empty());
2944 costs_and_savings.begin(), costs_and_savings.end(),
2945 [](
const std::pair<int64_t, Saving>& cs1,
2946 const std::pair<int64_t, Saving>& cs2) { return cs1 > cs2; });
2951 const int64_t
cost = costs_and_savings.back().first;
2952 while (!costs_and_savings.empty() &&
2953 costs_and_savings.back().first ==
cost) {
2954 sorted_savings_.push_back(
2955 {costs_and_savings.back().second, arc_index});
2956 costs_and_savings.pop_back();
2959 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2960 next_saving_type_and_index_for_arc_.clear();
2961 next_saving_type_and_index_for_arc_.resize(
2962 costs_and_savings_per_arc_.size(), {-1, -1});
2965 index_in_sorted_savings_ = 0;
2970 return index_in_sorted_savings_ < sorted_savings_.size() ||
2971 HasReinjectedSavings();
2975 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
2977 <<
"Update() should be called between two calls to GetSaving() !";
2981 if (HasReinjectedSavings()) {
2982 if (incoming_reinjected_savings_ !=
nullptr &&
2983 outgoing_reinjected_savings_ !=
nullptr) {
2985 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
2986 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
2987 if (incoming_saving < outgoing_saving) {
2988 current_saving_ = incoming_saving;
2989 using_incoming_reinjected_saving_ =
true;
2991 current_saving_ = outgoing_saving;
2992 using_incoming_reinjected_saving_ =
false;
2995 if (incoming_reinjected_savings_ !=
nullptr) {
2996 current_saving_ = incoming_reinjected_savings_->front();
2997 using_incoming_reinjected_saving_ =
true;
2999 if (outgoing_reinjected_savings_ !=
nullptr) {
3000 current_saving_ = outgoing_reinjected_savings_->front();
3001 using_incoming_reinjected_saving_ =
false;
3005 current_saving_ = sorted_savings_[index_in_sorted_savings_];
3007 return current_saving_.saving;
3010 void Update(
bool update_best_saving,
int type = -1) {
3011 CHECK(to_update_) <<
"Container already up to date!";
3012 if (update_best_saving) {
3013 const int64_t arc_index = current_saving_.arc_index;
3014 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
3016 if (!HasReinjectedSavings()) {
3017 index_in_sorted_savings_++;
3019 if (index_in_sorted_savings_ == sorted_savings_.size()) {
3020 sorted_savings_.swap(next_savings_);
3022 index_in_sorted_savings_ = 0;
3024 std::sort(sorted_savings_.begin(), sorted_savings_.end());
3025 next_saving_type_and_index_for_arc_.clear();
3026 next_saving_type_and_index_for_arc_.resize(
3027 costs_and_savings_per_arc_.size(), {-1, -1});
3030 UpdateReinjectedSavings();
3035 CHECK(!single_vehicle_type_);
3036 Update(
true, type);
3040 CHECK(sorted_) <<
"Savings not sorted yet!";
3041 CHECK_LT(type, vehicle_types_);
3042 return sorted_savings_per_vehicle_type_[type];
3046 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
3047 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
3051 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
3052 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
3056 struct SavingAndArc {
3060 bool operator<(
const SavingAndArc& other)
const {
3061 return std::tie(saving, arc_index) <
3062 std::tie(other.saving, other.arc_index);
3068 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
3069 const Saving& saving = saving_and_arc.saving;
3070 const int64_t before_node = savings_db_->GetBeforeNodeFromSaving(saving);
3071 const int64_t after_node = savings_db_->GetAfterNodeFromSaving(saving);
3072 if (!savings_db_->Contains(before_node)) {
3073 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
3075 if (!savings_db_->Contains(after_node)) {
3076 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
3090 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
3091 if (single_vehicle_type_) {
3094 SkipSavingForArc(current_saving_);
3097 CHECK_GE(arc_index, 0);
3098 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
3099 const int previous_index = type_and_index.second;
3100 const int previous_type = type_and_index.first;
3101 bool next_saving_added =
false;
3104 if (previous_index >= 0) {
3106 DCHECK_GE(previous_type, 0);
3107 if (type == -1 || previous_type == type) {
3110 next_saving_added =
true;
3111 next_saving = next_savings_[previous_index].saving;
3115 if (!next_saving_added &&
3116 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
3117 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
3118 if (previous_index >= 0) {
3120 next_savings_[previous_index] = {next_saving, arc_index};
3123 type_and_index.second = next_savings_.size();
3124 next_savings_.push_back({next_saving, arc_index});
3126 next_saving_added =
true;
3132 SkipSavingForArc(current_saving_);
3136 if (next_saving_added) {
3137 SkipSavingForArc({next_saving, arc_index});
3142 void UpdateReinjectedSavings() {
3143 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
3144 &incoming_reinjected_savings_,
3145 using_incoming_reinjected_saving_);
3146 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
3147 &outgoing_reinjected_savings_,
3148 !using_incoming_reinjected_saving_);
3149 incoming_new_reinjected_savings_ =
nullptr;
3150 outgoing_new_reinjected_savings_ =
nullptr;
3153 void UpdateGivenReinjectedSavings(
3154 std::deque<SavingAndArc>* new_reinjected_savings,
3155 std::deque<SavingAndArc>** reinjected_savings,
3156 bool using_reinjected_savings) {
3157 if (new_reinjected_savings ==
nullptr) {
3159 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
3160 CHECK(!(*reinjected_savings)->empty());
3161 (*reinjected_savings)->pop_front();
3162 if ((*reinjected_savings)->empty()) {
3163 *reinjected_savings =
nullptr;
3172 if (*reinjected_savings !=
nullptr) {
3173 (*reinjected_savings)->clear();
3175 *reinjected_savings =
nullptr;
3176 if (!new_reinjected_savings->empty()) {
3177 *reinjected_savings = new_reinjected_savings;
3181 bool HasReinjectedSavings() {
3182 return outgoing_reinjected_savings_ !=
nullptr ||
3183 incoming_reinjected_savings_ !=
nullptr;
3186 void UpdateArcIndicesCostsAndSavings(
3187 int64_t before_node, int64_t after_node,
3188 const std::pair<int64_t, Saving>& cost_and_saving) {
3189 if (single_vehicle_type_) {
3192 absl::flat_hash_map<int, int>& arc_indices =
3193 arc_indices_per_before_node_[before_node];
3194 const auto& arc_inserted = arc_indices.insert(
3195 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
3196 const int index = arc_inserted.first->second;
3197 if (arc_inserted.second) {
3198 costs_and_savings_per_arc_.push_back({cost_and_saving});
3200 DCHECK_LT(
index, costs_and_savings_per_arc_.size());
3201 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
3205 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
3206 Saving* next_saving) {
3207 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
3208 costs_and_savings_per_arc_[arc_index];
3210 bool found_saving =
false;
3211 while (!costs_and_savings.empty() && !found_saving) {
3212 const Saving& saving = costs_and_savings.back().second;
3213 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
3214 *next_saving = saving;
3215 found_saving =
true;
3217 costs_and_savings.pop_back();
3219 return found_saving;
3222 const SavingsFilteredHeuristic*
const savings_db_;
3223 int64_t index_in_sorted_savings_;
3224 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
3225 std::vector<SavingAndArc> sorted_savings_;
3226 std::vector<SavingAndArc> next_savings_;
3227 std::vector<std::pair< int,
int>>
3228 next_saving_type_and_index_for_arc_;
3229 SavingAndArc current_saving_;
3230 std::vector<std::vector<std::pair< int64_t, Saving>>>
3231 costs_and_savings_per_arc_;
3232 std::vector<absl::flat_hash_map< int,
int>>
3233 arc_indices_per_before_node_;
3234 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
3235 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
3236 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
3237 std::deque<SavingAndArc>* incoming_reinjected_savings_;
3238 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
3239 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
3240 const int vehicle_types_;
3241 const bool single_vehicle_type_;
3242 bool using_incoming_reinjected_saving_;
3249 SavingsFilteredHeuristic::SavingsFilteredHeuristic(
3253 vehicle_type_curator_(nullptr),
3260 size_squared_ = size * size;
3268 model()->GetVehicleTypeContainer());
3273 if (!ComputeSavings())
return false;
3278 if (!
Evaluate(
true).has_value())
return false;
3284 int type, int64_t before_node, int64_t after_node) {
3285 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
3301 ->GetCompatibleVehicleOfType(
3302 type, vehicle_is_compatible,
3303 [](
int) {
return false; })
3307 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
3308 std::vector<std::vector<int64_t>>* adjacency_lists) {
3309 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
3310 for (int64_t neighbor : (*adjacency_lists)[node]) {
3311 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
3314 (*adjacency_lists)[neighbor].push_back(node);
3317 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
3318 adjacency_lists->begin(), [](std::vector<int64_t> vec) {
3319 std::sort(vec.begin(), vec.end());
3320 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
3336 bool SavingsFilteredHeuristic::ComputeSavings() {
3340 std::vector<int64_t> uncontained_non_start_end_nodes;
3341 uncontained_non_start_end_nodes.reserve(size);
3342 for (
int node = 0; node < size; node++) {
3344 uncontained_non_start_end_nodes.push_back(node);
3348 const int64_t saving_neighbors =
3349 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
3350 static_cast<int64_t
>(uncontained_non_start_end_nodes.size()));
3353 std::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
3356 std::vector<std::vector<int64_t>> adjacency_lists(size);
3358 for (
int type = 0; type < num_vehicle_types; ++type) {
3361 if (vehicle < 0)
continue;
3363 const int64_t cost_class =
3371 for (
int before_node : uncontained_non_start_end_nodes) {
3372 std::vector<std::pair< int64_t, int64_t>>
3374 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
3376 for (
int after_node : uncontained_non_start_end_nodes) {
3377 if (after_node != before_node) {
3378 costed_after_nodes.push_back(std::make_pair(
3379 model()->GetArcCostForClass(before_node, after_node, cost_class),
3383 if (saving_neighbors < costed_after_nodes.size()) {
3384 std::nth_element(costed_after_nodes.begin(),
3385 costed_after_nodes.begin() + saving_neighbors,
3386 costed_after_nodes.end());
3387 costed_after_nodes.resize(saving_neighbors);
3389 adjacency_lists[before_node].resize(costed_after_nodes.size());
3390 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
3391 adjacency_lists[before_node].begin(),
3392 [](std::pair<int64_t, int64_t> cost_and_node) {
3393 return cost_and_node.second;
3397 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
3402 for (
int before_node : uncontained_non_start_end_nodes) {
3403 const int64_t before_to_end_cost =
3405 const int64_t start_to_before_cost =
3409 for (int64_t after_node : adjacency_lists[before_node]) {
3410 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
3411 before_node == after_node ||
Contains(after_node)) {
3414 const int64_t arc_cost =
3416 const int64_t start_to_after_cost =
3419 const int64_t after_to_end_cost =
3422 const double weighted_arc_cost_fp =
3424 const int64_t weighted_arc_cost =
3426 ?
static_cast<int64_t
>(weighted_arc_cost_fp)
3427 : std::numeric_limits<int64_t>::
max();
3428 const int64_t saving_value =
CapSub(
3429 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
3432 BuildSaving(-saving_value, type, before_node, after_node);
3434 const int64_t total_cost =
3435 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
3446 int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
3447 int num_vehicle_types)
const {
3450 const int64_t num_neighbors_with_ratio =
3457 const double max_memory_usage_in_savings_unit =
3475 if (num_vehicle_types > 1) {
3476 multiplicative_factor += 1.5;
3478 const double num_savings =
3479 max_memory_usage_in_savings_unit / multiplicative_factor;
3480 const int64_t num_neighbors_with_memory_restriction =
3481 std::max(1.0, num_savings / (num_vehicle_types * size));
3483 return std::min(num_neighbors_with_ratio,
3484 num_neighbors_with_memory_restriction);
3489 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3491 DCHECK_GT(vehicle_types, 0);
3495 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
3496 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
3497 for (
int type = 0; type < vehicle_types; type++) {
3498 const int vehicle_type_offset = type * size;
3499 const std::vector<Saving>& sorted_savings_for_type =
3501 for (
const Saving& saving : sorted_savings_for_type) {
3504 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3506 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3517 const bool nodes_contained =
Contains(before_node) ||
Contains(after_node);
3519 if (nodes_contained) {
3538 const int saving_offset = type * size;
3540 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
3541 out_index < out_savings_ptr[saving_offset + before_node].size()) {
3544 int before_before_node = -1;
3545 int after_after_node = -1;
3546 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
3547 const Saving& in_saving =
3548 *(in_savings_ptr[saving_offset + after_node][in_index]);
3549 if (out_index < out_savings_ptr[saving_offset + before_node].size()) {
3550 const Saving& out_saving =
3551 *(out_savings_ptr[saving_offset + before_node][out_index]);
3562 *(out_savings_ptr[saving_offset + before_node][out_index]));
3565 if (after_after_node != -1) {
3566 DCHECK_EQ(before_before_node, -1);
3570 SetValue(after_node, after_after_node);
3574 after_node = after_after_node;
3579 CHECK_GE(before_before_node, 0);
3581 if (!
Contains(before_before_node)) {
3583 SetValue(before_before_node, before_node);
3586 before_node = before_before_node;
3597 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3603 first_node_on_route_.resize(vehicles, -1);
3604 last_node_on_route_.resize(vehicles, -1);
3605 vehicle_of_first_or_last_node_.resize(size, -1);
3607 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
3615 vehicle_of_first_or_last_node_[node] = vehicle;
3616 first_node_on_route_[vehicle] = node;
3623 vehicle_of_first_or_last_node_[node] = vehicle;
3624 last_node_on_route_[vehicle] = node;
3637 bool committed =
false;
3645 vehicle_of_first_or_last_node_[before_node] = vehicle;
3646 vehicle_of_first_or_last_node_[after_node] = vehicle;
3647 first_node_on_route_[vehicle] = before_node;
3648 last_node_on_route_[vehicle] = after_node;
3660 const int v1 = vehicle_of_first_or_last_node_[before_node];
3661 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3663 const int v2 = vehicle_of_first_or_last_node_[after_node];
3664 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3666 if (before_node == last_node && after_node == first_node && v1 != v2 &&
3668 CHECK_EQ(
Value(before_node),
model()->End(v1));
3669 CHECK_EQ(
Value(
model()->Start(v2)), after_node);
3675 MergeRoutes(v1, v2, before_node, after_node);
3680 const int vehicle = vehicle_of_first_or_last_node_[before_node];
3681 const int64_t last_node =
3682 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3684 if (before_node == last_node) {
3689 if (type != route_type) {
3700 if (first_node_on_route_[vehicle] != before_node) {
3702 DCHECK_NE(
Value(
model()->Start(vehicle)), before_node);
3703 vehicle_of_first_or_last_node_[before_node] = -1;
3705 vehicle_of_first_or_last_node_[after_node] = vehicle;
3706 last_node_on_route_[vehicle] = after_node;
3713 const int vehicle = vehicle_of_first_or_last_node_[after_node];
3714 const int64_t first_node =
3715 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3717 if (after_node == first_node) {
3722 if (type != route_type) {
3733 if (last_node_on_route_[vehicle] != after_node) {
3735 DCHECK_NE(
Value(after_node),
model()->End(vehicle));
3736 vehicle_of_first_or_last_node_[after_node] = -1;
3738 vehicle_of_first_or_last_node_[before_node] = vehicle;
3739 first_node_on_route_[vehicle] = before_node;
3748 void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
3750 int64_t before_node,
3751 int64_t after_node) {
3753 const int64_t new_first_node = first_node_on_route_[first_vehicle];
3754 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
3755 CHECK_EQ(
Value(
model()->Start(first_vehicle)), new_first_node);
3756 const int64_t new_last_node = last_node_on_route_[second_vehicle];
3757 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
3758 CHECK_EQ(
Value(new_last_node),
model()->End(second_vehicle));
3761 int used_vehicle = first_vehicle;
3762 int unused_vehicle = second_vehicle;
3763 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
3764 model()->GetFixedCostOfVehicle(second_vehicle)) {
3765 used_vehicle = second_vehicle;
3766 unused_vehicle = first_vehicle;
3771 if (used_vehicle == first_vehicle) {
3776 bool committed =
Evaluate(
true).has_value();
3778 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
3779 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
3781 std::swap(used_vehicle, unused_vehicle);
3784 if (used_vehicle == first_vehicle) {
3789 committed =
Evaluate(
true).has_value();
3795 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
3796 model()->GetFixedCostOfVehicle(unused_vehicle));
3799 first_node_on_route_[unused_vehicle] = -1;
3800 last_node_on_route_[unused_vehicle] = -1;
3801 vehicle_of_first_or_last_node_[before_node] = -1;
3802 vehicle_of_first_or_last_node_[after_node] = -1;
3803 first_node_on_route_[used_vehicle] = new_first_node;
3804 last_node_on_route_[used_vehicle] = new_last_node;
3805 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
3806 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
3816 use_minimum_matching_(use_minimum_matching) {}
3826 std::vector<int> indices(1, 0);
3827 for (
int i = 1; i < size; ++i) {
3828 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
3829 indices.push_back(i);
3833 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
3834 std::vector<bool> class_covered(num_cost_classes,
false);
3835 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3836 const int64_t cost_class =
3838 if (!class_covered[cost_class]) {
3839 class_covered[cost_class] =
true;
3842 auto cost = [
this, &indices,
start,
end, cost_class](
int from,
int to) {
3843 DCHECK_LT(from, indices.size());
3844 DCHECK_LT(to, indices.size());
3845 const int from_index = (from == 0) ?
start : indices[from];
3846 const int to_index = (to == 0) ?
end : indices[to];
3847 const int64_t
cost =
3855 using Cost = decltype(
cost);
3857 indices.size(),
cost);
3858 if (use_minimum_matching_) {
3861 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
3863 if (christofides_solver.
Solve()) {
3864 path_per_cost_class[cost_class] =
3870 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3871 const int64_t cost_class =
3873 const std::vector<int>& path = path_per_cost_class[cost_class];
3874 if (path.empty())
continue;
3875 DCHECK_EQ(0, path[0]);
3876 DCHECK_EQ(0, path.back());
3880 for (
int i = 1; i < path.size() - 1 && prev !=
end; ++i) {
3882 int next = indices[path[i]];
3910 struct SweepIndexSortAngle {
3911 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3912 return (node1.angle < node2.angle);
3914 } SweepIndexAngleComparator;
3916 struct SweepIndexSortDistance {
3917 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3918 return (node1.distance < node2.distance);
3920 } SweepIndexDistanceComparator;
3924 const std::vector<std::pair<int64_t, int64_t>>& points)
3925 : coordinates_(2 * points.size(), 0), sectors_(1) {
3926 for (int64_t i = 0; i < points.size(); ++i) {
3927 coordinates_[2 * i] = points[i].first;
3928 coordinates_[2 * i + 1] = points[i].second;
3935 const double pi_rad = 3.14159265;
3937 const int x0 = coordinates_[0];
3938 const int y0 = coordinates_[1];
3940 std::vector<SweepIndex> sweep_indices;
3941 for (int64_t
index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3943 const int x = coordinates_[2 *
index];
3944 const int y = coordinates_[2 *
index + 1];
3945 const double x_delta = x - x0;
3946 const double y_delta = y - y0;
3947 double square_distance = x_delta * x_delta + y_delta * y_delta;
3948 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3950 SweepIndex sweep_index(
index,
angle, square_distance);
3951 sweep_indices.push_back(sweep_index);
3953 std::sort(sweep_indices.begin(), sweep_indices.end(),
3954 SweepIndexDistanceComparator);
3956 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
3957 for (
int sector = 0; sector < sectors_; ++sector) {
3958 std::vector<SweepIndex> cluster;
3959 std::vector<SweepIndex>::iterator begin =
3960 sweep_indices.begin() + sector * size;
3961 std::vector<SweepIndex>::iterator
end =
3962 sector == sectors_ - 1 ? sweep_indices.end()
3963 : sweep_indices.begin() + (sector + 1) * size;
3964 std::sort(begin,
end, SweepIndexAngleComparator);
3966 for (
const SweepIndex& sweep_index : sweep_indices) {
3967 indices->push_back(sweep_index.index);
3995 class RouteConstructor {
3997 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
3998 bool check_assignment, int64_t num_indices,
3999 const std::vector<Link>& links_list)
4000 : assignment_(assignment),
4002 check_assignment_(check_assignment),
4003 solver_(model_->solver()),
4004 num_indices_(num_indices),
4005 links_list_(links_list),
4006 nexts_(model_->Nexts()),
4007 in_route_(num_indices_, -1),
4009 index_to_chain_index_(num_indices, -1),
4010 index_to_vehicle_class_index_(num_indices, -1) {
4012 const std::vector<std::string> dimension_names =
4013 model_->GetAllDimensionNames();
4014 dimensions_.assign(dimension_names.size(),
nullptr);
4015 for (
int i = 0; i < dimension_names.size(); ++i) {
4016 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
4019 cumuls_.resize(dimensions_.size());
4020 for (std::vector<int64_t>& cumuls :
cumuls_) {
4021 cumuls.resize(num_indices_);
4023 new_possible_cumuls_.resize(dimensions_.size());
4026 ~RouteConstructor() {}
4029 model_->solver()->TopPeriodicCheck();
4032 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
4033 std::vector<int> route(1,
index);
4034 routes_.push_back(route);
4035 in_route_[
index] = routes_.size() - 1;
4039 for (
const Link&
link : links_list_) {
4040 model_->solver()->TopPeriodicCheck();
4041 const int index1 =
link.link.first;
4042 const int index2 =
link.link.second;
4048 if (index_to_vehicle_class_index_[index1] < 0) {
4049 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4050 ++dimension_index) {
4051 cumuls_[dimension_index][index1] =
4052 std::max(dimensions_[dimension_index]->GetTransitValue(
4054 dimensions_[dimension_index]->CumulVar(index1)->Min());
4057 if (index_to_vehicle_class_index_[index2] < 0) {
4058 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4059 ++dimension_index) {
4060 cumuls_[dimension_index][index2] =
4061 std::max(dimensions_[dimension_index]->GetTransitValue(
4063 dimensions_[dimension_index]->CumulVar(index2)->Min());
4067 const int route_index1 = in_route_[index1];
4068 const int route_index2 = in_route_[index2];
4070 route_index1 >= 0 && route_index2 >= 0 &&
4071 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
4074 if (Merge(merge, route_index1, route_index2)) {
4080 model_->solver()->TopPeriodicCheck();
4084 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4085 if (!deleted_chains_.contains(chain_index)) {
4086 final_chains_.push_back(chains_[chain_index]);
4089 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
4090 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
4091 if (!deleted_routes_.contains(route_index)) {
4092 final_routes_.push_back(routes_[route_index]);
4095 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
4097 const int extra_vehicles =
std::max(
4098 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
4100 int chain_index = 0;
4101 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
4103 if (chain_index - extra_vehicles >= model_->vehicles()) {
4106 const int start = final_chains_[chain_index].head;
4107 const int end = final_chains_[chain_index].tail;
4109 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
4110 assignment_->SetValue(
4111 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
start);
4112 assignment_->Add(nexts_[
end]);
4113 assignment_->SetValue(nexts_[
end],
4114 model_->End(chain_index - extra_vehicles));
4118 for (
int route_index = 0; route_index < final_routes_.size();
4120 if (chain_index - extra_vehicles >= model_->vehicles()) {
4123 DCHECK_LT(route_index, final_routes_.size());
4124 const int head = final_routes_[route_index].front();
4125 const int tail = final_routes_[route_index].back();
4126 if (
head ==
tail && head < model_->Size()) {
4128 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
4129 assignment_->SetValue(
4130 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
4131 assignment_->Add(nexts_[
tail]);
4132 assignment_->SetValue(nexts_[
tail],
4133 model_->End(chain_index - extra_vehicles));
4141 if (!assignment_->Contains(
next)) {
4142 assignment_->Add(
next);
4151 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
4154 bool operator()(
const std::vector<int>& route1,
4155 const std::vector<int>& route2)
const {
4156 return (route1.size() < route2.size());
4167 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
4168 return (chain1.nodes < chain2.nodes);
4172 bool Head(
int node)
const {
4173 return (node == routes_[in_route_[node]].front());
4176 bool Tail(
int node)
const {
4177 return (node == routes_[in_route_[node]].back());
4180 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
4181 int dimension_index) {
4182 const RoutingDimension& dimension = *dimensions_[dimension_index];
4183 std::vector<int>::const_iterator it = route.begin();
4184 int64_t cumul = route_cumul;
4185 while (it != route.end()) {
4186 const int previous = *it;
4187 const int64_t cumul_previous = cumul;
4191 if (it == route.end()) {
4194 const int next = *it;
4195 int64_t available_from_previous =
4196 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
4197 int64_t available_cumul_next =
4200 const int64_t slack = available_cumul_next - available_from_previous;
4201 if (slack > dimension.SlackVar(previous)->Max()) {
4202 available_cumul_next =
4203 available_from_previous + dimension.SlackVar(previous)->Max();
4206 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
4209 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
4212 cumul = available_cumul_next;
4217 bool CheckRouteConnection(
const std::vector<int>& route1,
4218 const std::vector<int>& route2,
int dimension_index,
4220 const int tail1 = route1.back();
4221 const int head2 = route2.front();
4222 const int tail2 = route2.back();
4223 const RoutingDimension& dimension = *dimensions_[dimension_index];
4224 int non_depot_node = -1;
4225 for (
int node = 0; node < num_indices_; ++node) {
4226 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
4227 non_depot_node = node;
4231 CHECK_GE(non_depot_node, 0);
4232 const int64_t depot_threshold =
4233 std::max(dimension.SlackVar(non_depot_node)->Max(),
4234 dimension.CumulVar(non_depot_node)->Max());
4236 int64_t available_from_tail1 =
cumuls_[dimension_index][tail1] +
4237 dimension.GetTransitValue(tail1, head2, 0);
4238 int64_t new_available_cumul_head2 =
4241 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
4242 if (slack > dimension.SlackVar(tail1)->Max()) {
4243 new_available_cumul_head2 =
4244 available_from_tail1 + dimension.SlackVar(tail1)->Max();
4247 bool feasible_route =
true;
4248 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
4251 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
4256 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
4257 const int64_t new_possible_cumul_tail2 =
4258 new_possible_cumuls_[dimension_index].contains(tail2)
4259 ? new_possible_cumuls_[dimension_index][tail2]
4260 :
cumuls_[dimension_index][tail2];
4262 if (!feasible_route || (new_possible_cumul_tail2 +
4263 dimension.GetTransitValue(tail2,
end_depot, 0) >
4270 bool FeasibleMerge(
const std::vector<int>& route1,
4271 const std::vector<int>& route2,
int node1,
int node2,
4274 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
4279 if (!((index_to_vehicle_class_index_[node1] == -1 &&
4280 index_to_vehicle_class_index_[node2] == -1) ||
4282 index_to_vehicle_class_index_[node2] == -1) ||
4283 (index_to_vehicle_class_index_[node1] == -1 &&
4292 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4293 ++dimension_index) {
4294 new_possible_cumuls_[dimension_index].clear();
4295 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
4304 bool CheckTempAssignment(Assignment*
const temp_assignment,
4305 int new_chain_index,
int old_chain_index,
int head1,
4306 int tail1,
int head2,
int tail2) {
4309 if (new_chain_index >= model_->vehicles())
return false;
4310 const int start = head1;
4311 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
4312 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
4314 temp_assignment->Add(nexts_[tail1]);
4315 temp_assignment->SetValue(nexts_[tail1], head2);
4316 temp_assignment->Add(nexts_[tail2]);
4317 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
4318 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4319 if ((chain_index != new_chain_index) &&
4320 (chain_index != old_chain_index) &&
4321 (!deleted_chains_.contains(chain_index))) {
4322 const int start = chains_[chain_index].head;
4323 const int end = chains_[chain_index].tail;
4324 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
4325 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
4327 temp_assignment->Add(nexts_[
end]);
4328 temp_assignment->SetValue(nexts_[
end], model_->End(chain_index));
4331 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
4334 bool UpdateAssignment(
const std::vector<int>& route1,
4335 const std::vector<int>& route2) {
4336 bool feasible =
true;
4337 const int head1 = route1.front();
4338 const int tail1 = route1.back();
4339 const int head2 = route2.front();
4340 const int tail2 = route2.back();
4341 const int chain_index1 = index_to_chain_index_[head1];
4342 const int chain_index2 = index_to_chain_index_[head2];
4343 if (chain_index1 < 0 && chain_index2 < 0) {
4344 const int chain_index = chains_.size();
4345 if (check_assignment_) {
4346 Assignment*
const temp_assignment =
4347 solver_->MakeAssignment(assignment_);
4348 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
4349 tail1, head2, tail2);
4356 index_to_chain_index_[head1] = chain_index;
4357 index_to_chain_index_[tail2] = chain_index;
4358 chains_.push_back(chain);
4360 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
4361 if (check_assignment_) {
4362 Assignment*
const temp_assignment =
4363 solver_->MakeAssignment(assignment_);
4365 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4366 head1, tail1, head2, tail2);
4369 index_to_chain_index_[tail2] = chain_index1;
4370 chains_[chain_index1].head = head1;
4371 chains_[chain_index1].tail = tail2;
4372 ++chains_[chain_index1].nodes;
4374 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
4375 if (check_assignment_) {
4376 Assignment*
const temp_assignment =
4377 solver_->MakeAssignment(assignment_);
4379 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
4380 head1, tail1, head2, tail2);
4383 index_to_chain_index_[head1] = chain_index2;
4384 chains_[chain_index2].head = head1;
4385 chains_[chain_index2].tail = tail2;
4386 ++chains_[chain_index2].nodes;
4389 if (check_assignment_) {
4390 Assignment*
const temp_assignment =
4391 solver_->MakeAssignment(assignment_);
4393 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4394 head1, tail1, head2, tail2);
4397 index_to_chain_index_[tail2] = chain_index1;
4398 chains_[chain_index1].head = head1;
4399 chains_[chain_index1].tail = tail2;
4400 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
4401 deleted_chains_.insert(chain_index2);
4405 assignment_->Add(nexts_[tail1]);
4406 assignment_->SetValue(nexts_[tail1], head2);
4411 bool Merge(
bool merge,
int index1,
int index2) {
4413 if (UpdateAssignment(routes_[index1], routes_[index2])) {
4415 for (
const int node : routes_[index2]) {
4416 in_route_[node] = index1;
4417 routes_[index1].push_back(node);
4419 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4420 ++dimension_index) {
4421 for (
const std::pair<int, int64_t> new_possible_cumul :
4422 new_possible_cumuls_[dimension_index]) {
4423 cumuls_[dimension_index][new_possible_cumul.first] =
4424 new_possible_cumul.second;
4427 deleted_routes_.insert(index2);
4434 Assignment*
const assignment_;
4435 RoutingModel*
const model_;
4436 const bool check_assignment_;
4437 Solver*
const solver_;
4438 const int64_t num_indices_;
4439 const std::vector<Link> links_list_;
4440 std::vector<IntVar*> nexts_;
4441 std::vector<const RoutingDimension*> dimensions_;
4442 std::vector<std::vector<int64_t>>
cumuls_;
4443 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
4444 std::vector<std::vector<int>> routes_;
4445 std::vector<int> in_route_;
4446 absl::flat_hash_set<int> deleted_routes_;
4447 std::vector<std::vector<int>> final_routes_;
4448 std::vector<Chain> chains_;
4449 absl::flat_hash_set<int> deleted_chains_;
4450 std::vector<Chain> final_chains_;
4451 std::vector<int> index_to_chain_index_;
4452 std::vector<int> index_to_vehicle_class_index_;
4458 class SweepBuilder :
public DecisionBuilder {
4460 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
4461 : model_(
model), check_assignment_(check_assignment) {}
4462 ~SweepBuilder()
override {}
4464 Decision* Next(Solver*
const solver)
override {
4469 Assignment*
const assignment = solver->MakeAssignment();
4470 route_constructor_ = std::make_unique<RouteConstructor>(
4471 assignment, model_, check_assignment_, num_indices_, links_);
4473 route_constructor_->Construct();
4474 route_constructor_.reset(
nullptr);
4476 assignment->Restore();
4483 const int depot = model_->GetDepot();
4484 num_indices_ = model_->Size() + model_->vehicles();
4485 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4486 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4487 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4489 std::vector<int64_t> indices;
4490 model_->sweep_arranger()->ArrangeIndices(&indices);
4491 for (
int i = 0; i < indices.size() - 1; ++i) {
4492 const int64_t first = indices[i];
4493 const int64_t second = indices[i + 1];
4494 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4495 (model_->IsStart(second) || !model_->IsEnd(second))) {
4496 if (first != depot && second != depot) {
4497 Link
link(std::make_pair(first, second), 0, 0, depot, depot);
4498 links_.push_back(
link);
4504 RoutingModel*
const model_;
4505 std::unique_ptr<RouteConstructor> route_constructor_;
4506 const bool check_assignment_;
4507 int64_t num_indices_;
4508 std::vector<Link> links_;
4513 bool check_assignment) {
4514 return model->solver()->RevAlloc(
new SweepBuilder(
model, check_assignment));
4523 class AllUnperformed :
public DecisionBuilder {
4526 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
4527 ~AllUnperformed()
override {}
4528 Decision* Next(Solver*
const )
override {
4531 model_->CostVar()->FreezeQueue();
4532 for (
int i = 0; i < model_->Size(); ++i) {
4533 if (!model_->IsStart(i)) {
4534 model_->ActiveVar(i)->SetValue(0);
4537 model_->CostVar()->UnfreezeQueue();
4542 RoutingModel*
const model_;
4547 return model->solver()->RevAlloc(
new AllUnperformed(
model));
4552 class GuidedSlackFinalizer :
public DecisionBuilder {
4554 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel*
model,
4555 std::function<int64_t(int64_t)> initializer);
4556 Decision* Next(Solver* solver)
override;
4559 int64_t SelectValue(int64_t
index);
4560 int64_t ChooseVariable();
4562 const RoutingDimension*
const dimension_;
4563 RoutingModel*
const model_;
4564 const std::function<int64_t(int64_t)> initializer_;
4565 RevArray<bool> is_initialized_;
4566 std::vector<int64_t> initial_values_;
4567 Rev<int64_t> current_index_;
4568 Rev<int64_t> current_route_;
4569 RevArray<int64_t> last_delta_used_;
4574 GuidedSlackFinalizer::GuidedSlackFinalizer(
4575 const RoutingDimension* dimension, RoutingModel*
model,
4576 std::function<int64_t(int64_t)> initializer)
4577 : dimension_(ABSL_DIE_IF_NULL(dimension)),
4578 model_(ABSL_DIE_IF_NULL(
model)),
4579 initializer_(std::move(initializer)),
4580 is_initialized_(dimension->slacks().size(), false),
4581 initial_values_(dimension->slacks().size(),
4582 std::numeric_limits<int64_t>::
min()),
4583 current_index_(model_->Start(0)),
4585 last_delta_used_(dimension->slacks().size(), 0) {}
4587 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4588 CHECK_EQ(solver, model_->solver());
4589 const int node_idx = ChooseVariable();
4590 CHECK(node_idx == -1 ||
4591 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
4592 if (node_idx != -1) {
4593 if (!is_initialized_[node_idx]) {
4594 initial_values_[node_idx] = initializer_(node_idx);
4595 is_initialized_.SetValue(solver, node_idx,
true);
4597 const int64_t
value = SelectValue(node_idx);
4598 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
4599 return solver->MakeAssignVariableValue(slack_variable,
value);
4604 int64_t GuidedSlackFinalizer::SelectValue(int64_t
index) {
4605 const IntVar*
const slack_variable = dimension_->SlackVar(
index);
4606 const int64_t center = initial_values_[
index];
4607 const int64_t max_delta =
4608 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4614 while (std::abs(
delta) < max_delta &&
4615 !slack_variable->Contains(center +
delta)) {
4622 last_delta_used_.SetValue(model_->solver(),
index,
delta);
4623 return center +
delta;
4626 int64_t GuidedSlackFinalizer::ChooseVariable() {
4627 int64_t int_current_node = current_index_.Value();
4628 int64_t int_current_route = current_route_.Value();
4630 while (int_current_route < model_->vehicles()) {
4631 while (!model_->IsEnd(int_current_node) &&
4632 dimension_->SlackVar(int_current_node)->Bound()) {
4633 int_current_node = model_->NextVar(int_current_node)->Value();
4635 if (!model_->IsEnd(int_current_node)) {
4638 int_current_route += 1;
4639 if (int_current_route < model_->vehicles()) {
4640 int_current_node = model_->Start(int_current_route);
4644 CHECK(int_current_route == model_->vehicles() ||
4645 !dimension_->SlackVar(int_current_node)->Bound());
4646 current_index_.SetValue(model_->solver(), int_current_node);
4647 current_route_.SetValue(model_->solver(), int_current_route);
4648 if (int_current_route < model_->vehicles()) {
4649 return int_current_node;
4658 std::function<int64_t(int64_t)> initializer) {
4659 return solver_->RevAlloc(
4660 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
4663 int64_t RoutingDimension::ShortestTransitionSlack(int64_t node)
const {
4664 CHECK_EQ(base_dimension_,
this);
4665 CHECK(!model_->IsEnd(node));
4669 const int64_t
next = model_->NextVar(node)->Value();
4670 if (model_->IsEnd(
next)) {
4671 return SlackVar(node)->Min();
4673 const int64_t next_next = model_->NextVar(
next)->Value();
4674 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4675 CHECK_EQ(serving_vehicle, model_->VehicleVar(
next)->Value());
4677 model_->StateDependentTransitCallback(
4678 state_dependent_class_evaluators_
4679 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
4682 const int64_t next_cumul_min = CumulVar(
next)->Min();
4683 const int64_t next_cumul_max = CumulVar(
next)->Max();
4684 const int64_t optimal_next_cumul =
4686 next_cumul_min, next_cumul_max + 1);
4688 DCHECK_LE(next_cumul_min, optimal_next_cumul);
4689 DCHECK_LE(optimal_next_cumul, next_cumul_max);
4694 const int64_t current_cumul = CumulVar(node)->Value();
4695 const int64_t current_state_independent_transit = model_->TransitCallback(
4696 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
4697 const int64_t current_state_dependent_transit =
4699 ->StateDependentTransitCallback(
4700 state_dependent_class_evaluators_
4701 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4703 .transit->Query(current_cumul);
4704 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4705 current_state_independent_transit -
4706 current_state_dependent_transit;
4707 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4708 CHECK_LE(optimal_slack, SlackVar(node)->Max());
4709 return optimal_slack;
4715 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4721 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
4723 const std::vector<IntVar*> variables_;
4725 int64_t current_step_;
4732 int64_t current_direction_;
4737 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4738 : variables_(std::move(variables)),
4741 current_direction_(0) {}
4743 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
4745 static const int64_t sings[] = {1, -1};
4746 for (; 1 <= current_step_; current_step_ /= 2) {
4747 for (; current_direction_ < 2 * variables_.size();) {
4748 const int64_t variable_idx = current_direction_ / 2;
4749 IntVar*
const variable = variables_[variable_idx];
4750 const int64_t sign_index = current_direction_ % 2;
4751 const int64_t sign = sings[sign_index];
4752 const int64_t offset = sign * current_step_;
4753 const int64_t new_value = center_->
Value(variable) + offset;
4754 ++current_direction_;
4755 if (variable->Contains(new_value)) {
4756 delta->Add(variable);
4757 delta->SetValue(variable, new_value);
4761 current_direction_ = 0;
4766 void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
4767 CHECK(assignment !=
nullptr);
4768 current_step_ = FindMaxDistanceToDomain(assignment);
4769 center_ = assignment;
4772 int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
4773 const Assignment* assignment) {
4775 for (
const IntVar*
const var : variables_) {
4784 std::vector<IntVar*> variables) {
4785 return std::unique_ptr<LocalSearchOperator>(
4786 new GreedyDescentLSOperator(std::move(variables)));
4791 CHECK(dimension !=
nullptr);
4793 std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t
index) {
4799 solver_->MakeSolveOnce(guided_finalizer);
4800 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
4801 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
4802 start_cumuls[vehicle_idx] = dimension->
CumulVar(
Start(vehicle_idx));
4805 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
4807 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
4809 Assignment*
const first_solution = solver_->MakeAssignment();
4810 first_solution->
Add(start_cumuls);
4811 for (
IntVar*
const cumul : start_cumuls) {
4812 first_solution->
SetValue(cumul, cumul->Min());
4815 solver_->MakeLocalSearchPhase(first_solution,
parameters);
const std::vector< IntVar * > vars_
void NoteChangedPriority(T *val)
bool Contains(const T *val) const
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
const E & Element(const V *const var) const
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.
IntContainer * MutableIntVarContainer()
const IntContainer & IntVarContainer() const
void SetValue(const IntVar *const var, int64_t value)
int64_t Value(const IntVar *const var) const
IntVarElement * Add(IntVar *const var)
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, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
void AppendInsertionPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int vehicle, bool ignore_cost, std::vector< NodeInsertion > *node_insertions)
Helper method to the ComputeEvaluatorSortedPositions* methods.
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...
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, 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.
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_
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',...
std::pair< StartEndValue, int > Seed
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
ChristofidesFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
std::vector< NodeIndex > TravelingSalesmanPath()
void SetMatchingAlgorithm(MatchingAlgorithm matching)
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, 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< bool()> stop_search, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void PushInsertion(int64_t node, int64_t insert_after, int vehicle, int bucket, int64_t value)
void ClearInsertions(int64_t insert_after)
NodeEntryQueue(int num_nodes)
bool IsEmpty(int64_t insert_after) const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, 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.
void AppendPickupDeliveryMultitourInsertions(int pickup, const std::vector< int > &path, const std::vector< bool > &node_is_pickup, const std::vector< bool > &node_is_delivery, std::vector< PickupDeliveryInsertion > &insertions)
Generates insertions for a pickup and delivery pair in a multitour path:
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
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.
virtual void Initialize()
Initialize the heuristic; called before starting to build a new solution.
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
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 IntVarIterator * MakeDomainIterator(bool reversible) const =0
Creates a domain iterator.
virtual int64_t Value() const =0
This method returns the value of the variable.
virtual uint64_t Size() const =0
This method returns the number of values in the domain of the variable.
void Initialize() override
Initialize the heuristic; called before starting to build a new solution.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
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.
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
Filter-based heuristic dedicated to routing.
bool MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed, always returns true.
RoutingFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager, bool omit_secondary_vars=true)
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
RoutingModel * model() const
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
bool StopSearch() override
Returns true if the search must be stopped.
virtual void ResetVehicleIndices()
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
virtual void SetVehicleIndex(int64_t, int)
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.
const std::vector< int > & GetNeighborsOfNodeForCostClass(int cost_class, int node_index) const
Returns the neighbors of the given node for the given cost_class.
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
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
const std::vector< IntVar * > & VehicleVars() const
Returns all vehicle variables of the model, such that VehicleVars(i) is the vehicle variable of the n...
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
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 NodeNeighborsByCostClass * GetOrCreateNodeNeighborsByCostClass(int num_neighbors)
Returns num_neighbors neighbors of all nodes for every cost class.
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)
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
std::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
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
int NumberOfSetCallsWithDifferentArguments() const
void ArrangeIndices(std::vector< int64_t > *indices)
SweepArranger(const std::vector< std::pair< int64_t, int64_t >> &points)
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.
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...
std::pair< int, int > GetCompatibleVehicleOfType(int type, const std::function< bool(int)> &vehicle_is_compatible, const std::function< bool(int)> &stop_and_return_vehicle)
Searches for the best compatible vehicle of the given type, i.e.
const std::vector< IntVar * > cumuls_
static const int64_t kint64max
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
void STLClearObject(T *obj)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
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)
std::vector< int64_t > ComputeVehicleEndChainStarts(const RoutingModel &model)
Computes and returns the first node in the end chain of each vehicle in the model,...
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.
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
bool operator<(const Entry &other) const
int64_t insert_delivery_after
int64_t insert_pickup_after
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.
#define VLOG(verboselevel)