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) {
172 return FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
174 if (has_single_vehicle_node) {
175 return FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC;
177 return FirstSolutionStrategy::PATH_CHEAPEST_ARC;
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();
915 const int64_t pickup = entry->pickup_to_insert();
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()) {
1089 const int64_t node_to_insert = node_entry->node_to_insert();
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_);
2541 Update(
true, 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;
2575 const int64_t before_node = savings_db_->GetBeforeNodeFromSaving(saving);
2576 const int64_t after_node = savings_db_->GetAfterNodeFromSaving(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)) {
2622 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(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,
2711 Saving* next_saving) {
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;
2718 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
2719 *next_saving = saving;
2720 found_saving =
true;
2722 costs_and_savings.pop_back();
2724 return found_saving;
2727 const SavingsFilteredHeuristic*
const savings_db_;
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_;
2754 SavingsFilteredHeuristic::SavingsFilteredHeuristic(
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]) {
2817 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
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] =
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 =
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);
const std::vector< IntVar * > vars_
#define DCHECK_LE(val1, val2)
#define DCHECK_NE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
const std::vector< T * > * Raw() const
void NoteChangedPriority(T *val)
bool Contains(const T *val) const
E * AddAtPosition(V *var, int position)
Advanced usage: Adds element at a given position; position has to have been allocated with Assignment...
const E & Element(const V *const var) const
void Resize(size_t size)
Advanced usage: Resizes the container, potentially adding elements with null variables.
An Assignment is a variable -> domains mapping, used to report solutions to the user.
IntContainer * MutableIntVarContainer()
const IntContainer & IntVarContainer() const
void SetValue(const IntVar *const var, int64_t value)
int64_t Value(const IntVar *const var) const
IntVarElement * Add(IntVar *const var)
Filtered-base decision builder based on the addition heuristic, extending a path from its start node ...
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, 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 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.
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
void InitializePriorityQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, Queue *priority_queue)
Initializes the priority_queue by inserting the best entry corresponding to each node,...
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
Returns the cost of inserting 'node_to_insert' between 'insert_after' and 'insert_before' on the 'veh...
std::pair< int64_t, int64_t > ValuedPosition
int64_t GetUnperformedValue(int64_t node_to_insert) const
Returns the cost of unperforming node 'node_to_insert'.
std::function< int64_t(int64_t)> penalty_evaluator_
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor)
Inserts 'node' just after 'predecessor', and just before 'successor', resulting in the following subs...
std::pair< StartEndValue, int > Seed
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
ChristofidesFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
std::vector< NodeIndex > TravelingSalesmanPath()
void SetMatchingAlgorithm(MatchingAlgorithm matching)
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
A DecisionBuilder is responsible for creating the search tree.
A Decision represents a choice point in the search tree.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
bool operator<(const NodeEntry &other) const
void set_vehicle(int vehicle)
void set_insert_after(int insert_after)
NodeEntry(int node_to_insert, int insert_after, int vehicle)
void set_value(int64_t value)
int node_to_insert() const
bool operator<(const PairEntry &other) const
void set_vehicle(int vehicle)
PairEntry(int pickup_to_insert, int pickup_insert_after, int delivery_to_insert, int delivery_insert_after, int vehicle)
int pickup_insert_after() const
void set_pickup_insert_after(int pickup_insert_after)
int delivery_insert_after() const
int delivery_to_insert() const
void set_value(int64_t value)
int pickup_to_insert() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
Utility class to encapsulate an IntVarIterator and use it in a range-based loop.
virtual int64_t Min() const =0
virtual int64_t Max() const =0
Decision * Next(Solver *solver) override
This is the main method of the decision builder class.
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
std::string DebugString() const override
int64_t number_of_rejects() const
Generic filter-based heuristic applied to IntVars.
void SetValue(int64_t index, int64_t value)
Modifies the current solution by setting the variable of index 'index' to value 'value'.
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
int Size() const
Returns the number of variables the decision builder is trying to instantiate.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
bool Commit()
Commits the modifications to the current solution if these modifications are "filter-feasible",...
Assignment *const assignment_
void ResetSolution()
Resets the data members for a new solution.
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
virtual bool InitializeSolution()
Virtual method to initialize the solution.
int64_t Value(int64_t index) const
Returns the value of the variable of index 'index' in the last committed solution.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, LocalSearchFilterManager *filter_manager)
Assignment *const BuildSolution()
Builds a solution.
The class IntVar is a subset of IntExpr.
virtual bool Contains(int64_t v) const =0
This method returns whether the value 'v' is in the domain of the variable.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
Filter manager: when a move is made, filters are executed to decide whether the solution is feasible ...
bool Accept(LocalSearchMonitor *const monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Returns true iff all filters return true, and the sum of their accepted objectives is between objecti...
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
The base class for all local search operators.
virtual bool MakeNextNeighbor(Assignment *delta, Assignment *deltadelta)=0
virtual void Start(const Assignment *assignment)=0
virtual int64_t RangeMinArgument(int64_t from, int64_t to) const =0
Dimensions represent quantities accumulated at nodes along the routes.
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
int64_t ShortestTransitionSlack(int64_t node) const
It makes sense to use the function only for self-dependent dimension.
IntVar * CumulVar(int64_t index) const
Get the cumul, transit and slack variables for the given node (given as int64_t var index).
Filter-based heuristic dedicated to routing.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
RoutingModel * model() const
virtual void SetVehicleIndex(int64_t node, int vehicle)
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
void MakeDisjunctionNodesUnperformed(int64_t node)
Make nodes in the same disjunction as 'node' unperformed.
bool StopSearch() override
Returns true if the search must be stopped.
void MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed.
virtual void ResetVehicleIndices()
void MakePartiallyPerformedPairsUnperformed()
Make all partially performed pickup and delivery pairs unperformed.
bool VehicleIsEmpty(int vehicle) const
const Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
RoutingFilteredHeuristic(RoutingModel *model, LocalSearchFilterManager *filter_manager)
Manager for any NodeIndex <-> variable index conversion.
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(int64_t index, int64_t max_cardinality, F f) const
Calls f for each variable index of indices in the same disjunctions as the node corresponding to the ...
RoutingIndexPair IndexPair
int64_t GetFixedCostOfVehicle(int vehicle) const
Returns the route fixed cost taken into account if the route of the vehicle is not empty,...
const std::vector< std::pair< int, int > > & GetDeliveryIndexPairs(int64_t node_index) const
Same as above for deliveries.
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< IntVar * > variables)
Perhaps move it to constraint_solver.h.
IntVar * VehicleVar(int64_t index) const
Returns the vehicle variable of the node corresponding to index.
int64_t Size() const
Returns the number of next variables in the model.
RoutingIndexPairs IndexPairs
const IndexPairs & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
int64_t Start(int vehicle) const
Model inspection.
int vehicles() const
Returns the number of vehicle routes in the model.
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64_t(int64_t)> initializer)
The next few members are in the public section only for testing purposes.
IntVar * CostVar() const
Returns the global cost variable which is being minimized.
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
Returns the cost of the segment between two nodes for a given cost class.
const std::vector< std::pair< int, int > > & GetPickupIndexPairs(int64_t node_index) const
Returns pairs for which the node is a pickup; the first element of each pair is the index in the pick...
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
SWIG
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
void ReinjectSkippedSavingsEndingAt(int64_t node)
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
void UpdateWithType(int type)
void ReinjectSkippedSavingsStartingAt(int64_t node)
void InitializeContainer(int64_t size, int64_t saving_neighbors)
void Update(bool update_best_saving, int type=-1)
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
Filter-based decision builder which builds a solution by using Clarke & Wright's Savings heuristic.
int64_t GetVehicleTypeFromSaving(const Saving &saving) const
Returns the cost class from a saving.
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetAfterNodeFromSaving(const Saving &saving) const
Returns the "after node" from a saving.
int64_t GetSavingValue(const Saving &saving) const
Returns the saving value from a saving.
std::pair< int64_t, int64_t > Saving
std::unique_ptr< SavingsContainer< Saving > > savings_container_
~SavingsFilteredHeuristic() override
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int64_t GetBeforeNodeFromSaving(const Saving &saving) const
Returns the "before node" from a saving.
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
Finds the best available vehicle of type "type" to start a new route to serve the arc before_node-->a...
virtual void BuildRoutesFromSavings()=0
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
void ArrangeIndices(std::vector< int64_t > *indices)
SweepArranger(const std::vector< std::pair< int64_t, int64_t >> &points)
void Update(const std::function< bool(int)> &remove_vehicle)
Goes through all the currently stored vehicles and removes vehicles for which remove_vehicle() return...
std::pair< int, int > GetCompatibleVehicleOfType(int type, std::function< bool(int)> vehicle_is_compatible, std::function< bool(int)> stop_and_return_vehicle)
Searches for the best compatible vehicle of the given type, i.e.
void Reset(const std::function< bool(int)> &store_vehicle)
Resets the vehicles stored, storing only vehicles from the vehicle_type_container_ for which store_ve...
const std::vector< IntVar * > cumuls_
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
void STLClearObject(T *obj)
bool ContainsKey(const Collection &collection, const Key &key)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
int64_t CapSub(int64_t x, int64_t y)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Returns the best value for the automatic first solution strategy, based on the given model parameters...
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
static const int kUnassigned
std::pair< int, int > link
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
std::function< int64_t(int64_t, int64_t)> evaluator_
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
double farthest_seeds_ratio
The ratio of routes on which to insert farthest nodes as seeds before starting the cheapest insertion...
bool use_neighbors_ratio_for_initialization
If true, only closest neighbors (see neighbors_ratio and min_neighbors) are considered as insertion p...
bool add_unperformed_entries
If true, entries are created for making the nodes/pairs unperformed, and when the cost of making a no...
What follows is relevant for models with time/state dependent transits.
RangeMinMaxIndexFunction * transit_plus_identity
f(x)
std::vector< std::set< VehicleClassEntry > > sorted_vehicle_classes_per_type
std::vector< std::deque< int > > vehicles_per_vehicle_class
double neighbors_ratio
If neighbors_ratio < 1 then for each node only this ratio of its neighbors leading to the smallest ar...
double arc_coefficient
arc_coefficient is a strictly positive parameter indicating the coefficient of the arc being consider...
double max_memory_usage_bytes
The number of neighbors considered for each node is also adapted so that the stored Savings don't use...
bool add_reverse_arcs
If add_reverse_arcs is true, the neighborhood relationships are considered symmetrically.