37 #include "absl/container/flat_hash_map.h" 38 #include "absl/container/flat_hash_set.h" 39 #include "absl/flags/flag.h" 40 #include "absl/memory/memory.h" 41 #include "absl/meta/type_traits.h" 42 #include "absl/strings/str_cat.h" 43 #include "absl/strings/string_view.h" 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, std::function<
bool(
int)> vehicle_is_compatible,
129 std::function<
bool(
int)> stop_and_return_vehicle) {
130 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
131 sorted_vehicle_classes_per_type_[type];
132 auto vehicle_class_it = sorted_classes.begin();
134 while (vehicle_class_it != sorted_classes.end()) {
136 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
137 DCHECK(!vehicles.empty());
139 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
141 const int vehicle = *vehicle_it;
142 if (vehicle_is_compatible(vehicle)) {
143 vehicles.erase(vehicle_it);
144 if (vehicles.empty()) {
145 sorted_classes.erase(vehicle_class_it);
147 return {vehicle, -1};
149 if (stop_and_return_vehicle(vehicle)) {
150 return {-1, vehicle};
169 bool has_pickup_deliveries,
bool has_node_precedences,
170 bool has_single_vehicle_node) {
171 if (has_pickup_deliveries || has_node_precedences) {
174 if (has_single_vehicle_node) {
185 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
186 : heuristic_(std::move(heuristic)) {}
189 Assignment*
const assignment = heuristic_->BuildSolution();
190 if (assignment !=
nullptr) {
191 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
192 VLOG(2) <<
"Number of rejected decisions: " 193 << heuristic_->number_of_rejects();
202 return heuristic_->number_of_decisions();
206 return heuristic_->number_of_rejects();
210 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
211 heuristic_->DebugString(),
")");
219 Solver* solver,
const std::vector<IntVar*>& vars,
221 : assignment_(solver->MakeAssignment()),
224 delta_(solver->MakeAssignment()),
225 is_in_delta_(
vars_.size(), false),
226 empty_(solver->MakeAssignment()),
227 filter_manager_(filter_manager),
228 number_of_decisions_(0),
229 number_of_rejects_(0) {
231 delta_indices_.reserve(vars_.size());
235 number_of_decisions_ = 0;
236 number_of_rejects_ = 0;
257 const std::function<int64_t(int64_t)>& next_accessor) {
262 start_chain_ends_.resize(
model()->vehicles());
263 end_chain_starts_.resize(
model()->vehicles());
265 for (
int v = 0; v < model_->
vehicles(); v++) {
266 int64_t node = model_->
Start(v);
267 while (!model_->
IsEnd(node)) {
268 const int64_t
next = next_accessor(node);
277 end_chain_starts_[v] =
model()->
End(v);
290 ++number_of_decisions_;
291 const bool accept = FilterAccept();
294 const int delta_size = delta_container.
Size();
297 for (
int i = 0; i < delta_size; ++i) {
306 ++number_of_rejects_;
309 for (
const int delta_index : delta_indices_) {
310 is_in_delta_[delta_index] =
false;
313 delta_indices_.clear();
321 bool IntVarFilteredHeuristic::FilterAccept() {
322 if (!filter_manager_)
return true;
324 return filter_manager_->
Accept(monitor, delta_, empty_,
336 bool RoutingFilteredHeuristic::InitializeSolution() {
346 start_chain_ends_.clear();
347 start_chain_ends_.resize(
model()->vehicles(), -1);
348 end_chain_starts_.clear();
349 end_chain_starts_.resize(
model()->vehicles(), -1);
352 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
354 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
360 start_chain_ends_[vehicle] = node;
363 std::vector<int64_t> starts(
Size() +
model()->vehicles(), -1);
364 std::vector<int64_t> ends(
Size() +
model()->vehicles(), -1);
370 std::vector<bool> touched(
Size(),
false);
371 for (
int node = 0; node <
Size(); ++node) {
373 while (!
model()->
IsEnd(current) && !touched[current]) {
374 touched[current] =
true;
375 IntVar*
const next_var =
Var(current);
376 if (next_var->Bound()) {
377 current = next_var->Value();
382 starts[ends[current]] = starts[node];
383 ends[starts[node]] = ends[current];
388 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
389 end_chain_starts_[vehicle] = starts[
model()->
End(vehicle)];
390 int64_t node = start_chain_ends_[vehicle];
391 if (!
model()->IsEnd(node)) {
396 while (!
model()->IsEnd(node)) {
414 node, 1, [
this, node](
int alternate) {
415 if (node != alternate && !
Contains(alternate)) {
430 std::vector<bool> to_make_unperformed(
Size(),
false);
431 for (
const auto& [pickups, deliveries] :
432 model()->GetPickupAndDeliveryPairs()) {
433 int64_t performed_pickup = -1;
434 for (int64_t pickup : pickups) {
436 performed_pickup = pickup;
440 int64_t performed_delivery = -1;
441 for (int64_t delivery : deliveries) {
443 performed_delivery = delivery;
447 if ((performed_pickup == -1) != (performed_delivery == -1)) {
448 if (performed_pickup != -1) {
449 to_make_unperformed[performed_pickup] =
true;
451 if (performed_delivery != -1) {
452 to_make_unperformed[performed_delivery] =
true;
460 const int64_t next_of_next =
Value(
next);
472 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
473 std::function<int64_t(int64_t)> penalty_evaluator,
477 penalty_evaluator_(std::move(penalty_evaluator)) {}
479 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
481 const std::vector<int>& vehicles) {
482 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
485 for (
int node = 0; node <
model()->
Size(); node++) {
487 std::vector<StartEndValue>& start_end_distances =
488 start_end_distances_per_node[node];
490 for (
const int vehicle : vehicles) {
491 const int64_t start =
model()->
Start(vehicle);
492 const int64_t end =
model()->
End(vehicle);
496 CapAdd(
model()->GetArcCostForVehicle(start, node, vehicle),
497 model()->GetArcCostForVehicle(node, end, vehicle));
498 start_end_distances.push_back({
distance, vehicle});
502 std::sort(start_end_distances.begin(), start_end_distances.end(),
504 return second < first;
507 return start_end_distances_per_node;
510 template <
class Queue>
512 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
513 Queue* priority_queue) {
515 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
517 for (
int node = 0; node < num_nodes; node++) {
519 std::vector<StartEndValue>& start_end_distances =
520 (*start_end_distances_per_node)[node];
521 if (start_end_distances.empty()) {
525 const StartEndValue& start_end_value = start_end_distances.back();
526 priority_queue->push(std::make_pair(start_end_value, node));
527 start_end_distances.pop_back();
540 int64_t node_to_insert, int64_t start, int64_t next_after_start,
541 int64_t vehicle, std::vector<ValuedPosition>* valued_positions) {
542 CHECK(valued_positions !=
nullptr);
543 int64_t insert_after = start;
544 while (!
model()->IsEnd(insert_after)) {
545 const int64_t insert_before =
546 (insert_after == start) ? next_after_start :
Value(insert_after);
547 valued_positions->push_back(std::make_pair(
549 insert_before, vehicle),
551 insert_after = insert_before;
556 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
559 evaluator_(node_to_insert, insert_before, vehicle)),
560 evaluator_(insert_after, insert_before, vehicle));
564 int64_t node_to_insert)
const {
573 void SortAndExtractPairSeconds(std::vector<std::pair<int64_t, T>>* pairs,
574 std::vector<T>* sorted_seconds) {
575 CHECK(pairs !=
nullptr);
576 CHECK(sorted_seconds !=
nullptr);
577 std::sort(pairs->begin(), pairs->end());
578 sorted_seconds->reserve(pairs->size());
579 for (
const std::pair<int64_t, T>& p : *pairs) {
580 sorted_seconds->push_back(p.second);
593 value_(std::numeric_limits<int64_t>::
max()),
604 if (value_ != other.value_) {
605 return value_ > other.value_;
607 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
608 return vehicle_ == -1;
610 return std::tie(pickup_insert_after_, pickup_to_insert_,
611 delivery_insert_after_, delivery_to_insert_, vehicle_) >
612 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
613 other.delivery_insert_after_, other.delivery_to_insert_,
618 int64_t
value()
const {
return value_; }
633 const int pickup_to_insert_;
634 int pickup_insert_after_;
635 const int delivery_to_insert_;
636 const int delivery_insert_after_;
645 value_(std::numeric_limits<int64_t>::
max()),
651 if (value_ != other.value_) {
652 return value_ > other.value_;
654 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
655 return vehicle_ == -1;
657 return std::tie(insert_after_, node_to_insert_, vehicle_) >
658 std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
662 int64_t
value()
const {
return value_; }
673 const int node_to_insert_;
683 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
684 std::function<int64_t(int64_t)> penalty_evaluator,
688 std::move(penalty_evaluator),
691 node_index_to_vehicle_(
model->Size(), -1),
692 empty_vehicle_type_curator_(nullptr) {
697 if (NumNeighbors() >= NumNonStartEndNodes() - 1) {
706 std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
710 void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
712 !node_index_to_neighbors_by_cost_class_.empty()) {
718 const int64_t num_neighbors = NumNeighbors();
721 DCHECK_LT(num_neighbors, NumNonStartEndNodes() - 1);
723 const RoutingModel& routing_model = *
model();
724 const int64_t size = routing_model.Size();
725 node_index_to_neighbors_by_cost_class_.resize(size);
726 const int num_cost_classes = routing_model.GetCostClassesCount();
727 for (int64_t node_index = 0; node_index < size; node_index++) {
728 node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
729 for (
int cc = 0; cc < num_cost_classes; cc++) {
730 node_index_to_neighbors_by_cost_class_[node_index][cc] =
731 absl::make_unique<SparseBitset<int64_t>>(size);
735 for (int64_t node_index = 0; node_index < size; ++node_index) {
736 DCHECK(!routing_model.IsEnd(node_index));
737 if (routing_model.IsStart(node_index)) {
744 for (
int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
745 if (!routing_model.HasVehicleWithCostClassIndex(
746 RoutingCostClassIndex(cost_class))) {
750 std::vector<std::pair< int64_t, int64_t>>
752 costed_after_nodes.reserve(size);
753 for (
int after_node = 0; after_node < size; ++after_node) {
754 if (after_node != node_index && !routing_model.IsStart(after_node)) {
755 costed_after_nodes.push_back(
756 std::make_pair(routing_model.GetArcCostForClass(
757 node_index, after_node, cost_class),
761 std::nth_element(costed_after_nodes.begin(),
762 costed_after_nodes.begin() + num_neighbors - 1,
763 costed_after_nodes.end());
764 costed_after_nodes.resize(num_neighbors);
766 for (
auto [
cost, neighbor] : costed_after_nodes) {
767 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
771 DCHECK(!routing_model.IsEnd(neighbor) &&
772 !routing_model.IsStart(neighbor));
773 node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
777 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
778 const int64_t vehicle_start = routing_model.Start(vehicle);
779 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
781 node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
788 bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
789 int cost_class, int64_t node_index, int64_t neighbor_index)
const {
791 (*node_index_to_neighbors_by_cost_class_[node_index]
792 [cost_class])[neighbor_index];
795 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
796 std::vector<bool> node_is_visited(
model()->
Size(), -1);
799 node =
Value(node)) {
800 if (node_index_to_vehicle_[node] != v) {
803 node_is_visited[node] =
true;
807 for (
int node = 0; node <
model()->
Size(); node++) {
808 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
817 ComputeNeighborhoods();
818 if (empty_vehicle_type_curator_ ==
nullptr) {
819 empty_vehicle_type_curator_ = absl::make_unique<VehicleTypeCurator>(
820 model()->GetVehicleTypeContainer());
823 empty_vehicle_type_curator_->Reset(
828 std::vector<int> pairs_to_insert;
829 absl::flat_hash_map<int, std::vector<int>> vehicle_to_pair_nodes;
832 int pickup_vehicle = -1;
833 for (int64_t pickup : index_pair.first) {
835 pickup_vehicle = node_index_to_vehicle_[pickup];
839 int delivery_vehicle = -1;
840 for (int64_t delivery : index_pair.second) {
842 delivery_vehicle = node_index_to_vehicle_[delivery];
846 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
847 pairs_to_insert.push_back(
index);
849 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
850 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle];
851 for (int64_t delivery : index_pair.second) {
852 pair_nodes.push_back(delivery);
855 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
856 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle];
857 for (int64_t pickup : index_pair.first) {
858 pair_nodes.push_back(pickup);
862 for (
const auto& vehicle_and_nodes : vehicle_to_pair_nodes) {
863 InsertNodesOnRoutes(vehicle_and_nodes.second, {vehicle_and_nodes.first});
866 InsertPairsAndNodesByRequirementTopologicalOrder();
870 InsertPairs(pairs_to_insert);
871 std::vector<int>
nodes;
872 for (
int node = 0; node <
model()->
Size(); ++node) {
875 nodes.push_back(node);
878 InsertFarthestNodesAsSeeds();
880 SequentialInsertNodes(
nodes);
882 InsertNodesOnRoutes(
nodes, {});
885 DCHECK(CheckVehicleIndices());
889 void GlobalCheapestInsertionFilteredHeuristic::
890 InsertPairsAndNodesByRequirementTopologicalOrder() {
891 for (
const std::vector<int>& types :
892 model()->GetTopologicallySortedVisitTypes()) {
893 for (
int type : types) {
894 InsertPairs(
model()->GetPairIndicesOfType(type));
895 InsertNodesOnRoutes(
model()->GetSingleNodesOfType(type), {});
900 void GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
901 const std::vector<int>& pair_indices) {
903 std::vector<PairEntries> pickup_to_entries;
904 std::vector<PairEntries> delivery_to_entries;
905 InitializePairPositions(pair_indices, &priority_queue, &pickup_to_entries,
906 &delivery_to_entries);
907 while (!priority_queue.
IsEmpty()) {
909 for (PairEntry*
const entry : *priority_queue.
Raw()) {
914 PairEntry*
const entry = priority_queue.
Top();
916 const int64_t delivery = entry->delivery_to_insert();
918 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
919 &delivery_to_entries);
923 const int entry_vehicle = entry->vehicle();
924 if (entry_vehicle == -1) {
929 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
930 &delivery_to_entries);
936 if (InsertPairEntryUsingEmptyVehicleTypeCurator(
937 pair_indices, entry, &priority_queue, &pickup_to_entries,
938 &delivery_to_entries)) {
944 const int64_t pickup_insert_after = entry->pickup_insert_after();
945 const int64_t pickup_insert_before =
Value(pickup_insert_after);
946 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
948 const int64_t delivery_insert_after = entry->delivery_insert_after();
949 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
950 ? pickup_insert_before
951 :
Value(delivery_insert_after);
952 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
954 UpdateAfterPairInsertion(pair_indices, entry_vehicle, pickup,
955 pickup_insert_after, delivery,
956 delivery_insert_after, &priority_queue,
957 &pickup_to_entries, &delivery_to_entries);
959 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
960 &delivery_to_entries);
965 bool GlobalCheapestInsertionFilteredHeuristic::
966 InsertPairEntryUsingEmptyVehicleTypeCurator(
967 const std::vector<int>& pair_indices,
968 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
970 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
972 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
974 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
975 delivery_to_entries) {
976 const int entry_vehicle = pair_entry->vehicle();
985 const int64_t pickup = pair_entry->pickup_to_insert();
986 const int64_t delivery = pair_entry->delivery_to_insert();
987 const int64_t entry_fixed_cost =
989 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
990 delivery](
int vehicle) {
996 const int64_t end =
model()->
End(vehicle);
1005 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1008 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1009 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1010 empty_vehicle_type_curator_->Type(entry_vehicle),
1011 vehicle_is_compatible, stop_and_return_vehicle);
1012 if (compatible_vehicle >= 0) {
1014 const int64_t vehicle_start =
model()->
Start(compatible_vehicle);
1015 const int num_previous_vehicle_entries =
1016 pickup_to_entries->at(vehicle_start).size() +
1017 delivery_to_entries->at(vehicle_start).size();
1018 UpdateAfterPairInsertion(pair_indices, compatible_vehicle, pickup,
1019 vehicle_start, delivery, pickup, priority_queue,
1020 pickup_to_entries, delivery_to_entries);
1021 if (compatible_vehicle != entry_vehicle) {
1027 DCHECK_EQ(num_previous_vehicle_entries, 0);
1033 const int new_empty_vehicle =
1034 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1035 empty_vehicle_type_curator_->Type(compatible_vehicle));
1037 if (new_empty_vehicle >= 0) {
1041 UpdatePairPositions(pair_indices, new_empty_vehicle,
1042 model()->Start(new_empty_vehicle), priority_queue,
1043 pickup_to_entries, delivery_to_entries);
1045 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1052 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1053 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1054 pair_entry->set_pickup_insert_after(
1055 model()->Start(next_fixed_cost_empty_vehicle));
1056 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1057 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1058 UpdatePairEntry(pair_entry, priority_queue);
1060 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1061 delivery_to_entries);
1067 void GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1068 const std::vector<int>&
nodes,
const absl::flat_hash_set<int>& vehicles) {
1070 std::vector<NodeEntries> position_to_node_entries;
1071 InitializePositions(
nodes, vehicles, &priority_queue,
1072 &position_to_node_entries);
1078 const bool all_vehicles =
1081 while (!priority_queue.
IsEmpty()) {
1082 NodeEntry*
const node_entry = priority_queue.
Top();
1084 for (NodeEntry*
const entry : *priority_queue.
Raw()) {
1091 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1095 const int entry_vehicle = node_entry->vehicle();
1096 if (entry_vehicle == -1) {
1099 SetValue(node_to_insert, node_to_insert);
1101 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1107 if (InsertNodeEntryUsingEmptyVehicleTypeCurator(
1108 nodes, all_vehicles, node_entry, &priority_queue,
1109 &position_to_node_entries)) {
1114 const int64_t insert_after = node_entry->insert_after();
1117 UpdatePositions(
nodes, entry_vehicle, node_to_insert, all_vehicles,
1118 &priority_queue, &position_to_node_entries);
1119 UpdatePositions(
nodes, entry_vehicle, insert_after, all_vehicles,
1120 &priority_queue, &position_to_node_entries);
1121 SetVehicleIndex(node_to_insert, entry_vehicle);
1123 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
1128 bool GlobalCheapestInsertionFilteredHeuristic::
1129 InsertNodeEntryUsingEmptyVehicleTypeCurator(
1130 const std::vector<int>&
nodes,
bool all_vehicles,
1131 GlobalCheapestInsertionFilteredHeuristic::NodeEntry*
const node_entry,
1133 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1135 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1136 position_to_node_entries) {
1137 const int entry_vehicle = node_entry->vehicle();
1138 if (entry_vehicle == -1 || !all_vehicles || !
VehicleIsEmpty(entry_vehicle)) {
1147 const int64_t node_to_insert = node_entry->node_to_insert();
1148 const int64_t entry_fixed_cost =
1150 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1151 node_to_insert](
int vehicle) {
1158 model()->End(vehicle));
1165 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1168 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1169 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1170 empty_vehicle_type_curator_->Type(entry_vehicle),
1171 vehicle_is_compatible, stop_and_return_vehicle);
1172 if (compatible_vehicle >= 0) {
1174 UpdatePositions(
nodes, compatible_vehicle, node_to_insert, all_vehicles,
1175 priority_queue, position_to_node_entries);
1176 const int64_t compatible_start =
model()->
Start(compatible_vehicle);
1177 const bool no_prior_entries_for_this_vehicle =
1178 position_to_node_entries->at(compatible_start).empty();
1179 UpdatePositions(
nodes, compatible_vehicle, compatible_start, all_vehicles,
1180 priority_queue, position_to_node_entries);
1181 SetVehicleIndex(node_to_insert, compatible_vehicle);
1182 if (compatible_vehicle != entry_vehicle) {
1188 DCHECK(no_prior_entries_for_this_vehicle);
1194 const int new_empty_vehicle =
1195 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1196 empty_vehicle_type_curator_->Type(compatible_vehicle));
1198 if (new_empty_vehicle >= 0) {
1202 UpdatePositions(
nodes, new_empty_vehicle,
1203 model()->Start(new_empty_vehicle), all_vehicles,
1204 priority_queue, position_to_node_entries);
1206 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1213 position_to_node_entries->at(node_entry->insert_after()).erase(node_entry);
1214 node_entry->set_insert_after(
model()->Start(next_fixed_cost_empty_vehicle));
1215 position_to_node_entries->at(node_entry->insert_after()).insert(node_entry);
1216 node_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1217 UpdateNodeEntry(node_entry, priority_queue);
1219 DeleteNodeEntry(node_entry, priority_queue, position_to_node_entries);
1225 void GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1226 const std::vector<int>&
nodes) {
1227 std::vector<bool> is_vehicle_used;
1228 absl::flat_hash_set<int> used_vehicles;
1229 std::vector<int> unused_vehicles;
1231 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1232 if (!used_vehicles.empty()) {
1233 InsertNodesOnRoutes(
nodes, used_vehicles);
1236 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1238 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
1242 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1245 while (vehicle >= 0) {
1246 InsertNodesOnRoutes(
nodes, {vehicle});
1247 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1252 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1253 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1254 absl::flat_hash_set<int>* used_vehicles) {
1255 is_vehicle_used->clear();
1256 is_vehicle_used->resize(
model()->vehicles());
1258 used_vehicles->clear();
1259 used_vehicles->reserve(
model()->vehicles());
1261 unused_vehicles->clear();
1262 unused_vehicles->reserve(
model()->vehicles());
1264 for (
int vehicle = 0; vehicle <
model()->
vehicles(); vehicle++) {
1266 (*is_vehicle_used)[vehicle] =
true;
1267 used_vehicles->insert(vehicle);
1269 (*is_vehicle_used)[vehicle] =
false;
1270 unused_vehicles->push_back(vehicle);
1275 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1278 const int num_seeds = static_cast<int>(
1281 std::vector<bool> is_vehicle_used;
1282 absl::flat_hash_set<int> used_vehicles;
1283 std::vector<int> unused_vehicles;
1284 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1285 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1290 std::priority_queue<Seed> farthest_node_queue;
1293 int inserted_seeds = 0;
1294 while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) {
1295 InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1304 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1305 empty_vehicle_type_curator_->Update(
1309 template <
class Queue>
1310 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1311 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1312 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
1313 while (!priority_queue->empty()) {
1315 const Seed& seed = priority_queue->top();
1317 const int seed_node = seed.second;
1318 const int seed_vehicle = seed.first.vehicle;
1320 std::vector<StartEndValue>& other_start_end_values =
1321 (*start_end_distances_per_node)[seed_node];
1326 priority_queue->pop();
1327 other_start_end_values.clear();
1330 if (!(*is_vehicle_used)[seed_vehicle]) {
1332 const int64_t start =
model()->
Start(seed_vehicle);
1333 const int64_t end =
model()->
End(seed_vehicle);
1337 priority_queue->pop();
1338 (*is_vehicle_used)[seed_vehicle] =
true;
1339 other_start_end_values.clear();
1340 SetVehicleIndex(seed_node, seed_vehicle);
1341 return seed_vehicle;
1348 priority_queue->pop();
1349 if (!other_start_end_values.empty()) {
1350 const StartEndValue& next_seed_value = other_start_end_values.back();
1351 priority_queue->push(std::make_pair(next_seed_value, seed_node));
1352 other_start_end_values.pop_back();
1359 void GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1360 const std::vector<int>& pair_indices,
1362 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1363 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1365 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1366 delivery_to_entries) {
1367 priority_queue->Clear();
1368 pickup_to_entries->clear();
1369 pickup_to_entries->resize(
model()->
Size());
1370 delivery_to_entries->clear();
1371 delivery_to_entries->resize(
model()->
Size());
1374 for (
int index : pair_indices) {
1376 for (int64_t pickup : index_pair.first) {
1378 for (int64_t delivery : index_pair.second) {
1385 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
1390 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1394 InitializeInsertionEntriesPerformingPair(
1395 pickup, delivery, priority_queue, pickup_to_entries,
1396 delivery_to_entries);
1402 void GlobalCheapestInsertionFilteredHeuristic::
1403 InitializeInsertionEntriesPerformingPair(
1404 int64_t pickup, int64_t delivery,
1406 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1408 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1410 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1411 delivery_to_entries) {
1413 std::vector<std::pair<std::pair<int64_t, int>, std::pair<int64_t, int64_t>>>
1415 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
1417 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1418 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1423 const int64_t start =
model()->
Start(vehicle);
1424 std::vector<ValuedPosition> valued_pickup_positions;
1426 &valued_pickup_positions);
1428 valued_pickup_positions) {
1429 const int64_t pickup_position = valued_pickup_position.second;
1431 std::vector<ValuedPosition> valued_delivery_positions;
1433 vehicle, &valued_delivery_positions);
1435 valued_delivery_positions) {
1436 valued_positions.push_back(std::make_pair(
1437 std::make_pair(
CapAdd(valued_pickup_position.first,
1438 valued_delivery_position.first),
1440 std::make_pair(pickup_position,
1441 valued_delivery_position.second)));
1445 for (
const auto& [cost_for_vehicle, pair_positions] : valued_positions) {
1446 DCHECK_NE(pair_positions.first, pair_positions.second);
1447 AddPairEntry(pickup, pair_positions.first, delivery,
1448 pair_positions.second, cost_for_vehicle.second,
1449 priority_queue, pickup_to_entries, delivery_to_entries);
1458 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1459 existing_insertion_positions;
1461 for (
const int64_t pickup_insert_after :
1462 GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
1463 if (!
Contains(pickup_insert_after)) {
1466 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1473 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1474 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1480 int64_t delivery_insert_after = pickup;
1481 while (!
model()->
IsEnd(delivery_insert_after)) {
1482 const std::pair<int64_t, int64_t> insertion_position = {
1483 pickup_insert_after, delivery_insert_after};
1485 insertion_position));
1486 existing_insertion_positions.insert(insertion_position);
1488 AddPairEntry(pickup, pickup_insert_after, delivery,
1489 delivery_insert_after, vehicle, priority_queue,
1490 pickup_to_entries, delivery_to_entries);
1491 delivery_insert_after = (delivery_insert_after == pickup)
1492 ?
Value(pickup_insert_after)
1493 :
Value(delivery_insert_after);
1498 for (
const int64_t delivery_insert_after :
1499 GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
1500 if (!
Contains(delivery_insert_after)) {
1503 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1505 model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
1514 int64_t pickup_insert_after =
model()->
Start(vehicle);
1515 while (pickup_insert_after != delivery_insert_after) {
1517 existing_insertion_positions,
1518 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1519 AddPairEntry(pickup, pickup_insert_after, delivery,
1520 delivery_insert_after, vehicle, priority_queue,
1521 pickup_to_entries, delivery_to_entries);
1523 pickup_insert_after =
Value(pickup_insert_after);
1529 void GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1530 const std::vector<int>& pair_indices,
int vehicle, int64_t pickup,
1531 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1533 std::vector<PairEntries>* pickup_to_entries,
1534 std::vector<PairEntries>* delivery_to_entries) {
1535 UpdatePairPositions(pair_indices, vehicle, pickup_position, priority_queue,
1536 pickup_to_entries, delivery_to_entries);
1537 UpdatePairPositions(pair_indices, vehicle, pickup, priority_queue,
1538 pickup_to_entries, delivery_to_entries);
1539 UpdatePairPositions(pair_indices, vehicle, delivery, priority_queue,
1540 pickup_to_entries, delivery_to_entries);
1541 if (delivery_position != pickup) {
1542 UpdatePairPositions(pair_indices, vehicle, delivery_position,
1543 priority_queue, pickup_to_entries, delivery_to_entries);
1545 SetVehicleIndex(pickup, vehicle);
1546 SetVehicleIndex(delivery, vehicle);
1549 void GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
1550 const std::vector<int>& pair_indices,
int vehicle,
1551 int64_t pickup_insert_after,
1553 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1554 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1556 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1557 delivery_to_entries) {
1560 using Pair = std::pair<int64_t, int64_t>;
1561 using Insertion = std::pair<Pair, int64_t>;
1562 absl::flat_hash_set<Insertion> existing_insertions;
1563 std::vector<PairEntry*> to_remove;
1564 for (PairEntry*
const pair_entry :
1565 pickup_to_entries->at(pickup_insert_after)) {
1567 DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
1568 if (
Contains(pair_entry->pickup_to_insert()) ||
1569 Contains(pair_entry->delivery_to_insert())) {
1570 to_remove.push_back(pair_entry);
1572 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1573 .contains(pair_entry));
1574 UpdatePairEntry(pair_entry, priority_queue);
1575 existing_insertions.insert(
1576 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1577 pair_entry->delivery_insert_after()});
1580 for (PairEntry*
const pair_entry : to_remove) {
1581 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1582 delivery_to_entries);
1587 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1590 for (
int pair_index : pair_indices) {
1592 pickup_delivery_pairs[pair_index];
1593 for (int64_t pickup : index_pair.first) {
1595 !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
1598 for (int64_t delivery : index_pair.second) {
1602 int64_t delivery_insert_after = pickup;
1603 while (!
model()->IsEnd(delivery_insert_after)) {
1604 const Insertion insertion = {{pickup, delivery},
1605 delivery_insert_after};
1607 AddPairEntry(pickup, pickup_insert_after, delivery,
1608 delivery_insert_after, vehicle, priority_queue,
1609 pickup_to_entries, delivery_to_entries);
1611 if (delivery_insert_after == pickup) {
1612 delivery_insert_after = pickup_insert_before;
1614 delivery_insert_after =
Value(delivery_insert_after);
1622 void GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
1623 const std::vector<int>& pair_indices,
int vehicle,
1624 int64_t delivery_insert_after,
1626 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1627 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1629 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1630 delivery_to_entries) {
1633 using Pair = std::pair<int64_t, int64_t>;
1634 using Insertion = std::pair<Pair, int64_t>;
1635 absl::flat_hash_set<Insertion> existing_insertions;
1636 std::vector<PairEntry*> to_remove;
1637 for (PairEntry*
const pair_entry :
1638 delivery_to_entries->at(delivery_insert_after)) {
1640 DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
1641 if (
Contains(pair_entry->pickup_to_insert()) ||
1642 Contains(pair_entry->delivery_to_insert())) {
1643 to_remove.push_back(pair_entry);
1645 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1646 .contains(pair_entry));
1647 existing_insertions.insert(
1648 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
1649 pair_entry->pickup_insert_after()});
1650 UpdatePairEntry(pair_entry, priority_queue);
1653 for (PairEntry*
const pair_entry : to_remove) {
1654 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1655 delivery_to_entries);
1662 for (
int pair_index : pair_indices) {
1664 pickup_delivery_pairs[pair_index];
1665 for (int64_t delivery : index_pair.second) {
1667 !IsNeighborForCostClass(cost_class, delivery_insert_after,
1671 for (int64_t pickup : index_pair.first) {
1675 int64_t pickup_insert_after =
model()->
Start(vehicle);
1676 while (pickup_insert_after != delivery_insert_after) {
1677 const Insertion insertion = {{pickup, delivery}, pickup_insert_after};
1679 AddPairEntry(pickup, pickup_insert_after, delivery,
1680 delivery_insert_after, vehicle, priority_queue,
1681 pickup_to_entries, delivery_to_entries);
1683 pickup_insert_after =
Value(pickup_insert_after);
1690 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1691 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1693 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1694 std::vector<PairEntries>* pickup_to_entries,
1695 std::vector<PairEntries>* delivery_to_entries) {
1696 priority_queue->
Remove(entry);
1697 if (entry->pickup_insert_after() != -1) {
1698 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1700 if (entry->delivery_insert_after() != -1) {
1701 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1706 void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1707 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1708 int64_t delivery_insert_after,
int vehicle,
1710 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1711 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1713 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1714 delivery_entries)
const {
1715 if (pickup_insert_after == -1) {
1718 PairEntry* pair_entry =
new PairEntry(pickup, -1, delivery, -1, -1);
1719 pair_entry->set_value(
1720 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1724 priority_queue->
Add(pair_entry);
1728 PairEntry*
const pair_entry =
new PairEntry(
1729 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle);
1730 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1731 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1735 pickup_entries->at(pickup_insert_after).insert(pair_entry);
1736 delivery_entries->at(delivery_insert_after).insert(pair_entry);
1737 priority_queue->
Add(pair_entry);
1740 void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1741 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1743 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1745 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1746 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1747 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1748 pair_entry->vehicle()));
1756 GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1757 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1758 int64_t delivery_insert_after,
int vehicle)
const {
1760 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1762 pickup, pickup_insert_after, pickup_insert_before, vehicle);
1765 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1766 ? pickup_insert_before
1767 :
Value(delivery_insert_after);
1769 delivery, delivery_insert_after, delivery_insert_before, vehicle);
1771 const int64_t penalty_shift =
1772 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1775 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
1778 void GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1779 const std::vector<int>&
nodes,
const absl::flat_hash_set<int>& vehicles,
1781 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1782 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1783 position_to_node_entries) {
1784 priority_queue->
Clear();
1785 position_to_node_entries->clear();
1786 position_to_node_entries->resize(
model()->
Size());
1788 const int num_vehicles =
1790 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
1792 for (
int node :
nodes) {
1799 AddNodeEntry(node, -1, -1, all_vehicles, priority_queue,
nullptr);
1802 InitializeInsertionEntriesPerformingNode(node, vehicles, priority_queue,
1803 position_to_node_entries);
1807 void GlobalCheapestInsertionFilteredHeuristic::
1808 InitializeInsertionEntriesPerformingNode(
1809 int64_t node,
const absl::flat_hash_set<int>& vehicles,
1811 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
1813 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1814 position_to_node_entries) {
1815 const int num_vehicles =
1817 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
1820 auto vehicles_it = vehicles.begin();
1821 for (
int v = 0; v < num_vehicles; v++) {
1822 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
1824 const int64_t start =
model()->
Start(vehicle);
1826 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1827 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1832 std::vector<ValuedPosition> valued_positions;
1835 for (
const std::pair<int64_t, int64_t>& valued_position :
1837 AddNodeEntry(node, valued_position.second, vehicle, all_vehicles,
1838 priority_queue, position_to_node_entries);
1846 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
1847 int v,
int cost_class) {
1849 (all_vehicles || vehicles.contains(v));
1853 for (
const int64_t insert_after :
1854 GetNeighborsOfNodeForCostClass(cost_class, node)) {
1858 const int vehicle = node_index_to_vehicle_[insert_after];
1859 if (vehicle == -1 ||
1860 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
1864 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1865 empty_vehicle_type_curator_->Type(vehicle)) != vehicle) {
1870 AddNodeEntry(node, insert_after, vehicle, all_vehicles, priority_queue,
1871 position_to_node_entries);
1876 void GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
1877 const std::vector<int>&
nodes,
int vehicle, int64_t insert_after,
1880 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1881 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
1885 std::vector<NodeEntry*> to_remove;
1886 absl::flat_hash_set<int> existing_insertions;
1887 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
1888 DCHECK_EQ(node_entry->insert_after(), insert_after);
1889 const int64_t node_to_insert = node_entry->node_to_insert();
1891 to_remove.push_back(node_entry);
1893 UpdateNodeEntry(node_entry, priority_queue);
1894 existing_insertions.insert(node_to_insert);
1897 for (NodeEntry*
const node_entry : to_remove) {
1898 DeleteNodeEntry(node_entry, priority_queue, node_entries);
1901 for (
int node_to_insert :
nodes) {
1903 !existing_insertions.contains(node_to_insert) &&
1904 IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
1905 AddNodeEntry(node_to_insert, insert_after, vehicle, all_vehicles,
1906 priority_queue, node_entries);
1911 void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
1912 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
1914 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
1915 std::vector<NodeEntries>* node_entries) {
1916 priority_queue->
Remove(entry);
1917 if (entry->insert_after() != -1) {
1918 node_entries->at(entry->insert_after()).erase(entry);
1923 void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
1924 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
1926 std::vector<NodeEntries>* node_entries)
const {
1928 const int64_t penalty_shift =
1929 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1932 if (insert_after == -1) {
1934 if (!all_vehicles) {
1940 NodeEntry*
const node_entry =
new NodeEntry(node, -1, -1);
1941 node_entry->set_value(
CapSub(node_penalty, penalty_shift));
1942 priority_queue->
Add(node_entry);
1947 node, insert_after,
Value(insert_after), vehicle);
1948 if (!all_vehicles && insertion_cost > node_penalty) {
1955 NodeEntry*
const node_entry =
new NodeEntry(node, insert_after, vehicle);
1956 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
1959 node_entries->at(insert_after).insert(node_entry);
1960 priority_queue->
Add(node_entry);
1963 void GlobalCheapestInsertionFilteredHeuristic::UpdateNodeEntry(
1964 NodeEntry*
const node_entry,
1966 const int64_t node = node_entry->node_to_insert();
1967 const int64_t insert_after = node_entry->insert_after();
1970 node, insert_after,
Value(insert_after), node_entry->vehicle());
1971 const int64_t penalty_shift =
1972 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1976 node_entry->set_value(
CapSub(insertion_cost, penalty_shift));
1987 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
1992 std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
1994 start_end_distances_per_node_ =
2000 std::vector<bool> visited(
model()->
Size(),
false);
2002 std::vector<int64_t> insertion_positions;
2005 std::vector<int64_t> delivery_insertion_positions;
2009 for (
const auto& index_pair : index_pairs) {
2010 for (int64_t pickup : index_pair.first) {
2014 for (int64_t delivery : index_pair.second) {
2021 visited[pickup] =
true;
2022 visited[delivery] =
true;
2023 ComputeEvaluatorSortedPositions(pickup, &insertion_positions);
2024 for (
const int64_t pickup_insertion : insertion_positions) {
2025 const int pickup_insertion_next =
Value(pickup_insertion);
2026 ComputeEvaluatorSortedPositionsOnRouteAfter(
2027 delivery, pickup, pickup_insertion_next,
2028 &delivery_insertion_positions);
2030 for (
const int64_t delivery_insertion :
2031 delivery_insertion_positions) {
2032 InsertBetween(pickup, pickup_insertion, pickup_insertion_next);
2033 const int64_t delivery_insertion_next =
2034 (delivery_insertion == pickup_insertion) ? pickup
2035 : (delivery_insertion == pickup) ? pickup_insertion_next
2036 :
Value(delivery_insertion);
2038 delivery_insertion_next);
2052 std::priority_queue<Seed> node_queue;
2055 while (!node_queue.empty()) {
2056 const int node = node_queue.top().second;
2058 if (
Contains(node) || visited[node])
continue;
2059 ComputeEvaluatorSortedPositions(node, &insertion_positions);
2060 for (
const int64_t insertion : insertion_positions) {
2072 void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2073 int64_t node, std::vector<int64_t>* sorted_positions) {
2074 CHECK(sorted_positions !=
nullptr);
2076 sorted_positions->clear();
2079 std::vector<std::pair<int64_t, int64_t>> valued_positions;
2080 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2081 const int64_t start =
model()->
Start(vehicle);
2085 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
2089 void LocalCheapestInsertionFilteredHeuristic::
2090 ComputeEvaluatorSortedPositionsOnRouteAfter(
2091 int64_t node, int64_t start, int64_t next_after_start,
2092 std::vector<int64_t>* sorted_positions) {
2093 CHECK(sorted_positions !=
nullptr);
2095 sorted_positions->clear();
2099 std::vector<std::pair<int64_t, int64_t>> valued_positions;
2102 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
2115 std::vector<std::vector<int64_t>> deliveries(
Size());
2116 std::vector<std::vector<int64_t>> pickups(
Size());
2118 for (
int first : pair.first) {
2119 for (
int second : pair.second) {
2120 deliveries[first].push_back(second);
2121 pickups[second].push_back(first);
2128 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
2129 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2130 sorted_vehicles[vehicle] = vehicle;
2132 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
2133 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
2135 for (
const int vehicle : sorted_vehicles) {
2137 bool extend_route =
true;
2142 while (extend_route) {
2143 extend_route =
false;
2145 int64_t
index = last_node;
2154 std::vector<int64_t> neighbors;
2156 std::unique_ptr<IntVarIterator> it(
2157 model()->Nexts()[
index]->MakeDomainIterator(
false));
2159 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
2162 for (
int i = 0; !found && i < neighbors.size(); ++i) {
2166 next = FindTopSuccessor(
index, neighbors);
2169 SortSuccessors(
index, &neighbors);
2170 ABSL_FALLTHROUGH_INTENDED;
2172 next = neighbors[i];
2179 bool contains_pickups =
false;
2180 for (int64_t pickup : pickups[
next]) {
2182 contains_pickups =
true;
2186 if (!contains_pickups) {
2190 std::vector<int64_t> next_deliveries;
2191 if (
next < deliveries.size()) {
2192 next_deliveries = GetPossibleNextsFromIterator(
2193 next, deliveries[
next].begin(), deliveries[
next].end());
2195 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
2196 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
2201 delivery = FindTopSuccessor(
next, next_deliveries);
2204 SortSuccessors(
next, &next_deliveries);
2205 ABSL_FALLTHROUGH_INTENDED;
2207 delivery = next_deliveries[j];
2225 if (
model()->IsEnd(end) && last_node != delivery) {
2226 last_node = delivery;
2227 extend_route =
true;
2242 bool CheapestAdditionFilteredHeuristic::
2243 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
2244 int vehicle2)
const {
2245 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2246 builder_.GetStartChainEnd(vehicle1));
2247 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
2248 builder_.GetStartChainEnd(vehicle2));
2249 if (has_partial_route1 == has_partial_route2) {
2250 return vehicle2 < vehicle1;
2252 return has_partial_route2 < has_partial_route1;
2265 int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2266 int64_t node,
const std::vector<int64_t>& successors) {
2268 int64_t best_successor = -1;
2269 for (int64_t successor : successors) {
2270 const int64_t evaluation = (successor >= 0)
2271 ? evaluator_(node, successor)
2273 if (evaluation < best_evaluation ||
2274 (evaluation == best_evaluation && successor > best_successor)) {
2275 best_evaluation = evaluation;
2276 best_successor = successor;
2279 return best_successor;
2282 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2283 int64_t node, std::vector<int64_t>* successors) {
2284 std::vector<std::pair<int64_t, int64_t>> values;
2285 values.reserve(successors->size());
2286 for (int64_t successor : *successors) {
2289 values.push_back({evaluator_(node, successor), -successor});
2291 std::sort(values.begin(), values.end());
2292 successors->clear();
2293 for (
auto value : values) {
2294 successors->push_back(-
value.second);
2305 comparator_(std::move(comparator)) {}
2307 int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
2308 int64_t node,
const std::vector<int64_t>& successors) {
2309 return *std::min_element(successors.begin(), successors.end(),
2310 [
this, node](
int successor1,
int successor2) {
2311 return comparator_(node, successor1, successor2);
2315 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
2316 int64_t node, std::vector<int64_t>* successors) {
2317 std::sort(successors->begin(), successors->end(),
2318 [
this, node](
int successor1,
int successor2) {
2319 return comparator_(node, successor1, successor2);
2371 template <
typename Saving>
2376 : savings_db_(savings_db),
2377 vehicle_types_(vehicle_types),
2378 index_in_sorted_savings_(0),
2379 single_vehicle_type_(vehicle_types == 1),
2380 using_incoming_reinjected_saving_(false),
2385 sorted_savings_per_vehicle_type_.clear();
2386 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
2387 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2388 savings.reserve(size * saving_neighbors);
2391 sorted_savings_.clear();
2392 costs_and_savings_per_arc_.clear();
2393 arc_indices_per_before_node_.clear();
2395 if (!single_vehicle_type_) {
2396 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
2397 arc_indices_per_before_node_.resize(size);
2398 for (
int before_node = 0; before_node < size; before_node++) {
2399 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
2402 skipped_savings_starting_at_.clear();
2403 skipped_savings_starting_at_.resize(size);
2404 skipped_savings_ending_at_.clear();
2405 skipped_savings_ending_at_.resize(size);
2406 incoming_reinjected_savings_ =
nullptr;
2407 outgoing_reinjected_savings_ =
nullptr;
2408 incoming_new_reinjected_savings_ =
nullptr;
2409 outgoing_new_reinjected_savings_ =
nullptr;
2413 int64_t before_node, int64_t after_node,
int vehicle_type) {
2414 CHECK(!sorted_savings_per_vehicle_type_.empty())
2415 <<
"Container not initialized!";
2416 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
2417 UpdateArcIndicesCostsAndSavings(before_node, after_node,
2418 {total_cost, saving});
2422 CHECK(!sorted_) <<
"Container already sorted!";
2424 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
2425 std::sort(savings.begin(), savings.end());
2428 if (single_vehicle_type_) {
2429 const auto& savings = sorted_savings_per_vehicle_type_[0];
2430 sorted_savings_.resize(savings.size());
2431 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
2432 [](
const Saving& saving) {
2433 return SavingAndArc({saving, -1});
2439 sorted_savings_.reserve(vehicle_types_ *
2440 costs_and_savings_per_arc_.size());
2442 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
2444 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2445 costs_and_savings_per_arc_[arc_index];
2446 DCHECK(!costs_and_savings.empty());
2449 costs_and_savings.begin(), costs_and_savings.end(),
2450 [](
const std::pair<int64_t, Saving>& cs1,
2451 const std::pair<int64_t, Saving>& cs2) {
return cs1 > cs2; });
2456 const int64_t
cost = costs_and_savings.back().first;
2457 while (!costs_and_savings.empty() &&
2458 costs_and_savings.back().first ==
cost) {
2459 sorted_savings_.push_back(
2460 {costs_and_savings.back().second, arc_index});
2461 costs_and_savings.pop_back();
2464 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2465 next_saving_type_and_index_for_arc_.clear();
2466 next_saving_type_and_index_for_arc_.resize(
2467 costs_and_savings_per_arc_.size(), {-1, -1});
2470 index_in_sorted_savings_ = 0;
2475 return index_in_sorted_savings_ < sorted_savings_.size() ||
2476 HasReinjectedSavings();
2480 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
2482 <<
"Update() should be called between two calls to GetSaving() !";
2486 if (HasReinjectedSavings()) {
2487 if (incoming_reinjected_savings_ !=
nullptr &&
2488 outgoing_reinjected_savings_ !=
nullptr) {
2490 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
2491 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
2492 if (incoming_saving < outgoing_saving) {
2493 current_saving_ = incoming_saving;
2494 using_incoming_reinjected_saving_ =
true;
2496 current_saving_ = outgoing_saving;
2497 using_incoming_reinjected_saving_ =
false;
2500 if (incoming_reinjected_savings_ !=
nullptr) {
2501 current_saving_ = incoming_reinjected_savings_->front();
2502 using_incoming_reinjected_saving_ =
true;
2504 if (outgoing_reinjected_savings_ !=
nullptr) {
2505 current_saving_ = outgoing_reinjected_savings_->front();
2506 using_incoming_reinjected_saving_ =
false;
2510 current_saving_ = sorted_savings_[index_in_sorted_savings_];
2512 return current_saving_.saving;
2515 void Update(
bool update_best_saving,
int type = -1) {
2516 CHECK(to_update_) <<
"Container already up to date!";
2517 if (update_best_saving) {
2518 const int64_t arc_index = current_saving_.arc_index;
2519 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
2521 if (!HasReinjectedSavings()) {
2522 index_in_sorted_savings_++;
2524 if (index_in_sorted_savings_ == sorted_savings_.size()) {
2525 sorted_savings_.swap(next_savings_);
2527 index_in_sorted_savings_ = 0;
2529 std::sort(sorted_savings_.begin(), sorted_savings_.end());
2530 next_saving_type_and_index_for_arc_.clear();
2531 next_saving_type_and_index_for_arc_.resize(
2532 costs_and_savings_per_arc_.size(), {-1, -1});
2535 UpdateReinjectedSavings();
2540 CHECK(!single_vehicle_type_);
2545 CHECK(sorted_) <<
"Savings not sorted yet!";
2547 return sorted_savings_per_vehicle_type_[type];
2551 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
2552 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
2556 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
2557 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
2561 struct SavingAndArc {
2565 bool operator<(
const SavingAndArc& other)
const {
2566 return std::tie(saving, arc_index) <
2567 std::tie(other.saving, other.arc_index);
2573 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
2574 const Saving& saving = saving_and_arc.saving;
2577 if (!savings_db_->
Contains(before_node)) {
2578 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
2580 if (!savings_db_->
Contains(after_node)) {
2581 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
2595 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
2596 if (single_vehicle_type_) {
2599 SkipSavingForArc(current_saving_);
2603 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
2604 const int previous_index = type_and_index.second;
2605 const int previous_type = type_and_index.first;
2606 bool next_saving_added =
false;
2609 if (previous_index >= 0) {
2612 if (type == -1 || previous_type == type) {
2615 next_saving_added =
true;
2616 next_saving = next_savings_[previous_index].saving;
2620 if (!next_saving_added &&
2621 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
2623 if (previous_index >= 0) {
2625 next_savings_[previous_index] = {next_saving, arc_index};
2628 type_and_index.second = next_savings_.size();
2629 next_savings_.push_back({next_saving, arc_index});
2631 next_saving_added =
true;
2637 SkipSavingForArc(current_saving_);
2641 if (next_saving_added) {
2642 SkipSavingForArc({next_saving, arc_index});
2647 void UpdateReinjectedSavings() {
2648 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
2649 &incoming_reinjected_savings_,
2650 using_incoming_reinjected_saving_);
2651 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
2652 &outgoing_reinjected_savings_,
2653 !using_incoming_reinjected_saving_);
2654 incoming_new_reinjected_savings_ =
nullptr;
2655 outgoing_new_reinjected_savings_ =
nullptr;
2658 void UpdateGivenReinjectedSavings(
2659 std::deque<SavingAndArc>* new_reinjected_savings,
2660 std::deque<SavingAndArc>** reinjected_savings,
2661 bool using_reinjected_savings) {
2662 if (new_reinjected_savings ==
nullptr) {
2664 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
2665 CHECK(!(*reinjected_savings)->empty());
2666 (*reinjected_savings)->pop_front();
2667 if ((*reinjected_savings)->empty()) {
2668 *reinjected_savings =
nullptr;
2677 if (*reinjected_savings !=
nullptr) {
2678 (*reinjected_savings)->clear();
2680 *reinjected_savings =
nullptr;
2681 if (!new_reinjected_savings->empty()) {
2682 *reinjected_savings = new_reinjected_savings;
2686 bool HasReinjectedSavings() {
2687 return outgoing_reinjected_savings_ !=
nullptr ||
2688 incoming_reinjected_savings_ !=
nullptr;
2691 void UpdateArcIndicesCostsAndSavings(
2692 int64_t before_node, int64_t after_node,
2693 const std::pair<int64_t, Saving>& cost_and_saving) {
2694 if (single_vehicle_type_) {
2697 absl::flat_hash_map<int, int>& arc_indices =
2698 arc_indices_per_before_node_[before_node];
2699 const auto& arc_inserted = arc_indices.insert(
2700 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
2701 const int index = arc_inserted.first->second;
2702 if (arc_inserted.second) {
2703 costs_and_savings_per_arc_.push_back({cost_and_saving});
2706 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
2710 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
2712 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
2713 costs_and_savings_per_arc_[arc_index];
2715 bool found_saving =
false;
2716 while (!costs_and_savings.empty() && !found_saving) {
2717 const Saving& saving = costs_and_savings.back().second;
2719 *next_saving = saving;
2720 found_saving =
true;
2722 costs_and_savings.pop_back();
2724 return found_saving;
2728 const int vehicle_types_;
2729 int64_t index_in_sorted_savings_;
2730 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
2731 std::vector<SavingAndArc> sorted_savings_;
2732 std::vector<SavingAndArc> next_savings_;
2733 std::vector<std::pair< int,
int>>
2734 next_saving_type_and_index_for_arc_;
2735 SavingAndArc current_saving_;
2736 const bool single_vehicle_type_;
2737 std::vector<std::vector<std::pair< int64_t,
Saving>>>
2738 costs_and_savings_per_arc_;
2739 std::vector<absl::flat_hash_map< int,
int>>
2740 arc_indices_per_before_node_;
2741 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
2742 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
2743 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
2744 std::deque<SavingAndArc>* incoming_reinjected_savings_;
2745 bool using_incoming_reinjected_saving_;
2746 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
2747 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
2758 vehicle_type_curator_(nullptr),
2766 size_squared_ = size * size;
2774 model()->GetVehicleTypeContainer());
2779 if (!ComputeSavings())
return false;
2784 if (!
Commit())
return false;
2790 int type, int64_t before_node, int64_t after_node) {
2791 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
2798 const int64_t start =
model()->
Start(vehicle);
2799 const int64_t end =
model()->
End(vehicle);
2807 ->GetCompatibleVehicleOfType(
2808 type, vehicle_is_compatible,
2809 [](
int) {
return false; })
2813 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
2814 std::vector<std::vector<int64_t>>* adjacency_lists) {
2815 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
2816 for (int64_t neighbor : (*adjacency_lists)[node]) {
2820 (*adjacency_lists)[neighbor].push_back(node);
2823 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
2824 adjacency_lists->begin(), [](std::vector<int64_t> vec) {
2825 std::sort(vec.begin(), vec.end());
2826 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
2842 bool SavingsFilteredHeuristic::ComputeSavings() {
2846 std::vector<int64_t> uncontained_non_start_end_nodes;
2847 uncontained_non_start_end_nodes.reserve(size);
2848 for (
int node = 0; node < size; node++) {
2850 uncontained_non_start_end_nodes.push_back(node);
2854 const int64_t saving_neighbors =
2855 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
2856 static_cast<int64_t>(uncontained_non_start_end_nodes.size()));
2859 absl::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
2862 std::vector<std::vector<int64_t>> adjacency_lists(size);
2864 for (
int type = 0; type < num_vehicle_types; ++type) {
2871 const int64_t cost_class =
2873 const int64_t start =
model()->
Start(vehicle);
2874 const int64_t end =
model()->
End(vehicle);
2879 for (
int before_node : uncontained_non_start_end_nodes) {
2880 std::vector<std::pair< int64_t, int64_t>>
2882 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
2884 for (
int after_node : uncontained_non_start_end_nodes) {
2885 if (after_node != before_node) {
2886 costed_after_nodes.push_back(std::make_pair(
2887 model()->GetArcCostForClass(before_node, after_node, cost_class),
2891 if (saving_neighbors < costed_after_nodes.size()) {
2892 std::nth_element(costed_after_nodes.begin(),
2893 costed_after_nodes.begin() + saving_neighbors,
2894 costed_after_nodes.end());
2895 costed_after_nodes.resize(saving_neighbors);
2897 adjacency_lists[before_node].resize(costed_after_nodes.size());
2898 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
2899 adjacency_lists[before_node].begin(),
2900 [](std::pair<int64_t, int64_t> cost_and_node) {
2901 return cost_and_node.second;
2905 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
2910 for (
int before_node : uncontained_non_start_end_nodes) {
2911 const int64_t before_to_end_cost =
2913 const int64_t start_to_before_cost =
2914 CapSub(
model()->GetArcCostForClass(start, before_node, cost_class),
2917 for (int64_t after_node : adjacency_lists[before_node]) {
2918 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
2919 before_node == after_node ||
Contains(after_node)) {
2922 const int64_t arc_cost =
2924 const int64_t start_to_after_cost =
2925 CapSub(
model()->GetArcCostForClass(start, after_node, cost_class),
2927 const int64_t after_to_end_cost =
2930 const double weighted_arc_cost_fp =
2932 const int64_t weighted_arc_cost =
2934 ? static_cast<int64_t>(weighted_arc_cost_fp)
2935 : std::numeric_limits<int64_t>::
max();
2936 const int64_t saving_value =
CapSub(
2937 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
2940 BuildSaving(-saving_value, type, before_node, after_node);
2942 const int64_t total_cost =
2943 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
2954 int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
2955 int num_vehicle_types)
const {
2958 const int64_t num_neighbors_with_ratio =
2965 const double max_memory_usage_in_savings_unit =
2983 if (num_vehicle_types > 1) {
2984 multiplicative_factor += 1.5;
2986 const double num_savings =
2987 max_memory_usage_in_savings_unit / multiplicative_factor;
2988 const int64_t num_neighbors_with_memory_restriction =
2989 std::max(1.0, num_savings / (num_vehicle_types * size));
2991 return std::min(num_neighbors_with_ratio,
2992 num_neighbors_with_memory_restriction);
2997 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3003 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
3004 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
3005 for (
int type = 0; type < vehicle_types; type++) {
3006 const int vehicle_type_offset = type * size;
3007 const std::vector<Saving>& sorted_savings_for_type =
3009 for (
const Saving& saving : sorted_savings_for_type) {
3012 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3014 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3025 const bool nodes_not_contained =
3028 bool committed =
false;
3030 if (nodes_not_contained) {
3038 const int64_t start =
model()->
Start(vehicle);
3039 const int64_t end =
model()->
End(vehicle);
3043 const int saving_offset = type * size;
3045 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
3047 out_savings_ptr[saving_offset + before_node].size()) {
3050 int before_before_node = -1;
3051 int after_after_node = -1;
3052 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
3053 const Saving& in_saving =
3054 *(in_savings_ptr[saving_offset + after_node][in_index]);
3056 out_savings_ptr[saving_offset + before_node].size()) {
3057 const Saving& out_saving =
3058 *(out_savings_ptr[saving_offset + before_node][out_index]);
3073 *(out_savings_ptr[saving_offset + before_node][out_index]));
3076 if (after_after_node != -1) {
3080 SetValue(after_node, after_after_node);
3084 after_node = after_after_node;
3094 if (!
Contains(before_before_node)) {
3095 SetValue(start, before_before_node);
3096 SetValue(before_before_node, before_node);
3099 before_node = before_before_node;
3116 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3122 first_node_on_route_.resize(vehicles, -1);
3123 last_node_on_route_.resize(vehicles, -1);
3124 vehicle_of_first_or_last_node_.resize(size, -1);
3126 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
3127 const int64_t start =
model()->
Start(vehicle);
3128 const int64_t end =
model()->
End(vehicle);
3132 int64_t node =
Value(start);
3134 vehicle_of_first_or_last_node_[node] = vehicle;
3135 first_node_on_route_[vehicle] = node;
3138 while (
next != end) {
3142 vehicle_of_first_or_last_node_[node] = vehicle;
3143 last_node_on_route_[vehicle] = node;
3156 bool committed =
false;
3164 vehicle_of_first_or_last_node_[before_node] = vehicle;
3165 vehicle_of_first_or_last_node_[after_node] = vehicle;
3166 first_node_on_route_[vehicle] = before_node;
3167 last_node_on_route_[vehicle] = after_node;
3179 const int v1 = vehicle_of_first_or_last_node_[before_node];
3180 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3182 const int v2 = vehicle_of_first_or_last_node_[after_node];
3183 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3185 if (before_node == last_node && after_node == first_node && v1 != v2 &&
3194 MergeRoutes(v1, v2, before_node, after_node);
3199 const int vehicle = vehicle_of_first_or_last_node_[before_node];
3200 const int64_t last_node =
3201 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3203 if (before_node == last_node) {
3204 const int64_t end =
model()->
End(vehicle);
3208 if (type != route_type) {
3219 if (first_node_on_route_[vehicle] != before_node) {
3222 vehicle_of_first_or_last_node_[before_node] = -1;
3224 vehicle_of_first_or_last_node_[after_node] = vehicle;
3225 last_node_on_route_[vehicle] = after_node;
3232 const int vehicle = vehicle_of_first_or_last_node_[after_node];
3233 const int64_t first_node =
3234 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3236 if (after_node == first_node) {
3237 const int64_t start =
model()->
Start(vehicle);
3241 if (type != route_type) {
3252 if (last_node_on_route_[vehicle] != after_node) {
3255 vehicle_of_first_or_last_node_[after_node] = -1;
3257 vehicle_of_first_or_last_node_[before_node] = vehicle;
3258 first_node_on_route_[vehicle] = before_node;
3267 void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
3269 int64_t before_node,
3270 int64_t after_node) {
3272 const int64_t new_first_node = first_node_on_route_[first_vehicle];
3273 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
3275 const int64_t new_last_node = last_node_on_route_[second_vehicle];
3276 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
3280 int used_vehicle = first_vehicle;
3281 int unused_vehicle = second_vehicle;
3282 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
3283 model()->GetFixedCostOfVehicle(second_vehicle)) {
3284 used_vehicle = second_vehicle;
3285 unused_vehicle = first_vehicle;
3290 if (used_vehicle == first_vehicle) {
3295 bool committed =
Commit();
3297 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
3298 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
3300 std::swap(used_vehicle, unused_vehicle);
3303 if (used_vehicle == first_vehicle) {
3314 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
3315 model()->GetFixedCostOfVehicle(unused_vehicle));
3318 first_node_on_route_[unused_vehicle] = -1;
3319 last_node_on_route_[unused_vehicle] = -1;
3320 vehicle_of_first_or_last_node_[before_node] = -1;
3321 vehicle_of_first_or_last_node_[after_node] = -1;
3322 first_node_on_route_[used_vehicle] = new_first_node;
3323 last_node_on_route_[used_vehicle] = new_last_node;
3324 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
3325 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
3333 bool use_minimum_matching)
3335 use_minimum_matching_(use_minimum_matching) {}
3345 std::vector<int> indices(1, 0);
3346 for (
int i = 1; i < size; ++i) {
3347 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
3348 indices.push_back(i);
3352 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
3353 std::vector<bool> class_covered(num_cost_classes,
false);
3354 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3355 const int64_t cost_class =
3357 if (!class_covered[cost_class]) {
3358 class_covered[cost_class] =
true;
3359 const int64_t start =
model()->
Start(vehicle);
3360 const int64_t end =
model()->
End(vehicle);
3361 auto cost = [
this, &indices, start, end, cost_class](
int from,
int to) {
3364 const int from_index = (from == 0) ? start : indices[from];
3365 const int to_index = (to == 0) ? end : indices[to];
3366 const int64_t
cost =
3374 using Cost = decltype(
cost);
3376 indices.size(),
cost);
3377 if (use_minimum_matching_) {
3380 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
3382 if (christofides_solver.Solve()) {
3383 path_per_cost_class[cost_class] =
3384 christofides_solver.TravelingSalesmanPath();
3389 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3390 const int64_t cost_class =
3392 const std::vector<int>& path = path_per_cost_class[cost_class];
3393 if (path.empty())
continue;
3398 const int end =
model()->
End(vehicle);
3399 for (
int i = 1; i < path.size() - 1 && prev != end; ++i) {
3401 int next = indices[path[i]];
3429 struct SweepIndexSortAngle {
3430 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3431 return (node1.angle < node2.angle);
3433 } SweepIndexAngleComparator;
3435 struct SweepIndexSortDistance {
3436 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
3437 return (node1.distance < node2.distance);
3439 } SweepIndexDistanceComparator;
3443 const std::vector<std::pair<int64_t, int64_t>>& points)
3444 : coordinates_(2 * points.size(), 0), sectors_(1) {
3445 for (int64_t i = 0; i < points.size(); ++i) {
3446 coordinates_[2 * i] = points[i].first;
3447 coordinates_[2 * i + 1] = points[i].second;
3454 const double pi_rad = 3.14159265;
3456 const int x0 = coordinates_[0];
3457 const int y0 = coordinates_[1];
3459 std::vector<SweepIndex> sweep_indices;
3460 for (int64_t
index = 0; index < static_cast<int>(coordinates_.size()) / 2;
3462 const int x = coordinates_[2 *
index];
3463 const int y = coordinates_[2 *
index + 1];
3464 const double x_delta = x - x0;
3465 const double y_delta = y - y0;
3466 double square_distance = x_delta * x_delta + y_delta * y_delta;
3467 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
3469 SweepIndex sweep_index(
index,
angle, square_distance);
3470 sweep_indices.push_back(sweep_index);
3472 std::sort(sweep_indices.begin(), sweep_indices.end(),
3473 SweepIndexDistanceComparator);
3475 const int size = static_cast<int>(sweep_indices.size()) / sectors_;
3476 for (
int sector = 0; sector < sectors_; ++sector) {
3477 std::vector<SweepIndex> cluster;
3478 std::vector<SweepIndex>::iterator begin =
3479 sweep_indices.begin() + sector * size;
3480 std::vector<SweepIndex>::iterator end =
3481 sector == sectors_ - 1 ? sweep_indices.end()
3482 : sweep_indices.begin() + (sector + 1) * size;
3483 std::sort(begin, end, SweepIndexAngleComparator);
3485 for (
const SweepIndex& sweep_index : sweep_indices) {
3486 indices->push_back(sweep_index.index);
3514 class RouteConstructor {
3516 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
3517 bool check_assignment, int64_t num_indices,
3518 const std::vector<Link>& links_list)
3519 : assignment_(assignment),
3521 check_assignment_(check_assignment),
3522 solver_(model_->solver()),
3523 num_indices_(num_indices),
3524 links_list_(links_list),
3525 nexts_(model_->Nexts()),
3526 in_route_(num_indices_, -1),
3528 index_to_chain_index_(num_indices, -1),
3529 index_to_vehicle_class_index_(num_indices, -1) {
3531 const std::vector<std::string> dimension_names =
3532 model_->GetAllDimensionNames();
3533 dimensions_.assign(dimension_names.size(),
nullptr);
3534 for (
int i = 0; i < dimension_names.size(); ++i) {
3535 dimensions_[i] = &model_->GetDimensionOrDie(dimension_names[i]);
3538 cumuls_.resize(dimensions_.size());
3539 for (std::vector<int64_t>& cumuls :
cumuls_) {
3540 cumuls.resize(num_indices_);
3542 new_possible_cumuls_.resize(dimensions_.size());
3545 ~RouteConstructor() {}
3548 model_->solver()->TopPeriodicCheck();
3551 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
3552 std::vector<int> route(1,
index);
3553 routes_.push_back(route);
3554 in_route_[
index] = routes_.size() - 1;
3558 for (
const Link&
link : links_list_) {
3559 model_->solver()->TopPeriodicCheck();
3560 const int index1 =
link.link.first;
3561 const int index2 =
link.link.second;
3567 if (index_to_vehicle_class_index_[index1] < 0) {
3568 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3569 ++dimension_index) {
3570 cumuls_[dimension_index][index1] =
3571 std::max(dimensions_[dimension_index]->GetTransitValue(
3573 dimensions_[dimension_index]->CumulVar(index1)->Min());
3576 if (index_to_vehicle_class_index_[index2] < 0) {
3577 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3578 ++dimension_index) {
3579 cumuls_[dimension_index][index2] =
3580 std::max(dimensions_[dimension_index]->GetTransitValue(
3582 dimensions_[dimension_index]->CumulVar(index2)->Min());
3586 const int route_index1 = in_route_[index1];
3587 const int route_index2 = in_route_[index2];
3589 route_index1 >= 0 && route_index2 >= 0 &&
3590 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
3593 if (Merge(merge, route_index1, route_index2)) {
3599 model_->solver()->TopPeriodicCheck();
3603 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3605 final_chains_.push_back(chains_[chain_index]);
3608 std::sort(final_chains_.begin(), final_chains_.end(), ChainComparator);
3609 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
3611 final_routes_.push_back(routes_[route_index]);
3614 std::sort(final_routes_.begin(), final_routes_.end(), RouteComparator);
3616 const int extra_vehicles =
std::max(
3617 0, static_cast<int>(final_chains_.size()) - model_->vehicles());
3619 int chain_index = 0;
3620 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
3622 if (chain_index - extra_vehicles >= model_->vehicles()) {
3625 const int start = final_chains_[chain_index].head;
3626 const int end = final_chains_[chain_index].tail;
3628 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3629 assignment_->SetValue(
3630 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
3631 assignment_->Add(nexts_[end]);
3632 assignment_->SetValue(nexts_[end],
3633 model_->End(chain_index - extra_vehicles));
3637 for (
int route_index = 0; route_index < final_routes_.size();
3639 if (chain_index - extra_vehicles >= model_->vehicles()) {
3642 DCHECK_LT(route_index, final_routes_.size());
3643 const int head = final_routes_[route_index].front();
3644 const int tail = final_routes_[route_index].back();
3645 if (
head ==
tail && head < model_->Size()) {
3647 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
3648 assignment_->SetValue(
3649 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
3650 assignment_->Add(nexts_[
tail]);
3651 assignment_->SetValue(nexts_[
tail],
3652 model_->End(chain_index - extra_vehicles));
3660 if (!assignment_->Contains(
next)) {
3661 assignment_->Add(
next);
3669 const std::vector<std::vector<int>>& final_routes()
const {
3670 return final_routes_;
3674 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
3677 bool operator()(
const std::vector<int>& route1,
3678 const std::vector<int>& route2)
const {
3679 return (route1.size() < route2.size());
3690 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
3691 return (chain1.nodes < chain2.nodes);
3695 bool Head(
int node)
const {
3696 return (node == routes_[in_route_[node]].front());
3699 bool Tail(
int node)
const {
3700 return (node == routes_[in_route_[node]].back());
3703 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
3704 int dimension_index) {
3705 const RoutingDimension& dimension = *dimensions_[dimension_index];
3706 std::vector<int>::const_iterator it = route.begin();
3707 int64_t cumul = route_cumul;
3708 while (it != route.end()) {
3709 const int previous = *it;
3710 const int64_t cumul_previous = cumul;
3714 if (it == route.end()) {
3717 const int next = *it;
3718 int64_t available_from_previous =
3719 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
3720 int64_t available_cumul_next =
3723 const int64_t slack = available_cumul_next - available_from_previous;
3724 if (slack > dimension.SlackVar(previous)->Max()) {
3725 available_cumul_next =
3726 available_from_previous + dimension.SlackVar(previous)->Max();
3729 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
3732 if (available_cumul_next <=
cumuls_[dimension_index][
next]) {
3735 cumul = available_cumul_next;
3740 bool CheckRouteConnection(
const std::vector<int>& route1,
3741 const std::vector<int>& route2,
int dimension_index,
3743 const int tail1 = route1.back();
3744 const int head2 = route2.front();
3745 const int tail2 = route2.back();
3746 const RoutingDimension& dimension = *dimensions_[dimension_index];
3747 int non_depot_node = -1;
3748 for (
int node = 0; node < num_indices_; ++node) {
3749 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
3750 non_depot_node = node;
3755 const int64_t depot_threshold =
3756 std::max(dimension.SlackVar(non_depot_node)->Max(),
3757 dimension.CumulVar(non_depot_node)->Max());
3759 int64_t available_from_tail1 =
cumuls_[dimension_index][tail1] +
3760 dimension.GetTransitValue(tail1, head2, 0);
3761 int64_t new_available_cumul_head2 =
3764 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
3765 if (slack > dimension.SlackVar(tail1)->Max()) {
3766 new_available_cumul_head2 =
3767 available_from_tail1 + dimension.SlackVar(tail1)->Max();
3770 bool feasible_route =
true;
3771 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
3774 if (new_available_cumul_head2 <=
cumuls_[dimension_index][head2]) {
3779 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
3780 const int64_t new_possible_cumul_tail2 =
3782 ? new_possible_cumuls_[dimension_index][tail2]
3783 :
cumuls_[dimension_index][tail2];
3785 if (!feasible_route || (new_possible_cumul_tail2 +
3786 dimension.GetTransitValue(tail2,
end_depot, 0) >
3793 bool FeasibleMerge(
const std::vector<int>& route1,
3794 const std::vector<int>& route2,
int node1,
int node2,
3797 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
3802 if (!((index_to_vehicle_class_index_[node1] == -1 &&
3803 index_to_vehicle_class_index_[node2] == -1) ||
3805 index_to_vehicle_class_index_[node2] == -1) ||
3806 (index_to_vehicle_class_index_[node1] == -1 &&
3815 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3816 ++dimension_index) {
3817 new_possible_cumuls_[dimension_index].clear();
3818 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
3827 bool CheckTempAssignment(Assignment*
const temp_assignment,
3828 int new_chain_index,
int old_chain_index,
int head1,
3829 int tail1,
int head2,
int tail2) {
3832 if (new_chain_index >= model_->vehicles())
return false;
3833 const int start = head1;
3834 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
3835 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
3837 temp_assignment->Add(nexts_[tail1]);
3838 temp_assignment->SetValue(nexts_[tail1], head2);
3839 temp_assignment->Add(nexts_[tail2]);
3840 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
3841 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
3842 if ((chain_index != new_chain_index) &&
3843 (chain_index != old_chain_index) &&
3845 const int start = chains_[chain_index].head;
3846 const int end = chains_[chain_index].tail;
3847 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
3848 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
3850 temp_assignment->Add(nexts_[end]);
3851 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
3854 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
3857 bool UpdateAssignment(
const std::vector<int>& route1,
3858 const std::vector<int>& route2) {
3859 bool feasible =
true;
3860 const int head1 = route1.front();
3861 const int tail1 = route1.back();
3862 const int head2 = route2.front();
3863 const int tail2 = route2.back();
3864 const int chain_index1 = index_to_chain_index_[head1];
3865 const int chain_index2 = index_to_chain_index_[head2];
3866 if (chain_index1 < 0 && chain_index2 < 0) {
3867 const int chain_index = chains_.size();
3868 if (check_assignment_) {
3869 Assignment*
const temp_assignment =
3870 solver_->MakeAssignment(assignment_);
3871 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
3872 tail1, head2, tail2);
3879 index_to_chain_index_[head1] = chain_index;
3880 index_to_chain_index_[tail2] = chain_index;
3881 chains_.push_back(chain);
3883 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
3884 if (check_assignment_) {
3885 Assignment*
const temp_assignment =
3886 solver_->MakeAssignment(assignment_);
3888 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
3889 head1, tail1, head2, tail2);
3892 index_to_chain_index_[tail2] = chain_index1;
3893 chains_[chain_index1].head = head1;
3894 chains_[chain_index1].tail = tail2;
3895 ++chains_[chain_index1].nodes;
3897 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
3898 if (check_assignment_) {
3899 Assignment*
const temp_assignment =
3900 solver_->MakeAssignment(assignment_);
3902 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
3903 head1, tail1, head2, tail2);
3906 index_to_chain_index_[head1] = chain_index2;
3907 chains_[chain_index2].head = head1;
3908 chains_[chain_index2].tail = tail2;
3909 ++chains_[chain_index2].nodes;
3912 if (check_assignment_) {
3913 Assignment*
const temp_assignment =
3914 solver_->MakeAssignment(assignment_);
3916 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
3917 head1, tail1, head2, tail2);
3920 index_to_chain_index_[tail2] = chain_index1;
3921 chains_[chain_index1].head = head1;
3922 chains_[chain_index1].tail = tail2;
3923 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
3924 deleted_chains_.insert(chain_index2);
3928 assignment_->Add(nexts_[tail1]);
3929 assignment_->SetValue(nexts_[tail1], head2);
3934 bool Merge(
bool merge,
int index1,
int index2) {
3936 if (UpdateAssignment(routes_[index1], routes_[index2])) {
3938 for (
const int node : routes_[index2]) {
3939 in_route_[node] = index1;
3940 routes_[index1].push_back(node);
3942 for (
int dimension_index = 0; dimension_index < dimensions_.size();
3943 ++dimension_index) {
3944 for (
const std::pair<int, int64_t> new_possible_cumul :
3945 new_possible_cumuls_[dimension_index]) {
3946 cumuls_[dimension_index][new_possible_cumul.first] =
3947 new_possible_cumul.second;
3950 deleted_routes_.insert(index2);
3957 Assignment*
const assignment_;
3958 RoutingModel*
const model_;
3959 const bool check_assignment_;
3960 Solver*
const solver_;
3961 const int64_t num_indices_;
3962 const std::vector<Link> links_list_;
3963 std::vector<IntVar*> nexts_;
3964 std::vector<const RoutingDimension*> dimensions_;
3965 std::vector<std::vector<int64_t>>
cumuls_;
3966 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
3967 std::vector<std::vector<int>> routes_;
3968 std::vector<int> in_route_;
3969 absl::flat_hash_set<int> deleted_routes_;
3970 std::vector<std::vector<int>> final_routes_;
3971 std::vector<Chain> chains_;
3972 absl::flat_hash_set<int> deleted_chains_;
3973 std::vector<Chain> final_chains_;
3974 std::vector<int> index_to_chain_index_;
3975 std::vector<int> index_to_vehicle_class_index_;
3981 class SweepBuilder :
public DecisionBuilder {
3983 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
3984 : model_(
model), check_assignment_(check_assignment) {}
3985 ~SweepBuilder()
override {}
3987 Decision* Next(Solver*
const solver)
override {
3992 Assignment*
const assignment = solver->MakeAssignment();
3993 route_constructor_ = absl::make_unique<RouteConstructor>(
3994 assignment, model_, check_assignment_, num_indices_, links_);
3996 route_constructor_->Construct();
3997 route_constructor_.reset(
nullptr);
3999 assignment->Restore();
4006 const int depot = model_->GetDepot();
4007 num_indices_ = model_->Size() + model_->vehicles();
4008 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4009 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4010 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4012 std::vector<int64_t> indices;
4013 model_->sweep_arranger()->ArrangeIndices(&indices);
4014 for (
int i = 0; i < indices.size() - 1; ++i) {
4015 const int64_t first = indices[i];
4016 const int64_t second = indices[i + 1];
4017 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4018 (model_->IsStart(second) || !model_->IsEnd(second))) {
4019 if (first != depot && second != depot) {
4020 Link
link(std::make_pair(first, second), 0, 0, depot, depot);
4021 links_.push_back(
link);
4027 RoutingModel*
const model_;
4028 std::unique_ptr<RouteConstructor> route_constructor_;
4029 const bool check_assignment_;
4030 int64_t num_indices_;
4031 std::vector<Link> links_;
4036 bool check_assignment) {
4037 return model->solver()->RevAlloc(
new SweepBuilder(
model, check_assignment));
4046 class AllUnperformed :
public DecisionBuilder {
4049 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
4050 ~AllUnperformed()
override {}
4051 Decision* Next(Solver*
const solver)
override {
4054 model_->CostVar()->FreezeQueue();
4055 for (
int i = 0; i < model_->Size(); ++i) {
4056 if (!model_->IsStart(i)) {
4057 model_->ActiveVar(i)->SetValue(0);
4060 model_->CostVar()->UnfreezeQueue();
4065 RoutingModel*
const model_;
4070 return model->solver()->RevAlloc(
new AllUnperformed(
model));
4075 class GuidedSlackFinalizer :
public DecisionBuilder {
4077 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel*
model,
4078 std::function<int64_t(int64_t)> initializer);
4079 Decision* Next(Solver* solver)
override;
4082 int64_t SelectValue(int64_t
index);
4083 int64_t ChooseVariable();
4085 const RoutingDimension*
const dimension_;
4086 RoutingModel*
const model_;
4087 const std::function<int64_t(int64_t)> initializer_;
4088 RevArray<bool> is_initialized_;
4089 std::vector<int64_t> initial_values_;
4090 Rev<int64_t> current_index_;
4091 Rev<int64_t> current_route_;
4092 RevArray<int64_t> last_delta_used_;
4097 GuidedSlackFinalizer::GuidedSlackFinalizer(
4098 const RoutingDimension* dimension, RoutingModel*
model,
4099 std::function<int64_t(int64_t)> initializer)
4102 initializer_(std::move(initializer)),
4103 is_initialized_(dimension->slacks().size(), false),
4104 initial_values_(dimension->slacks().size(),
4105 std::numeric_limits<int64_t>::
min()),
4106 current_index_(model_->Start(0)),
4108 last_delta_used_(dimension->slacks().size(), 0) {}
4110 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4111 CHECK_EQ(solver, model_->solver());
4112 const int node_idx = ChooseVariable();
4113 CHECK(node_idx == -1 ||
4114 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
4115 if (node_idx != -1) {
4116 if (!is_initialized_[node_idx]) {
4117 initial_values_[node_idx] = initializer_(node_idx);
4118 is_initialized_.SetValue(solver, node_idx,
true);
4120 const int64_t
value = SelectValue(node_idx);
4121 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
4122 return solver->MakeAssignVariableValue(slack_variable,
value);
4127 int64_t GuidedSlackFinalizer::SelectValue(int64_t
index) {
4128 const IntVar*
const slack_variable = dimension_->SlackVar(
index);
4129 const int64_t center = initial_values_[
index];
4130 const int64_t max_delta =
4131 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4137 while (std::abs(
delta) < max_delta &&
4138 !slack_variable->Contains(center +
delta)) {
4145 last_delta_used_.SetValue(model_->solver(),
index,
delta);
4146 return center +
delta;
4149 int64_t GuidedSlackFinalizer::ChooseVariable() {
4150 int64_t int_current_node = current_index_.Value();
4151 int64_t int_current_route = current_route_.Value();
4153 while (int_current_route < model_->vehicles()) {
4154 while (!model_->IsEnd(int_current_node) &&
4155 dimension_->SlackVar(int_current_node)->Bound()) {
4156 int_current_node = model_->NextVar(int_current_node)->Value();
4158 if (!model_->IsEnd(int_current_node)) {
4161 int_current_route += 1;
4162 if (int_current_route < model_->vehicles()) {
4163 int_current_node = model_->Start(int_current_route);
4167 CHECK(int_current_route == model_->vehicles() ||
4168 !dimension_->SlackVar(int_current_node)->Bound());
4169 current_index_.SetValue(model_->solver(), int_current_node);
4170 current_route_.SetValue(model_->solver(), int_current_route);
4171 if (int_current_route < model_->vehicles()) {
4172 return int_current_node;
4181 std::function<int64_t(int64_t)> initializer) {
4182 return solver_->RevAlloc(
4183 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
4186 int64_t RoutingDimension::ShortestTransitionSlack(int64_t node)
const {
4188 CHECK(!model_->IsEnd(node));
4192 const int64_t
next = model_->NextVar(node)->Value();
4193 if (model_->IsEnd(
next)) {
4194 return SlackVar(node)->Min();
4196 const int64_t next_next = model_->NextVar(
next)->Value();
4197 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4198 CHECK_EQ(serving_vehicle, model_->VehicleVar(
next)->Value());
4200 model_->StateDependentTransitCallback(
4201 state_dependent_class_evaluators_
4202 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
4205 const int64_t next_cumul_min = CumulVar(
next)->Min();
4206 const int64_t next_cumul_max = CumulVar(
next)->Max();
4207 const int64_t optimal_next_cumul =
4208 transit_from_next.transit_plus_identity->RangeMinArgument(
4209 next_cumul_min, next_cumul_max + 1);
4211 DCHECK_LE(next_cumul_min, optimal_next_cumul);
4212 DCHECK_LE(optimal_next_cumul, next_cumul_max);
4217 const int64_t current_cumul = CumulVar(node)->Value();
4218 const int64_t current_state_independent_transit = model_->TransitCallback(
4219 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
4220 const int64_t current_state_dependent_transit =
4222 ->StateDependentTransitCallback(
4223 state_dependent_class_evaluators_
4224 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4226 .transit->Query(current_cumul);
4227 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4228 current_state_independent_transit -
4229 current_state_dependent_transit;
4230 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4231 CHECK_LE(optimal_slack, SlackVar(node)->Max());
4232 return optimal_slack;
4238 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4244 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
4246 const std::vector<IntVar*> variables_;
4248 int64_t current_step_;
4255 int64_t current_direction_;
4260 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
4261 : variables_(std::move(variables)),
4264 current_direction_(0) {}
4266 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
4268 static const int64_t sings[] = {1, -1};
4269 for (; 1 <= current_step_; current_step_ /= 2) {
4270 for (; current_direction_ < 2 * variables_.size();) {
4271 const int64_t variable_idx = current_direction_ / 2;
4272 IntVar*
const variable = variables_[variable_idx];
4273 const int64_t sign_index = current_direction_ % 2;
4274 const int64_t sign = sings[sign_index];
4275 const int64_t offset = sign * current_step_;
4276 const int64_t new_value = center_->
Value(variable) + offset;
4277 ++current_direction_;
4278 if (variable->Contains(new_value)) {
4279 delta->Add(variable);
4280 delta->SetValue(variable, new_value);
4284 current_direction_ = 0;
4289 void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
4290 CHECK(assignment !=
nullptr);
4291 current_step_ = FindMaxDistanceToDomain(assignment);
4292 center_ = assignment;
4295 int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
4296 const Assignment* assignment) {
4298 for (
const IntVar*
const var : variables_) {
4307 std::vector<IntVar*> variables) {
4308 return std::unique_ptr<LocalSearchOperator>(
4309 new GreedyDescentLSOperator(std::move(variables)));
4314 CHECK(dimension !=
nullptr);
4316 std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t
index) {
4322 solver_->MakeSolveOnce(guided_finalizer);
4323 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
4324 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
4325 start_cumuls[vehicle_idx] = dimension->
CumulVar(starts_[vehicle_idx]);
4328 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
4330 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
4332 Assignment*
const first_solution = solver_->MakeAssignment();
4333 first_solution->
Add(start_cumuls);
4334 for (
IntVar*
const cumul : start_cumuls) {
4335 first_solution->
SetValue(cumul, cumul->Min());
4338 solver_->MakeLocalSearchPhase(first_solution,
parameters);
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
Returns the cost of inserting 'node_to_insert' between 'insert_after' and 'insert_before' on the 'veh...
int64_t CapSub(int64_t x, int64_t y)
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
void UpdateWithType(int type)
int64_t Value(const IntVar *const var) const
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle)
void set_insert_after(int insert_after)
The base class for all local search operators.
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
CheapestAdditionFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
#define CHECK_GE(val1, val2)
static constexpr Value PATH_MOST_CONSTRAINED_ARC
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
std::unique_ptr< SavingsContainer< Saving > > savings_container_
int delivery_to_insert() const
~SavingsFilteredHeuristic() override
Manager for any NodeIndex <-> variable index conversion.
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
int64_t GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
void SetValue(const IntVar *const var, int64_t value)
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
#define CHECK_GT(val1, val2)
void set_value(int64_t value)
#define VLOG(verboselevel)
Filter-based heuristic dedicated to routing.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.
IntVarElement * Add(IntVar *const var)
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
int pickup_to_insert() const
RoutingModel * model() const
virtual int64_t Min() const =0
std::string DebugString() const override
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
Dimensions represent quantities accumulated at nodes along the routes.
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
#define DCHECK_GT(val1, val2)
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
std::function< int64_t(int64_t)> penalty_evaluator_
void Reset(const std::function< bool(int)> &store_vehicle)
Resets the vehicles stored, storing only vehicles from the vehicle_type_container_ for which store_ve...
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
const E & Element(const V *const var) const
static constexpr Value PATH_CHEAPEST_ARC
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
A DecisionBuilder is responsible for creating the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
int node_to_insert() const
const Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
void ReinjectSkippedSavingsEndingAt(int64_t node)
#define CHECK_LT(val1, val2)
void set_vehicle(int vehicle)
bool StopSearch() override
Returns true if the search must be stopped.
virtual void Start(const Assignment *assignment)=0
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Generic filter-based heuristic applied to IntVars.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
void ResetSolution()
Resets the data members for a new solution.
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64_t node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
const std::vector< IntVar * > cumuls_
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
void SetMatchingAlgorithm(MatchingAlgorithm matching)
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
int64_t CapAdd(int64_t x, int64_t y)
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
#define DCHECK_NE(val1, val2)
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void set_pickup_insert_after(int pickup_insert_after)
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
int pickup_insert_after() const
bool IsStart(int64_t index) const
Returns true if 'index' represents the first node of a route.
#define CHECK_LE(val1, val2)
std::pair< int64_t, int64_t > Saving
void InitializeContainer(int64_t size, int64_t saving_neighbors)
virtual void ResetVehicleIndices()
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
bool VehicleIsEmpty(int vehicle) const
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
void InitializePriorityQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, Queue *priority_queue)
Initializes the priority_queue by inserting the best entry corresponding to each node,...
IntContainer * MutableIntVarContainer()
bool ContainsKey(const Collection &collection, const Key &key)
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor)
Inserts 'node' just after 'predecessor', and just before 'successor', resulting in the following subs...
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
std::function< int64_t(int64_t, int64_t)> evaluator_
std::pair< int, int > link
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(int64_t index, int64_t max_cardinality, F f) const
Calls f for each variable index of indices in the same disjunctions as the node corresponding to the ...
An Assignment is a variable -> domains mapping, used to report solutions to the user.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
The class IntVar is a subset of IntExpr.
void Update(bool update_best_saving, int type=-1)
#define DCHECK_GE(val1, val2)
std::vector< std::deque< int > > vehicles_per_vehicle_class
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
Assignment *const assignment_
void STLClearObject(T *obj)
#define CHECK_EQ(val1, val2)
const std::vector< IntVar * > vars_
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
FirstSolutionStrategy_Value
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
RoutingIndexPairs IndexPairs
void NoteChangedPriority(T *val)
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
#define DCHECK(condition)
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
std::pair< StartEndValue, int > Seed
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
void ReinjectSkippedSavingsStartingAt(int64_t node)
bool Contains(const T *val) const
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
int vehicles() const
Returns the number of vehicle routes in the model.
RoutingIndexPair IndexPair
SavingsFilteredHeuristic(RoutingModel *model, const RoutingIndexManager *manager, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(const std::vector< int > &vehicles)
Computes and returns the distance of each uninserted node to every vehicle in "vehicles" as a std::ve...
void ArrangeIndices(std::vector< int64_t > *indices)
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
static const int kUnassigned
NodeEntry(int node_to_insert, int insert_after, int vehicle)
#define DCHECK_EQ(val1, val2)
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
A Decision represents a choice point in the search tree.
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64_t(int64_t)> initializer)
The next few members are in the public section only for testing purposes.
#define DCHECK_LE(val1, val2)
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
Finds the best available vehicle of type "type" to start a new route to serve the arc before_node-->a...
virtual void SetVehicleIndex(int64_t node, int vehicle)
static constexpr Value PARALLEL_CHEAPEST_INSERTION
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
bool operator<(const NodeEntry &other) const
Collection of objects used to extend the Constraint Solver library.
std::pair< int, int > GetCompatibleVehicleOfType(int type, std::function< bool(int)> vehicle_is_compatible, std::function< bool(int)> stop_and_return_vehicle)
Searches for the best compatible vehicle of the given type, i.e.
const std::vector< T * > * Raw() const
int delivery_insert_after() const
void set_value(int64_t value)
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
SweepArranger(const std::vector< std::pair< int64_t, int64_t >> &points)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
int64_t Size() const
Returns the number of next variables in the model.
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
void set_vehicle(int vehicle)
virtual void BuildRoutesFromSavings()=0
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
virtual bool InitializeSolution()
Virtual method to initialize the solution.
What follows is relevant for models with time/state dependent transits.
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
const IntContainer & IntVarContainer() const
Assignment *const BuildSolution()
Builds a solution.
std::pair< int64_t, int64_t > ValuedPosition
void AppendEvaluatedPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int64_t vehicle, std::vector< ValuedPosition > *valued_positions)
Helper method to the ComputeEvaluatorSortedPositions* methods.
virtual int64_t Max() const =0
bool operator<(const PairEntry &other) const
#define DCHECK_LT(val1, val2)
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
int64_t number_of_rejects() const
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
int64_t Start(int vehicle) const
Model inspection.