37#include "absl/algorithm/container.h"
38#include "absl/base/attributes.h"
39#include "absl/container/flat_hash_map.h"
40#include "absl/container/flat_hash_set.h"
41#include "absl/flags/flag.h"
42#include "absl/log/check.h"
43#include "absl/log/die_if_null.h"
44#include "absl/strings/str_cat.h"
45#include "absl/strings/string_view.h"
54#include "ortools/constraint_solver/routing_enums.pb.h"
55#include "ortools/constraint_solver/routing_parameters.pb.h"
64class LocalSearchPhaseParameters;
67ABSL_FLAG(
bool, routing_shift_insertion_cost_by_penalty,
true,
68 "Shift insertion costs by the penalty of the inserted node(s).");
71 "The number of sectors the space is divided into before it is sweeped"
79 const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
80 vehicle_type_container_->sorted_vehicle_classes_per_type;
81 sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
82 const std::vector<std::deque<int>>& all_vehicles_per_class =
83 vehicle_type_container_->vehicles_per_vehicle_class;
84 vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
86 for (
int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
87 std::set<VehicleClassEntry>& stored_class_entries =
88 sorted_vehicle_classes_per_type_[type];
89 stored_class_entries.clear();
90 for (VehicleClassEntry class_entry : all_vehicle_classes_per_type[type]) {
92 std::vector<int>& stored_vehicles =
94 stored_vehicles.clear();
96 if (store_vehicle(vehicle)) {
97 stored_vehicles.push_back(vehicle);
100 if (!stored_vehicles.empty()) {
101 stored_class_entries.insert(class_entry);
108 const std::function<
bool(
int)>& remove_vehicle) {
109 for (std::set<VehicleClassEntry>& class_entries :
110 sorted_vehicle_classes_per_type_) {
111 auto class_entry_it = class_entries.begin();
112 while (class_entry_it != class_entries.end()) {
114 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
115 vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
116 [&remove_vehicle](
int vehicle) {
117 return remove_vehicle(vehicle);
120 if (vehicles.empty()) {
121 class_entry_it = class_entries.erase(class_entry_it);
130 int type,
const std::function<
bool(
int)>& vehicle_is_compatible)
const {
131 for (
const VehicleClassEntry& vehicle_class_entry :
132 sorted_vehicle_classes_per_type_[type]) {
134 vehicles_per_vehicle_class_[vehicle_class_entry.vehicle_class]) {
135 if (vehicle_is_compatible(vehicle))
return true;
142 int type,
const std::function<
bool(
int)>& vehicle_is_compatible,
143 const std::function<
bool(
int)>& stop_and_return_vehicle) {
144 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
145 sorted_vehicle_classes_per_type_[type];
146 auto vehicle_class_it = sorted_classes.begin();
148 while (vehicle_class_it != sorted_classes.end()) {
150 std::vector<int>& vehicles = vehicles_per_vehicle_class_[
vehicle_class];
151 DCHECK(!vehicles.empty());
153 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
155 const int vehicle = *vehicle_it;
156 if (vehicle_is_compatible(vehicle)) {
157 vehicles.erase(vehicle_it);
158 if (vehicles.empty()) {
159 sorted_classes.erase(vehicle_class_it);
161 return {vehicle, -1};
163 if (stop_and_return_vehicle(vehicle)) {
164 return {-1, vehicle};
183 bool has_pickup_deliveries,
bool has_node_precedences,
184 bool has_single_vehicle_node) {
185 if (has_pickup_deliveries || has_node_precedences) {
186 return FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
188 if (has_single_vehicle_node) {
189 return FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC;
191 return FirstSolutionStrategy::PATH_CHEAPEST_ARC;
196 const int num_vehicles =
model.vehicles();
204 std::vector<int64_t> starts(
size + num_vehicles, -1);
205 std::vector<int64_t> ends(
size + num_vehicles, -1);
206 for (
int node = 0; node <
size + num_vehicles; ++node) {
211 std::vector<bool> touched(
size,
false);
212 for (
int node = 0; node <
size; ++node) {
214 while (!
model.IsEnd(current) && !touched[current]) {
215 touched[current] =
true;
217 if (next_var->
Bound()) {
218 current = next_var->
Value();
223 starts[ends[current]] = starts[node];
224 ends[starts[node]] = ends[current];
228 std::vector<int64_t> end_chain_starts(num_vehicles);
229 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
230 end_chain_starts[vehicle] = starts[
model.End(vehicle)];
232 return end_chain_starts;
240 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
241 : heuristic_(
std::move(heuristic)) {}
244 Assignment*
const assignment = heuristic_->BuildSolution();
245 if (assignment !=
nullptr) {
246 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
247 VLOG(2) <<
"Number of rejected decisions: "
248 << heuristic_->number_of_rejects();
257 return heuristic_->number_of_decisions();
261 return heuristic_->number_of_rejects();
265 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
266 heuristic_->DebugString(),
")");
274 Solver* solver,
const std::vector<IntVar*>& vars,
275 const std::vector<IntVar*>& secondary_vars,
277 : assignment_(solver->MakeAssignment()),
280 base_vars_size_(vars.
size()),
281 delta_(solver->MakeAssignment()),
282 empty_(solver->MakeAssignment()),
283 filter_manager_(filter_manager),
284 objective_upper_bound_(
std::numeric_limits<int64_t>::
max()),
285 number_of_decisions_(0),
286 number_of_rejects_(0) {
287 if (!secondary_vars.empty()) {
288 vars_.insert(vars_.end(), secondary_vars.begin(), secondary_vars.end());
291 is_in_delta_.resize(vars_.size(),
false);
292 delta_indices_.reserve(vars_.size());
296 number_of_decisions_ = 0;
297 number_of_rejects_ = 0;
319 const std::function<int64_t(int64_t)>& next_accessor) {
326 if (!InitializeSolution()) {
330 for (
int v = 0; v < model_->vehicles(); v++) {
331 int64_t node = model_->Start(v);
332 while (!model_->IsEnd(node)) {
333 const int64_t
next = next_accessor(node);
334 DCHECK_NE(
next, node);
351 ++number_of_decisions_;
352 const bool accept = FilterAccept();
354 if (filter_manager_ !=
nullptr) {
363 objective_upper_bound_);
369 const int delta_size = delta_container.
Size();
372 for (
int i = 0; i < delta_size; ++i) {
375 DCHECK_EQ(
var, vars_[delta_indices_[i]]);
382 ++number_of_rejects_;
385 for (
const int delta_index : delta_indices_) {
386 is_in_delta_[delta_index] =
false;
389 delta_indices_.clear();
390 return accept ? std::optional<int64_t>{objective_upper_bound_} : std::nullopt;
396 objective_upper_bound_ = std::numeric_limits<int64_t>::max();
399bool IntVarFilteredHeuristic::FilterAccept() {
400 if (!filter_manager_)
return true;
402 return filter_manager_->
Accept(monitor, delta_, empty_,
403 std::numeric_limits<int64_t>::min(),
404 objective_upper_bound_);
410 RoutingModel*
model, std::function<
bool()> stop_search,
413 model->CostsAreHomogeneousAcrossVehicles()
415 :
model->VehicleVars(),
418 stop_search_(
std::move(stop_search)) {}
420bool RoutingFilteredHeuristic::InitializeSolution() {
425 start_chain_ends_.resize(
model()->vehicles());
426 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
427 int64_t node =
model()->Start(vehicle);
428 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
434 start_chain_ends_[vehicle] = node;
441 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
442 int64_t node = start_chain_ends_[vehicle];
443 if (!
model()->IsEnd(node)) {
444 int64_t
next = end_chain_starts_[vehicle];
448 while (!
model()->IsEnd(node)) {
465 model()->ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(
466 node, 1, [
this, node](
int alternate) {
467 if (node != alternate && !
Contains(alternate)) {
468 SetNext(alternate, alternate, -1);
486 const int num_nexts =
model()->Nexts().size();
487 std::vector<bool> to_make_unperformed(num_nexts,
false);
488 for (
const auto& [pickups, deliveries] :
489 model()->GetPickupAndDeliveryPairs()) {
490 int64_t performed_pickup = -1;
491 for (int64_t pickup : pickups) {
493 performed_pickup = pickup;
497 int64_t performed_delivery = -1;
498 for (int64_t delivery : deliveries) {
500 performed_delivery = delivery;
504 if ((performed_pickup == -1) != (performed_delivery == -1)) {
505 if (performed_pickup != -1) {
506 to_make_unperformed[performed_pickup] =
true;
508 if (performed_delivery != -1) {
509 to_make_unperformed[performed_delivery] =
true;
518 while (
next < num_nexts && to_make_unperformed[
next]) {
519 const int64_t next_of_next =
Value(
next);
530 RoutingModel*
model, std::function<
bool()> stop_search,
531 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
532 std::function<int64_t(int64_t)> penalty_evaluator,
535 evaluator_(
std::move(evaluator)),
536 penalty_evaluator_(
std::move(penalty_evaluator)) {}
539void ProcessVehicleStartEndCosts(
540 const RoutingModel&
model,
int node,
541 const std::function<
void(int64_t,
int)>& process_cost,
543 bool ignore_end =
false) {
544 const auto start_end_cost = [&
model, ignore_start, ignore_end](int64_t node,
546 const int64_t start_cost =
547 ignore_start ? 0 :
model.GetArcCostForVehicle(
model.Start(v), node, v);
548 const int64_t end_cost =
549 ignore_end ? 0 :
model.GetArcCostForVehicle(
model.End(v), node, v);
550 return CapAdd(start_cost, end_cost);
556 const IntVar*
const vehicle_var =
model.VehicleVar(node);
557 if (vehicle_var->Size() < vehicle_set.
size()) {
558 std::unique_ptr<IntVarIterator> it(vehicle_var->MakeDomainIterator(
false));
559 for (
const int v : InitAndGetValues(it.get())) {
560 if (v < 0 || !vehicle_set[v]) {
563 process_cost(start_end_cost(node, v), v);
566 for (
const int v : vehicle_set) {
567 if (!vehicle_var->Contains(v))
continue;
568 process_cost(start_end_cost(node, v), v);
574std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
576 const std::vector<int>& vehicles) {
579 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
583 for (
int v : vehicles) vehicle_set.
Set(v);
585 for (
int node = 0; node <
model.Size(); node++) {
587 std::vector<StartEndValue>& start_end_distances =
588 start_end_distances_per_node[node];
589 start_end_distances.reserve(
590 std::min(
model.VehicleVar(node)->Size(),
591 static_cast<uint64_t
>(vehicles.size())));
593 ProcessVehicleStartEndCosts(
595 [&start_end_distances](int64_t dist,
int v) {
596 start_end_distances.push_back({dist, v});
602 absl::c_sort(start_end_distances,
604 return second < first;
607 return start_end_distances_per_node;
611 int node, std::vector<StartEndValue>* start_end_distances,
SeedQueue* sq) {
612 if (start_end_distances->empty()) {
617 StartEndValue& start_end_value = start_end_distances->back();
621 const uint64_t num_allowed_vehicles =
model()->VehicleVar(node)->Size();
622 const int64_t neg_penalty =
CapOpp(
model()->UnperformedPenalty(node));
624 {num_allowed_vehicles, neg_penalty, start_end_value,
true, node});
625 start_end_distances->pop_back();
629 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
631 const int num_nodes =
model()->Size();
632 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
634 for (
int node = 0; node < num_nodes; node++) {
655 int64_t node_to_insert, int64_t
start, int64_t next_after_start,
656 int vehicle,
bool ignore_cost,
657 std::vector<NodeInsertion>* node_insertions) {
658 DCHECK(node_insertions !=
nullptr);
659 int64_t insert_after =
start;
660 if (!
model()->VehicleVar(node_to_insert)->
Contains(vehicle))
return;
661 while (!
model()->IsEnd(insert_after)) {
662 const int64_t insert_before =
663 (insert_after ==
start) ? next_after_start :
Value(insert_after);
665 InsertBetween(node_to_insert, insert_after, insert_before, vehicle);
666 std::optional<int64_t> insertion_cost =
Evaluate(
false);
667 if (insertion_cost.has_value()) {
668 node_insertions->push_back({insert_after, vehicle, *insertion_cost});
671 node_insertions->push_back(
672 {insert_after, vehicle,
676 insert_before, vehicle)});
678 insert_after = insert_before;
683 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
687 evaluator_(node_to_insert, insert_before, vehicle)),
688 evaluator_(insert_after, insert_before, vehicle));
692 int64_t node_to_insert)
const {
696 return std::numeric_limits<int64_t>::max();
703 RoutingModel*
model, std::function<
bool()> stop_search,
704 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
705 std::function<int64_t(int64_t)> penalty_evaluator,
709 model,
std::move(stop_search),
std::move(evaluator),
710 std::move(penalty_evaluator), filter_manager),
712 node_index_to_vehicle_(
model->Size(), -1),
713 node_index_to_neighbors_by_cost_class_(nullptr),
714 empty_vehicle_type_curator_(nullptr) {
720bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
721 std::vector<bool> node_is_visited(
model()->Size(),
false);
722 for (
int v = 0; v <
model()->vehicles(); v++) {
723 for (
int node =
model()->Start(v); !
model()->IsEnd(node);
724 node =
Value(node)) {
725 if (node_index_to_vehicle_[node] != v) {
728 node_is_visited[node] =
true;
732 for (
int node = 0; node <
model()->Size(); node++) {
733 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
743 double neighbors_ratio_used = 1;
744 node_index_to_neighbors_by_cost_class_ =
747 neighbors_ratio_used);
748 if (neighbors_ratio_used == 1) {
752 if (empty_vehicle_type_curator_ ==
nullptr) {
753 empty_vehicle_type_curator_ = std::make_unique<VehicleTypeCurator>(
754 model()->GetVehicleTypeContainer());
757 empty_vehicle_type_curator_->Reset(
760 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
761 model()->GetPickupAndDeliveryPairs();
762 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
763 absl::flat_hash_map<int, std::map<int64_t, std::vector<int>>>
764 vehicle_to_pair_nodes;
767 pickup_delivery_pairs[
index];
768 const auto& [pickups, deliveries] = pickup_delivery_pair;
769 int pickup_vehicle = -1;
770 for (int64_t pickup : pickups) {
772 pickup_vehicle = node_index_to_vehicle_[pickup];
776 int delivery_vehicle = -1;
777 for (int64_t delivery : deliveries) {
779 delivery_vehicle = node_index_to_vehicle_[delivery];
783 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
784 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pair)]
787 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
788 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle][1];
789 for (int64_t delivery : deliveries) {
790 pair_nodes.push_back(delivery);
793 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
794 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle][1];
795 for (int64_t pickup : pickups) {
796 pair_nodes.push_back(pickup);
801 const auto unperform_unassigned_and_check = [
this]() {
805 for (
const auto& [vehicle,
nodes] : vehicle_to_pair_nodes) {
806 if (!InsertNodesOnRoutes(
nodes, {vehicle})) {
807 return unperform_unassigned_and_check();
811 if (!InsertPairsAndNodesByRequirementTopologicalOrder()) {
812 return unperform_unassigned_and_check();
817 if (!InsertPairs(pairs_to_insert_by_bucket)) {
818 return unperform_unassigned_and_check();
820 std::map<int64_t, std::vector<int>> nodes_by_bucket;
821 for (
int node = 0; node <
model()->Size(); ++node) {
823 !
model()->IsDelivery(node)) {
824 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
827 InsertFarthestNodesAsSeeds();
829 if (!SequentialInsertNodes(nodes_by_bucket)) {
830 return unperform_unassigned_and_check();
832 }
else if (!InsertNodesOnRoutes(nodes_by_bucket, {})) {
833 return unperform_unassigned_and_check();
835 DCHECK(CheckVehicleIndices());
836 return unperform_unassigned_and_check();
839bool GlobalCheapestInsertionFilteredHeuristic::
840 InsertPairsAndNodesByRequirementTopologicalOrder() {
841 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
842 model()->GetPickupAndDeliveryPairs();
843 for (
const std::vector<int>& types :
844 model()->GetTopologicallySortedVisitTypes()) {
845 for (
int type : types) {
846 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
847 for (
int index :
model()->GetPairIndicesOfType(type)) {
848 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pairs[
index])]
851 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
852 std::map<int64_t, std::vector<int>> nodes_by_bucket;
853 for (
int node :
model()->GetSingleNodesOfType(type)) {
854 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
856 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
862bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
863 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket) {
865 std::vector<PairEntries> pickup_to_entries;
866 std::vector<PairEntries> delivery_to_entries;
867 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
868 model()->GetPickupAndDeliveryPairs();
869 auto pair_is_performed = [
this, &pickup_delivery_pairs](
int pair_index) {
870 const auto& [pickups, deliveries] = pickup_delivery_pairs[pair_index];
871 for (int64_t pickup : pickups) {
874 for (int64_t delivery : deliveries) {
875 if (
Contains(delivery))
return true;
879 absl::flat_hash_set<int> pair_indices_to_insert;
880 for (
const auto& [bucket, pair_indices] : pair_indices_by_bucket) {
881 for (
const int pair_index : pair_indices) {
882 if (!pair_is_performed(pair_index)) {
883 pair_indices_to_insert.insert(pair_index);
886 if (!InitializePairPositions(pair_indices_to_insert, &priority_queue,
887 &pickup_to_entries, &delivery_to_entries)) {
890 while (!priority_queue.
IsEmpty()) {
891 if (StopSearchAndCleanup(&priority_queue)) {
894 PairEntry*
const entry = priority_queue.
Top();
895 const int64_t pickup = entry->pickup_to_insert();
896 const int64_t delivery = entry->delivery_to_insert();
898 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
899 &delivery_to_entries);
903 const int entry_vehicle = entry->vehicle();
904 if (entry_vehicle == -1) {
907 SetNext(delivery, delivery, -1);
909 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
910 &delivery_to_entries);
916 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle)) {
917 if (!InsertPairEntryUsingEmptyVehicleTypeCurator(
918 pair_indices_to_insert, entry, &priority_queue,
919 &pickup_to_entries, &delivery_to_entries)) {
927 const int64_t pickup_insert_after = entry->pickup_insert_after();
928 const int64_t pickup_insert_before =
Value(pickup_insert_after);
929 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
931 const int64_t delivery_insert_after = entry->delivery_insert_after();
932 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
933 ? pickup_insert_before
934 :
Value(delivery_insert_after);
935 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
937 if (!UpdateAfterPairInsertion(
938 pair_indices_to_insert, entry_vehicle, pickup,
939 pickup_insert_after, delivery, delivery_insert_after,
940 &priority_queue, &pickup_to_entries, &delivery_to_entries)) {
944 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
945 &delivery_to_entries);
950 for (
auto it = pair_indices_to_insert.begin(),
951 last = pair_indices_to_insert.end();
953 if (pair_is_performed(*it)) {
954 pair_indices_to_insert.erase(it++);
963bool GlobalCheapestInsertionFilteredHeuristic::
964 InsertPairEntryUsingEmptyVehicleTypeCurator(
965 const absl::flat_hash_set<int>& pair_indices,
966 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
968 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
970 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
972 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
973 delivery_to_entries) {
974 const int entry_vehicle = pair_entry->vehicle();
975 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle));
981 const int64_t pickup = pair_entry->pickup_to_insert();
982 const int64_t delivery = pair_entry->delivery_to_insert();
983 const int64_t entry_fixed_cost =
984 model()->GetFixedCostOfVehicle(entry_vehicle);
985 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
986 delivery](
int vehicle) {
987 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
992 const int64_t
end =
model()->End(vehicle);
1001 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1002 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1004 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1005 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1006 empty_vehicle_type_curator_->Type(entry_vehicle),
1007 vehicle_is_compatible, stop_and_return_vehicle);
1008 if (compatible_vehicle >= 0) {
1010 const int64_t vehicle_start =
model()->Start(compatible_vehicle);
1011 const int num_previous_vehicle_entries =
1012 pickup_to_entries->at(vehicle_start).size() +
1013 delivery_to_entries->at(vehicle_start).size();
1014 if (!UpdateAfterPairInsertion(
1015 pair_indices, compatible_vehicle, pickup, vehicle_start, delivery,
1016 pickup, priority_queue, pickup_to_entries, delivery_to_entries)) {
1019 if (compatible_vehicle != entry_vehicle) {
1026 num_previous_vehicle_entries == 0 ||
1027 model()->GetVehicleClassIndexOfVehicle(compatible_vehicle).
value() !=
1028 model()->GetVehicleClassIndexOfVehicle(entry_vehicle).
value());
1034 const int new_empty_vehicle =
1035 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1036 empty_vehicle_type_curator_->Type(compatible_vehicle));
1038 if (new_empty_vehicle >= 0) {
1045 const int64_t new_empty_vehicle_start =
model()->Start(new_empty_vehicle);
1046 const std::vector<PairEntry*> to_remove(
1047 pickup_to_entries->at(new_empty_vehicle_start).begin(),
1048 pickup_to_entries->at(new_empty_vehicle_start).end());
1049 for (PairEntry* entry : to_remove) {
1050 DeletePairEntry(entry, priority_queue, pickup_to_entries,
1051 delivery_to_entries);
1053 if (!AddPairEntriesWithPickupAfter(
1054 pair_indices, new_empty_vehicle, new_empty_vehicle_start,
1056 pickup_to_entries, delivery_to_entries)) {
1060 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1067 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1068 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1069 pair_entry->set_pickup_insert_after(
1070 model()->Start(next_fixed_cost_empty_vehicle));
1071 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1072 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1073 UpdatePairEntry(pair_entry, priority_queue);
1075 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1076 delivery_to_entries);
1106 : entries_(num_nodes), touched_entries_(num_nodes) {}
1108 priority_queue_.
Clear();
1109 for (Entries& entries : entries_) entries.Clear();
1113 return priority_queue_.
IsEmpty() &&
1117 return insert_after >= entries_.size() ||
1118 entries_[insert_after].entries.empty();
1123 SortInsertions(&entries_[touched]);
1126 DCHECK(!priority_queue_.
IsEmpty());
1127 Entries* entries = priority_queue_.
Top();
1128 DCHECK(!entries->entries.empty());
1129 return entries->Top();
1134 Entries* top = priority_queue_.
Top();
1135 if (top->IncrementTop()) {
1138 priority_queue_.
Remove(top);
1143 if (
IsEmpty(insert_after))
return;
1144 Entries& entries = entries_[insert_after];
1145 if (priority_queue_.
Contains(&entries)) {
1146 priority_queue_.
Remove(&entries);
1151 int bucket, int64_t
value) {
1152 entries_[insert_after].entries.push_back(
1153 {
value, node, insert_after, vehicle, bucket});
1154 touched_entries_.
Set(insert_after);
1159 bool operator<(
const Entries& other)
const {
1160 DCHECK(!entries.empty());
1161 DCHECK(!other.entries.empty());
1162 return other.entries[other.top] < entries[top];
1169 void SetHeapIndex(
int index) { heap_index =
index; }
1170 int GetHeapIndex()
const {
return heap_index; }
1171 bool IncrementTop() {
1173 return top < entries.size();
1175 Entry* Top() {
return &entries[top]; }
1177 std::vector<Entry> entries;
1179 int heap_index = -1;
1182 void SortInsertions(Entries* entries) {
1184 if (entries->entries.empty())
return;
1185 absl::c_sort(entries->entries);
1186 if (!priority_queue_.
Contains(entries)) {
1187 priority_queue_.
Add(entries);
1194 std::vector<Entries> entries_;
1195 SparseBitset<int> touched_entries_;
1198bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1199 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
1200 const absl::flat_hash_set<int>& vehicles) {
1201 NodeEntryQueue queue(
model()->Nexts().
size());
1202 std::vector<bool> nodes_to_insert(
model()->Size(),
false);
1203 for (
const auto& [bucket,
nodes] : nodes_by_bucket) {
1204 for (
int node :
nodes) nodes_to_insert[node] =
true;
1205 if (!InitializePositions(nodes_to_insert, vehicles, &queue)) {
1214 const bool all_vehicles =
1215 vehicles.empty() || vehicles.size() ==
model()->vehicles();
1217 while (!queue.IsEmpty()) {
1218 const NodeEntryQueue::Entry* node_entry = queue.Top();
1220 const int64_t node_to_insert = node_entry->node_to_insert;
1226 const int entry_vehicle = node_entry->vehicle;
1227 if (entry_vehicle == -1) {
1228 DCHECK(all_vehicles);
1230 SetNext(node_to_insert, node_to_insert, -1);
1238 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles)) {
1239 DCHECK(all_vehicles);
1240 if (!InsertNodeEntryUsingEmptyVehicleTypeCurator(
1241 nodes_to_insert, all_vehicles, &queue)) {
1247 const int64_t insert_after = node_entry->insert_after;
1250 if (!UpdateAfterNodeInsertion(nodes_to_insert, entry_vehicle,
1251 node_to_insert, insert_after,
1252 all_vehicles, &queue)) {
1261 for (
int node = 0; node < nodes_to_insert.size(); ++node) {
1262 if (
Contains(node)) nodes_to_insert[node] =
false;
1268bool GlobalCheapestInsertionFilteredHeuristic::
1269 InsertNodeEntryUsingEmptyVehicleTypeCurator(
const std::vector<bool>&
nodes,
1271 NodeEntryQueue* queue) {
1272 const NodeEntryQueue::Entry* node_entry = queue->Top();
1273 const int entry_vehicle = node_entry->vehicle;
1274 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles));
1281 const int64_t node_to_insert = node_entry->node_to_insert;
1282 const int bucket = node_entry->bucket;
1283 const int64_t entry_fixed_cost =
1284 model()->GetFixedCostOfVehicle(entry_vehicle);
1285 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1286 node_to_insert](
int vehicle) {
1287 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1293 model()->End(vehicle), vehicle);
1300 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1301 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1303 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1304 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1305 empty_vehicle_type_curator_->Type(entry_vehicle),
1306 vehicle_is_compatible, stop_and_return_vehicle);
1307 if (compatible_vehicle >= 0) {
1309 const int64_t compatible_start =
model()->Start(compatible_vehicle);
1310 const bool no_prior_entries_for_this_vehicle =
1311 queue->IsEmpty(compatible_start);
1312 if (!UpdateAfterNodeInsertion(
nodes, compatible_vehicle, node_to_insert,
1313 compatible_start, all_vehicles, queue)) {
1316 if (compatible_vehicle != entry_vehicle) {
1323 no_prior_entries_for_this_vehicle ||
1324 model()->GetVehicleClassIndexOfVehicle(compatible_vehicle).
value() !=
1325 model()->GetVehicleClassIndexOfVehicle(entry_vehicle).
value());
1331 const int new_empty_vehicle =
1332 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1333 empty_vehicle_type_curator_->Type(compatible_vehicle));
1335 if (new_empty_vehicle >= 0) {
1342 const int64_t new_empty_vehicle_start =
model()->Start(new_empty_vehicle);
1343 queue->ClearInsertions(new_empty_vehicle_start);
1344 if (!AddNodeEntriesAfter(
nodes, new_empty_vehicle,
1345 new_empty_vehicle_start, all_vehicles, queue)) {
1349 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1357 const int64_t insert_after =
model()->Start(next_fixed_cost_empty_vehicle);
1359 node_to_insert, insert_after,
Value(insert_after),
1360 next_fixed_cost_empty_vehicle);
1361 const int64_t penalty_shift =
1362 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1365 queue->PushInsertion(node_to_insert, insert_after,
1366 next_fixed_cost_empty_vehicle, bucket,
1367 CapSub(insertion_cost, penalty_shift));
1375bool GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1376 const std::map<int64_t, std::vector<int>>& nodes_by_bucket) {
1377 std::vector<bool> is_vehicle_used;
1378 absl::flat_hash_set<int> used_vehicles;
1379 std::vector<int> unused_vehicles;
1381 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1382 if (!used_vehicles.empty() &&
1383 !InsertNodesOnRoutes(nodes_by_bucket, used_vehicles)) {
1387 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1389 SeedQueue first_node_queue(
false);
1392 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1395 while (vehicle >= 0) {
1396 if (!InsertNodesOnRoutes(nodes_by_bucket, {vehicle})) {
1399 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1405void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1406 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1407 absl::flat_hash_set<int>* used_vehicles) {
1408 is_vehicle_used->clear();
1409 is_vehicle_used->resize(
model()->vehicles());
1411 used_vehicles->clear();
1412 used_vehicles->reserve(
model()->vehicles());
1414 unused_vehicles->clear();
1415 unused_vehicles->reserve(
model()->vehicles());
1417 for (
int vehicle = 0; vehicle <
model()->vehicles(); vehicle++) {
1419 (*is_vehicle_used)[vehicle] =
true;
1420 used_vehicles->insert(vehicle);
1422 (*is_vehicle_used)[vehicle] =
false;
1423 unused_vehicles->push_back(vehicle);
1428bool GlobalCheapestInsertionFilteredHeuristic::IsCheapestClassRepresentative(
1429 int vehicle)
const {
1433 const int curator_vehicle =
1434 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1435 empty_vehicle_type_curator_->Type(vehicle));
1436 if (curator_vehicle != vehicle &&
1437 model()->GetVehicleClassIndexOfVehicle(curator_vehicle).
value() ==
1438 model()->GetVehicleClassIndexOfVehicle(vehicle).
value()) {
1445void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1449 const int num_seeds =
static_cast<int>(
1452 std::vector<bool> is_vehicle_used;
1453 absl::flat_hash_set<int> used_vehicles;
1454 std::vector<int> unused_vehicles;
1455 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1456 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1461 SeedQueue farthest_node_queue(
true);
1464 int inserted_seeds = 0;
1465 while (inserted_seeds++ < num_seeds) {
1466 if (InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1467 &is_vehicle_used) < 0) {
1476 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1477 empty_vehicle_type_curator_->Update(
1481int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1482 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1483 SeedQueue* sq, std::vector<bool>* is_vehicle_used) {
1484 auto& priority_queue = sq->priority_queue;
1485 while (!priority_queue.empty()) {
1487 const Seed& seed = priority_queue.top();
1488 const int seed_node = seed.index;
1489 DCHECK(seed.is_node_index);
1490 const int seed_vehicle = seed.start_end_value.vehicle;
1491 priority_queue.pop();
1493 std::vector<StartEndValue>& other_start_end_values =
1494 (*start_end_distances_per_node)[seed_node];
1499 other_start_end_values.clear();
1502 if (!(*is_vehicle_used)[seed_vehicle]) {
1504 const int64_t
start =
model()->Start(seed_vehicle);
1505 const int64_t
end =
model()->End(seed_vehicle);
1509 (*is_vehicle_used)[seed_vehicle] =
true;
1510 other_start_end_values.clear();
1511 SetVehicleIndex(seed_node, seed_vehicle);
1512 return seed_vehicle;
1524bool GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1525 const absl::flat_hash_set<int>& pair_indices,
1527 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1528 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1530 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1531 delivery_to_entries) {
1532 priority_queue->
Clear();
1533 pickup_to_entries->clear();
1534 pickup_to_entries->resize(
model()->Size());
1535 delivery_to_entries->clear();
1536 delivery_to_entries->resize(
model()->Size());
1537 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1538 model()->GetPickupAndDeliveryPairs();
1539 for (
int index : pair_indices) {
1540 const auto& [pickups, deliveries] = pickup_delivery_pairs[
index];
1541 for (int64_t pickup : pickups) {
1543 for (int64_t delivery : deliveries) {
1545 if (StopSearchAndCleanup(priority_queue))
return false;
1551 deliveries.size() == 1 &&
1553 std::numeric_limits<int64_t>::max() &&
1555 std::numeric_limits<int64_t>::max()) {
1556 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1560 InitializeInsertionEntriesPerformingPair(
1561 pickup, delivery, priority_queue, pickup_to_entries,
1562 delivery_to_entries);
1569void GlobalCheapestInsertionFilteredHeuristic::
1570 InitializeInsertionEntriesPerformingPair(
1571 int64_t pickup, int64_t delivery,
1573 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1575 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1577 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1578 delivery_to_entries) {
1580 struct PairInsertion {
1581 int64_t insert_pickup_after;
1582 int64_t insert_delivery_after;
1585 std::vector<PairInsertion> pair_insertions;
1586 std::vector<NodeInsertion> pickup_insertions;
1587 std::vector<NodeInsertion> delivery_insertions;
1588 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
1589 if (!IsCheapestClassRepresentative(vehicle))
continue;
1590 const int64_t
start =
model()->Start(vehicle);
1591 pickup_insertions.clear();
1593 true, &pickup_insertions);
1594 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
1595 DCHECK(!
model()->IsEnd(pickup_insertion.insert_after));
1596 delivery_insertions.clear();
1598 delivery, pickup,
Value(pickup_insertion.insert_after), vehicle,
1599 true, &delivery_insertions);
1600 for (
const NodeInsertion& delivery_insertion : delivery_insertions) {
1601 pair_insertions.push_back({pickup_insertion.insert_after,
1602 delivery_insertion.insert_after, vehicle});
1606 for (
const auto& [insert_pickup_after, insert_delivery_after, vehicle] :
1608 DCHECK_NE(insert_pickup_after, insert_delivery_after);
1609 AddPairEntry(pickup, insert_pickup_after, delivery, insert_delivery_after,
1610 vehicle, priority_queue, pickup_to_entries,
1611 delivery_to_entries);
1618 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
1620 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1621 existing_insertion_positions;
1623 for (
const int64_t pickup_insert_after :
1624 node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass(
1625 cost_class, pickup)) {
1626 if (!
Contains(pickup_insert_after)) {
1629 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1631 model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
1635 if (!IsCheapestClassRepresentative(vehicle))
continue;
1637 int64_t delivery_insert_after = pickup;
1638 while (!
model()->IsEnd(delivery_insert_after)) {
1639 const std::pair<int64_t, int64_t> insertion_position = {
1640 pickup_insert_after, delivery_insert_after};
1641 DCHECK(!existing_insertion_positions.contains(insertion_position));
1642 existing_insertion_positions.insert(insertion_position);
1644 AddPairEntry(pickup, pickup_insert_after, delivery,
1645 delivery_insert_after, vehicle, priority_queue,
1646 pickup_to_entries, delivery_to_entries);
1647 delivery_insert_after = (delivery_insert_after == pickup)
1648 ?
Value(pickup_insert_after)
1649 :
Value(delivery_insert_after);
1654 for (
const int64_t delivery_insert_after :
1655 node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass(
1656 cost_class, delivery)) {
1657 if (!
Contains(delivery_insert_after)) {
1660 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1662 model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
1668 DCHECK_EQ(delivery_insert_after,
model()->Start(vehicle));
1671 int64_t pickup_insert_after =
model()->Start(vehicle);
1672 while (pickup_insert_after != delivery_insert_after) {
1673 if (!existing_insertion_positions.contains(
1674 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1675 AddPairEntry(pickup, pickup_insert_after, delivery,
1676 delivery_insert_after, vehicle, priority_queue,
1677 pickup_to_entries, delivery_to_entries);
1679 pickup_insert_after =
Value(pickup_insert_after);
1685bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1686 const absl::flat_hash_set<int>& pair_indices,
int vehicle, int64_t pickup,
1687 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1689 std::vector<PairEntries>* pickup_to_entries,
1690 std::vector<PairEntries>* delivery_to_entries) {
1693 const std::vector<PairEntry*> to_remove(
1694 delivery_to_entries->at(pickup).begin(),
1695 delivery_to_entries->at(pickup).end());
1696 for (PairEntry* pair_entry : to_remove) {
1697 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1698 delivery_to_entries);
1700 DCHECK(pickup_to_entries->at(pickup).empty());
1701 DCHECK(pickup_to_entries->at(delivery).empty());
1702 DCHECK(delivery_to_entries->at(pickup).empty());
1703 DCHECK(delivery_to_entries->at(delivery).empty());
1706 if (!UpdateExistingPairEntriesOnChain(pickup_position,
Value(pickup_position),
1707 priority_queue, pickup_to_entries,
1708 delivery_to_entries) ||
1709 !UpdateExistingPairEntriesOnChain(
1710 delivery_position,
Value(delivery_position), priority_queue,
1711 pickup_to_entries, delivery_to_entries)) {
1717 if (!AddPairEntriesAfter(pair_indices, vehicle, pickup,
1719 priority_queue, pickup_to_entries,
1720 delivery_to_entries) ||
1721 !AddPairEntriesAfter(pair_indices, vehicle, delivery,
1723 priority_queue, pickup_to_entries,
1724 delivery_to_entries)) {
1727 SetVehicleIndex(pickup, vehicle);
1728 SetVehicleIndex(delivery, vehicle);
1732bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingPairEntriesOnChain(
1733 int64_t insert_after_start, int64_t insert_after_end,
1735 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1736 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1738 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1739 delivery_to_entries) {
1740 int64_t insert_after = insert_after_start;
1741 while (insert_after != insert_after_end) {
1742 DCHECK(!
model()->IsEnd(insert_after));
1745 std::vector<PairEntry*> to_remove;
1746 for (
const PairEntries* pair_entries :
1747 {&pickup_to_entries->at(insert_after),
1748 &delivery_to_entries->at(insert_after)}) {
1749 if (StopSearchAndCleanup(priority_queue))
return false;
1750 for (PairEntry*
const pair_entry : *pair_entries) {
1751 DCHECK(priority_queue->
Contains(pair_entry));
1752 if (
Contains(pair_entry->pickup_to_insert()) ||
1753 Contains(pair_entry->delivery_to_insert())) {
1754 to_remove.push_back(pair_entry);
1756 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1757 .contains(pair_entry));
1758 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1759 .contains(pair_entry));
1760 UpdatePairEntry(pair_entry, priority_queue);
1764 for (PairEntry*
const pair_entry : to_remove) {
1765 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1766 delivery_to_entries);
1768 insert_after =
Value(insert_after);
1773bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithPickupAfter(
1774 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
1775 int64_t insert_after, int64_t skip_entries_inserting_delivery_after,
1777 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1778 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1780 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1781 delivery_to_entries) {
1782 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
1783 const int64_t pickup_insert_before =
Value(insert_after);
1784 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1785 model()->GetPickupAndDeliveryPairs();
1786 DCHECK(pickup_to_entries->at(insert_after).empty());
1787 for (
const int64_t pickup :
1788 node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass(
1789 cost_class, insert_after)) {
1790 if (StopSearchAndCleanup(priority_queue))
return false;
1794 for (
const auto& [pair_index, unused] :
1795 model()->GetPickupPositions(pickup)) {
1796 if (!pair_indices.contains(pair_index))
continue;
1797 for (
const int64_t delivery :
1798 pickup_delivery_pairs[pair_index].delivery_alternatives) {
1803 int64_t delivery_insert_after = pickup;
1804 while (!
model()->IsEnd(delivery_insert_after)) {
1805 if (delivery_insert_after != skip_entries_inserting_delivery_after) {
1806 AddPairEntry(pickup, insert_after, delivery, delivery_insert_after,
1807 vehicle, priority_queue, pickup_to_entries,
1808 delivery_to_entries);
1810 if (delivery_insert_after == pickup) {
1811 delivery_insert_after = pickup_insert_before;
1813 delivery_insert_after =
Value(delivery_insert_after);
1822bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithDeliveryAfter(
1823 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
1824 int64_t insert_after,
1826 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1827 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1829 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1830 delivery_to_entries) {
1831 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
1832 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1833 model()->GetPickupAndDeliveryPairs();
1834 for (
const int64_t delivery :
1835 node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass(
1836 cost_class, insert_after)) {
1837 if (StopSearchAndCleanup(priority_queue))
return false;
1842 for (
const auto& [pair_index, unused] :
1843 model()->GetDeliveryPositions(delivery)) {
1844 if (!pair_indices.contains(pair_index))
continue;
1845 for (
const int64_t pickup :
1846 pickup_delivery_pairs[pair_index].pickup_alternatives) {
1851 int64_t pickup_insert_after =
model()->Start(vehicle);
1852 while (pickup_insert_after != insert_after) {
1853 AddPairEntry(pickup, pickup_insert_after, delivery, insert_after,
1854 vehicle, priority_queue, pickup_to_entries,
1855 delivery_to_entries);
1856 pickup_insert_after =
Value(pickup_insert_after);
1864void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
1865 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
1867 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1868 std::vector<PairEntries>* pickup_to_entries,
1869 std::vector<PairEntries>* delivery_to_entries) {
1870 priority_queue->
Remove(entry);
1871 if (entry->pickup_insert_after() != -1) {
1872 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
1874 if (entry->delivery_insert_after() != -1) {
1875 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
1877 pair_entry_allocator_.FreeEntry(entry);
1880void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
1881 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1882 int64_t delivery_insert_after,
int vehicle,
1884 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1885 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1887 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1888 delivery_entries)
const {
1889 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
1890 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
1891 if (!pickup_vehicle_var->Contains(vehicle) ||
1892 !delivery_vehicle_var->Contains(vehicle)) {
1896 const auto vehicle_is_compatible = [pickup_vehicle_var,
1897 delivery_vehicle_var](
int vehicle) {
1898 return pickup_vehicle_var->Contains(vehicle) &&
1899 delivery_vehicle_var->Contains(vehicle);
1901 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
1902 empty_vehicle_type_curator_->Type(vehicle),
1903 vehicle_is_compatible)) {
1907 const int num_allowed_vehicles =
1908 std::min(pickup_vehicle_var->Size(), delivery_vehicle_var->Size());
1909 if (pickup_insert_after == -1) {
1910 DCHECK_EQ(delivery_insert_after, -1);
1911 DCHECK_EQ(vehicle, -1);
1912 PairEntry* pair_entry = pair_entry_allocator_.NewEntry(
1913 pickup, -1, delivery, -1, -1, num_allowed_vehicles);
1914 pair_entry->set_value(
1915 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1919 priority_queue->
Add(pair_entry);
1923 PairEntry*
const pair_entry = pair_entry_allocator_.NewEntry(
1924 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle,
1925 num_allowed_vehicles);
1926 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1927 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
1930 DCHECK(!priority_queue->
Contains(pair_entry));
1931 pickup_entries->at(pickup_insert_after).insert(pair_entry);
1932 delivery_entries->at(delivery_insert_after).insert(pair_entry);
1933 priority_queue->
Add(pair_entry);
1936void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
1937 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1939 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
1941 pair_entry->set_value(GetInsertionValueForPairAtPositions(
1942 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
1943 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
1944 pair_entry->vehicle()));
1947 DCHECK(priority_queue->
Contains(pair_entry));
1952GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
1953 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
1954 int64_t delivery_insert_after,
int vehicle)
const {
1955 DCHECK_GE(pickup_insert_after, 0);
1956 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1958 pickup, pickup_insert_after, pickup_insert_before, vehicle);
1960 DCHECK_GE(delivery_insert_after, 0);
1961 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1962 ? pickup_insert_before
1963 :
Value(delivery_insert_after);
1965 delivery, delivery_insert_after, delivery_insert_before, vehicle);
1967 const int64_t penalty_shift =
1968 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1971 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
1974bool GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
1975 const std::vector<bool>&
nodes,
const absl::flat_hash_set<int>& vehicles,
1976 NodeEntryQueue* queue) {
1979 const int num_vehicles =
1980 vehicles.empty() ?
model()->vehicles() : vehicles.size();
1981 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
1983 for (
int node = 0; node <
nodes.size(); node++) {
1991 AddNodeEntry(node, node, -1, all_vehicles, queue);
1994 InitializeInsertionEntriesPerformingNode(node, vehicles, queue);
1999void GlobalCheapestInsertionFilteredHeuristic::
2000 InitializeInsertionEntriesPerformingNode(
2001 int64_t node,
const absl::flat_hash_set<int>& vehicles,
2002 NodeEntryQueue* queue) {
2003 const int num_vehicles =
2004 vehicles.empty() ?
model()->vehicles() : vehicles.size();
2005 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
2008 auto vehicles_it = vehicles.begin();
2009 std::vector<NodeInsertion> insertions;
2015 for (
int v = 0; v < num_vehicles; v++) {
2016 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
2018 const int64_t
start =
model()->Start(vehicle);
2019 if (all_vehicles && !IsCheapestClassRepresentative(vehicle))
continue;
2023 for (
const NodeInsertion& insertion : insertions) {
2024 DCHECK_EQ(insertion.vehicle, vehicle);
2025 AddNodeEntry(node, insertion.insert_after, vehicle, all_vehicles,
2034 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
2035 int v,
int cost_class) {
2036 return (
model()->GetCostClassIndexOfVehicle(v).
value() == cost_class) &&
2037 (all_vehicles || vehicles.contains(v));
2039 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
2041 for (
const int64_t insert_after :
2042 node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass(
2043 cost_class, node)) {
2047 const int vehicle = node_index_to_vehicle_[insert_after];
2048 if (vehicle == -1 ||
2049 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
2052 if (all_vehicles && !IsCheapestClassRepresentative(vehicle))
continue;
2053 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2058bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterNodeInsertion(
2059 const std::vector<bool>&
nodes,
int vehicle, int64_t node,
2060 int64_t insert_after,
bool all_vehicles, NodeEntryQueue* queue) {
2063 if (!UpdateExistingNodeEntriesOnChain(
nodes, vehicle, insert_after,
2064 Value(insert_after), all_vehicles,
2069 if (!AddNodeEntriesAfter(
nodes, vehicle, node, all_vehicles, queue)) {
2072 SetVehicleIndex(node, vehicle);
2076bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingNodeEntriesOnChain(
2077 const std::vector<bool>&
nodes,
int vehicle, int64_t insert_after_start,
2078 int64_t insert_after_end,
bool all_vehicles, NodeEntryQueue* queue) {
2079 int64_t insert_after = insert_after_start;
2080 while (insert_after != insert_after_end) {
2081 DCHECK(!
model()->IsEnd(insert_after));
2082 AddNodeEntriesAfter(
nodes, vehicle, insert_after, all_vehicles, queue);
2083 insert_after =
Value(insert_after);
2088bool GlobalCheapestInsertionFilteredHeuristic::AddNodeEntriesAfter(
2089 const std::vector<bool>&
nodes,
int vehicle, int64_t insert_after,
2090 bool all_vehicles, NodeEntryQueue* queue) {
2091 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
2094 queue->ClearInsertions(insert_after);
2096 node_index_to_neighbors_by_cost_class_->GetNeighborsOfNodeForCostClass(
2097 cost_class, insert_after)) {
2100 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2106void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2107 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2108 NodeEntryQueue* queue)
const {
2110 const int64_t penalty_shift =
2111 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2114 const IntVar*
const vehicle_var =
model()->VehicleVar(node);
2115 if (!vehicle_var->Contains(vehicle)) {
2119 const auto vehicle_is_compatible = [vehicle_var](
int vehicle) {
2120 return vehicle_var->Contains(vehicle);
2122 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2123 empty_vehicle_type_curator_->Type(vehicle),
2124 vehicle_is_compatible)) {
2128 const int num_allowed_vehicles = vehicle_var->Size();
2129 if (vehicle == -1) {
2130 DCHECK_EQ(node, insert_after);
2131 if (!all_vehicles) {
2137 queue->PushInsertion(node, node, -1, num_allowed_vehicles,
2138 CapSub(node_penalty, penalty_shift));
2143 node, insert_after,
Value(insert_after), vehicle);
2144 if (!all_vehicles && insertion_cost > node_penalty) {
2151 queue->PushInsertion(node, insert_after, vehicle, num_allowed_vehicles,
2152 CapSub(insertion_cost, penalty_shift));
2156 int pickup,
int delivery,
int vehicle,
const std::vector<int>& path,
2157 const std::vector<bool>& path_node_is_pickup,
2158 const std::vector<bool>& path_node_is_delivery,
2160 const int num_nodes = path.size();
2161 DCHECK_GE(num_nodes, 2);
2162 const int kNoPrevIncrease = -1;
2163 const int kNoNextDecrease = num_nodes;
2165 prev_decrease_.resize(num_nodes - 1);
2166 prev_increase_.resize(num_nodes - 1);
2167 int prev_decrease = 0;
2168 int prev_increase = kNoPrevIncrease;
2169 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2170 if (path_node_is_delivery[pos]) prev_decrease = pos;
2171 prev_decrease_[pos] = prev_decrease;
2172 if (path_node_is_pickup[pos]) prev_increase = pos;
2173 prev_increase_[pos] = prev_increase;
2177 next_decrease_.resize(num_nodes - 1);
2178 next_increase_.resize(num_nodes - 1);
2179 int next_increase = num_nodes - 1;
2180 int next_decrease = kNoNextDecrease;
2181 for (
int pos = num_nodes - 2; pos >= 0; --pos) {
2182 next_decrease_[pos] = next_decrease;
2183 if (path_node_is_delivery[pos]) next_decrease = pos;
2184 next_increase_[pos] = next_increase;
2185 if (path_node_is_pickup[pos]) next_increase = pos;
2189 auto append = [pickup, delivery, vehicle, num_nodes, &path, &insertions](
2190 int pickup_pos,
int delivery_pos) {
2191 if (pickup_pos < 0 || num_nodes - 1 <= pickup_pos)
return;
2192 if (delivery_pos < 0 || num_nodes - 1 <= delivery_pos)
return;
2193 const int delivery_pred =
2194 pickup_pos == delivery_pos ? pickup : path[delivery_pos];
2196 vehicle, {{.pred = path[pickup_pos], .node = pickup},
2197 {.pred = delivery_pred, .node = delivery}});
2201 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2202 const bool is_after_decrease = prev_increase_[pos] < prev_decrease_[pos];
2203 const bool is_before_increase = next_increase_[pos] < next_decrease_[pos];
2204 if (is_after_decrease) {
2205 append(prev_increase_[pos], pos);
2206 if (is_before_increase) {
2207 append(pos, next_increase_[pos] - 1);
2208 append(pos, next_decrease_[pos] - 1);
2216 if (next_increase_[pos] - 1 != pos) {
2218 if (prev_decrease_[pos] != pos) append(prev_decrease_[pos], pos);
2222 append(pos, next_decrease_[pos] - 1);
2223 if (!is_before_increase && next_decrease_[pos] - 1 != pos) {
2232 if (prev_increase_[pos] != pos) append(prev_increase_[pos], pos);
2242 RoutingModel*
model, std::function<
bool()> stop_search,
2243 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
2244 RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy,
2246 std::function<
bool(
const std::vector<RoutingModel::VariableValuePair>&,
2247 std::vector<RoutingModel::VariableValuePair>*)>
2248 optimize_on_insertion)
2250 std::move(evaluator), nullptr,
2252 pair_insertion_strategy_(pair_insertion_strategy),
2253 bin_capacities_(bin_capacities),
2254 optimize_on_insertion_(
std::move(optimize_on_insertion)) {}
2259 synchronize_insertion_optimizer_ =
true;
2260 ComputeInsertionOrder();
2263bool LocalCheapestInsertionFilteredHeuristic::OptimizeOnInsertion(
2264 std::vector<int> delta_indices) {
2265 if (optimize_on_insertion_ ==
nullptr)
return false;
2266 std::vector<RoutingModel::VariableValuePair> in_state;
2267 if (synchronize_insertion_optimizer_) {
2268 for (
int i = 0; i <
model()->Nexts().size(); ++i) {
2270 in_state.push_back({i,
Value(i)});
2273 synchronize_insertion_optimizer_ =
false;
2279 std::vector<RoutingModel::VariableValuePair> out_state;
2280 optimize_on_insertion_(in_state, &out_state);
2281 if (out_state.empty())
return false;
2282 for (
const auto& [
var,
value] : out_state) {
2295int64_t GetNegMaxDistanceFromVehicles(
const RoutingModel&
model,
2297 const auto& [pickups, deliveries] =
2298 model.GetPickupAndDeliveryPairs()[pair_index];
2300 Bitset64<int> vehicle_set(
model.vehicles());
2301 for (
int v = 0; v <
model.vehicles(); ++v) vehicle_set.
Set(v);
2304 std::vector<std::vector<int64_t>> pickup_costs(
model.Size());
2305 for (int64_t pickup : pickups) {
2306 std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2307 cost_from_start.resize(
model.vehicles(), -1);
2309 ProcessVehicleStartEndCosts(
2311 [&cost_from_start](int64_t cost,
int v) { cost_from_start[v] = cost; },
2312 vehicle_set,
false,
true);
2316 std::vector<std::vector<int64_t>> delivery_costs(
model.Size());
2317 for (int64_t delivery : deliveries) {
2318 std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2319 cost_to_end.resize(
model.vehicles(), -1);
2321 ProcessVehicleStartEndCosts(
2323 [&cost_to_end](int64_t cost,
int v) { cost_to_end[v] = cost; },
2324 vehicle_set,
true,
false);
2327 int64_t max_pair_distance = 0;
2328 for (int64_t pickup : pickups) {
2329 const std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2330 for (int64_t delivery : deliveries) {
2331 const std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2332 int64_t closest_vehicle_distance = std::numeric_limits<int64_t>::max();
2333 for (
int v = 0; v <
model.vehicles(); v++) {
2334 if (cost_from_start[v] < 0 || cost_to_end[v] < 0) {
2338 closest_vehicle_distance =
2339 std::min(closest_vehicle_distance,
2340 CapAdd(cost_from_start[v], cost_to_end[v]));
2342 max_pair_distance = std::max(max_pair_distance, closest_vehicle_distance);
2345 return CapOpp(max_pair_distance);
2349void LocalCheapestInsertionFilteredHeuristic::ComputeInsertionOrder() {
2350 if (!insertion_order_.empty())
return;
2359 insertion_order_.reserve(
model.Size() +
2360 model.GetPickupAndDeliveryPairs().size());
2363 const std::vector<PickupDeliveryPair>& pairs =
2364 model.GetPickupAndDeliveryPairs();
2366 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
2367 const auto& [pickups, deliveries] = pairs[pair_index];
2368 uint64_t num_allowed_vehicles = std::numeric_limits<uint64_t>::max();
2369 int64_t pickup_penalty = 0;
2370 for (int64_t pickup : pickups) {
2371 num_allowed_vehicles =
2372 std::min(num_allowed_vehicles,
model.VehicleVar(pickup)->Size());
2374 std::max(pickup_penalty,
model.UnperformedPenalty(pickup));
2376 int64_t delivery_penalty = 0;
2377 for (int64_t delivery : deliveries) {
2378 num_allowed_vehicles =
2379 std::min(num_allowed_vehicles,
model.VehicleVar(delivery)->Size());
2381 std::max(delivery_penalty,
model.UnperformedPenalty(delivery));
2383 insertion_order_.push_back(
2384 {num_allowed_vehicles,
2386 {GetNegMaxDistanceFromVehicles(
model, pair_index), 0},
2391 Bitset64<int> vehicle_set(
model.vehicles());
2392 for (
int v = 0; v <
model.vehicles(); ++v) vehicle_set.
Set(v);
2394 for (
int node = 0; node <
model.Size(); ++node) {
2395 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
2397 int64_t min_distance = std::numeric_limits<int64_t>::max();
2398 ProcessVehicleStartEndCosts(
2400 [&min_distance](int64_t dist,
int) {
2401 min_distance = std::min(min_distance, dist);
2405 const uint64_t num_allowed_vehicles =
model.VehicleVar(node)->Size();
2406 const int64_t neg_penalty =
CapOpp(
model.UnperformedPenalty(node));
2407 insertion_order_.push_back({num_allowed_vehicles,
2409 {
CapOpp(min_distance), 0},
2414 absl::c_sort(insertion_order_, std::greater<Seed>());
2415 absl::c_reverse(insertion_order_);
2418bool LocalCheapestInsertionFilteredHeuristic::InsertPair(
2419 int64_t pickup, int64_t insert_pickup_after, int64_t delivery,
2420 int64_t insert_delivery_after,
int vehicle) {
2421 const int64_t insert_pickup_before =
Value(insert_pickup_after);
2422 InsertBetween(pickup, insert_pickup_after, insert_pickup_before, vehicle);
2423 DCHECK_NE(insert_delivery_after, insert_pickup_after);
2424 const int64_t insert_delivery_before = (insert_delivery_after == pickup)
2425 ? insert_pickup_before
2426 :
Value(insert_delivery_after);
2427 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
2432 OptimizeOnInsertion(std::move(indices));
2438void LocalCheapestInsertionFilteredHeuristic::InsertBestPickupThenDelivery(
2439 const PickupDeliveryPair& index_pair) {
2440 for (
int pickup : index_pair.pickup_alternatives) {
2441 std::vector<NodeInsertion> pickup_insertions =
2442 ComputeEvaluatorSortedPositions(pickup);
2443 for (
int delivery : index_pair.delivery_alternatives) {
2445 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
2446 const int vehicle = pickup_insertion.vehicle;
2447 if (!
model()->VehicleVar(delivery)->
Contains(vehicle))
continue;
2448 if (MustUpdateBinCapacities() &&
2453 for (
const NodeInsertion& delivery_insertion :
2454 ComputeEvaluatorSortedPositionsOnRouteAfter(
2455 delivery, pickup,
Value(pickup_insertion.insert_after),
2457 if (InsertPair(pickup, pickup_insertion.insert_after, delivery,
2458 delivery_insertion.insert_after, vehicle)) {
2459 if (MustUpdateBinCapacities()) {
2472void LocalCheapestInsertionFilteredHeuristic::InsertBestPair(
2473 const PickupDeliveryPair& pair) {
2474 for (
int pickup : pair.pickup_alternatives) {
2475 for (
int delivery : pair.delivery_alternatives) {
2477 std::vector<PickupDeliveryInsertion> sorted_pair_positions =
2478 ComputeEvaluatorSortedPairPositions(pickup, delivery);
2479 if (sorted_pair_positions.empty())
continue;
2480 for (
const auto [insert_pickup_after, insert_delivery_after, unused_value,
2481 vehicle] : sorted_pair_positions) {
2482 if (InsertPair(pickup, insert_pickup_after, delivery,
2483 insert_delivery_after, vehicle)) {
2484 if (MustUpdateBinCapacities()) {
2496void LocalCheapestInsertionFilteredHeuristic::InsertBestPairMultitour(
2497 const PickupDeliveryPair& pair) {
2498 using InsertionSequence = InsertionSequenceContainer::InsertionSequence;
2499 using Insertion = InsertionSequenceContainer::Insertion;
2501 std::vector<int> path;
2502 std::vector<bool> path_node_is_pickup;
2503 std::vector<bool> path_node_is_delivery;
2505 auto fill_path = [&path, &path_node_is_pickup, &path_node_is_delivery,
2506 this](
int vehicle) {
2508 path_node_is_pickup.clear();
2509 path_node_is_delivery.clear();
2511 const int end =
model()->End(vehicle);
2513 path.push_back(node);
2514 path_node_is_pickup.push_back(
model()->IsPickup(node));
2515 path_node_is_delivery.push_back(
model()->IsDelivery(node));
2517 path.push_back(
end);
2521 auto price_insertion_sequences_evaluator =
2522 [
this](BinCapacities* bin_capacities) {
2523 for (InsertionSequence sequence : insertion_container_) {
2524 int64_t sequence_cost = 0;
2525 int previous_node = -1;
2526 int previous_succ = -1;
2527 for (
const Insertion& insertion : sequence) {
2528 const int succ = previous_node == insertion.pred
2530 :
Value(insertion.pred);
2532 insertion.node, insertion.pred, succ, sequence.Vehicle());
2533 sequence_cost =
CapAdd(sequence_cost, cost);
2534 previous_node = insertion.node;
2535 previous_succ = succ;
2537 sequence.Cost() = sequence_cost;
2539 if (bin_capacities ==
nullptr)
return;
2540 for (InsertionSequence sequence : insertion_container_) {
2541 const int64_t old_cost = bin_capacities->
TotalCost();
2542 for (
const Insertion& insertion : sequence) {
2543 bin_capacities->
AddItemToBin(insertion.node, sequence.Vehicle());
2545 const int64_t new_cost = bin_capacities->
TotalCost();
2546 const int64_t delta_cost =
CapSub(new_cost, old_cost);
2547 sequence.Cost() =
CapAdd(sequence.Cost(), delta_cost);
2548 for (
const Insertion& insertion : sequence) {
2550 sequence.Vehicle());
2555 auto price_insertion_sequences_no_evaluator = [
this]() {
2556 for (InsertionSequence sequence : insertion_container_) {
2557 int previous_node = -1;
2558 int previous_succ = -1;
2559 for (
const Insertion& insertion : sequence) {
2560 const int succ = previous_node == insertion.pred
2562 :
Value(insertion.pred);
2563 InsertBetween(insertion.node, insertion.pred, succ, sequence.Vehicle());
2564 previous_node = insertion.node;
2565 previous_succ = succ;
2571 for (
int pickup : pair.pickup_alternatives) {
2572 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
2574 for (
int delivery : pair.delivery_alternatives) {
2575 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
2576 insertion_container_.Clear();
2577 std::unique_ptr<IntVarIterator> pickup_vehicles(
2578 pickup_vehicle_var->MakeDomainIterator(
false));
2579 for (
const int vehicle : InitAndGetValues(pickup_vehicles.get())) {
2580 if (vehicle == -1)
continue;
2581 if (!delivery_vehicle_var->Contains(vehicle))
continue;
2582 if (MustUpdateBinCapacities() &&
2589 pickup, delivery, vehicle, path, path_node_is_pickup,
2590 path_node_is_delivery, insertion_container_);
2594 price_insertion_sequences_no_evaluator();
2596 price_insertion_sequences_evaluator(bin_capacities_);
2599 insertion_container_.RemoveIf(
2600 [](
const InsertionSequence& sequence) ->
bool {
2603 insertion_container_.Sort();
2604 for (InsertionSequence sequence : insertion_container_) {
2606 int previous_node = -1;
2607 int previous_succ = -1;
2608 const int vehicle = sequence.Vehicle();
2609 for (
const Insertion& insertion : sequence) {
2610 const int succ = previous_node == insertion.pred
2612 :
Value(insertion.pred);
2613 InsertBetween(insertion.node, insertion.pred, succ, vehicle);
2614 previous_node = insertion.node;
2615 previous_succ = succ;
2619 if (MustUpdateBinCapacities()) {
2631void SetFalseForAllAlternatives(
const PickupDeliveryPair& pair,
2632 std::vector<bool>* data) {
2633 for (
const int64_t pickup : pair.pickup_alternatives) {
2634 data->at(pickup) =
false;
2636 for (
const int64_t delivery : pair.delivery_alternatives) {
2637 data->at(delivery) =
false;
2646 if (MustUpdateBinCapacities()) {
2648 for (
int vehicle = 0; vehicle <
model.vehicles(); ++vehicle) {
2656 const std::vector<PickupDeliveryPair>& pairs =
2657 model.GetPickupAndDeliveryPairs();
2658 std::vector<bool> ignore_pair_index(pairs.size(),
false);
2659 std::vector<bool> insert_as_single_node(
model.Size(),
true);
2660 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
2661 const auto& [pickups, deliveries] = pairs[pair_index];
2662 bool pickup_contained =
false;
2663 for (int64_t pickup : pickups) {
2665 pickup_contained =
true;
2669 bool delivery_contained =
false;
2670 for (int64_t delivery : deliveries) {
2672 delivery_contained =
true;
2676 ignore_pair_index[pair_index] = pickup_contained || delivery_contained;
2677 if (pickup_contained == delivery_contained) {
2682 SetFalseForAllAlternatives(pairs[pair_index], &insert_as_single_node);
2686 for (
const Seed& seed : insertion_order_) {
2687 const int index = seed.index;
2688 if (!seed.is_node_index) {
2689 if (ignore_pair_index[
index])
continue;
2691 const auto& pair = pairs[
index];
2692 switch (pair_insertion_strategy_) {
2693 case RoutingSearchParameters::AUTOMATIC:
2694 case RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR:
2695 InsertBestPair(pair);
2697 case RoutingSearchParameters::BEST_PICKUP_THEN_BEST_DELIVERY:
2698 InsertBestPickupThenDelivery(pair);
2700 case RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR_MULTITOUR:
2701 InsertBestPairMultitour(pair);
2704 LOG(ERROR) <<
"Unknown pair insertion strategy value.";
2715 ComputeEvaluatorSortedPositions(
index)) {
2720 Value(insertion.insert_after), insertion.vehicle);
2724 if (MustUpdateBinCapacities()) {
2727 OptimizeOnInsertion(std::move(indices));
2736std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
2737LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
2741 if (node >=
size)
return {};
2742 std::vector<NodeInsertion> sorted_insertions;
2743 const IntVar* vehicle_var =
model()->VehicleVar(node);
2744 std::unique_ptr<IntVarIterator> node_vehicles(
2745 vehicle_var->MakeDomainIterator(
false));
2746 for (
const int vehicle : InitAndGetValues(node_vehicles.get())) {
2747 if (vehicle == -1)
continue;
2748 if (MustUpdateBinCapacities() &&
2752 const int64_t
start =
model()->Start(vehicle);
2753 const size_t old_num_insertions = sorted_insertions.size();
2755 false, &sorted_insertions);
2756 if (MustUpdateBinCapacities() &&
evaluator_) {
2758 const int64_t old_cost = bin_capacities_->
TotalCost();
2760 const int64_t new_cost = bin_capacities_->
TotalCost();
2762 const int64_t delta_cost =
CapSub(new_cost, old_cost);
2764 for (
size_t i = old_num_insertions;
i < sorted_insertions.size(); ++
i) {
2765 sorted_insertions[
i].value =
2770 absl::c_sort(sorted_insertions);
2771 return sorted_insertions;
2774std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
2775LocalCheapestInsertionFilteredHeuristic::
2776 ComputeEvaluatorSortedPositionsOnRouteAfter(int64_t node, int64_t
start,
2777 int64_t next_after_start,
2781 if (node >=
size)
return {};
2782 std::vector<NodeInsertion> sorted_insertions;
2784 false, &sorted_insertions);
2785 absl::c_sort(sorted_insertions);
2786 return sorted_insertions;
2789std::vector<PickupDeliveryInsertion>
2790LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions(
2791 int pickup,
int delivery) {
2792 std::vector<PickupDeliveryInsertion> sorted_pickup_delivery_insertions;
2794 DCHECK_LT(pickup,
size);
2795 DCHECK_LT(delivery,
size);
2796 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
2797 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
2798 std::unique_ptr<IntVarIterator> pickup_vehicles(
2799 pickup_vehicle_var->MakeDomainIterator(
false));
2800 for (
const int vehicle : InitAndGetValues(pickup_vehicles.get())) {
2801 if (vehicle == -1)
continue;
2802 if (!delivery_vehicle_var->Contains(vehicle))
continue;
2803 if (MustUpdateBinCapacities() &&
2808 int64_t insert_pickup_after =
model()->Start(vehicle);
2809 while (!
model()->IsEnd(insert_pickup_after)) {
2810 const int64_t insert_pickup_before =
Value(insert_pickup_after);
2811 int64_t insert_delivery_after = pickup;
2812 while (!
model()->IsEnd(insert_delivery_after)) {
2814 const int64_t insert_delivery_before =
2815 insert_delivery_after == pickup ? insert_pickup_before
2816 :
Value(insert_delivery_after);
2818 InsertBetween(pickup, insert_pickup_after, insert_pickup_before,
2820 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
2822 std::optional<int64_t> insertion_cost =
Evaluate(
false);
2823 if (insertion_cost.has_value()) {
2824 sorted_pickup_delivery_insertions.push_back(
2825 {insert_pickup_after, insert_delivery_after, *insertion_cost,
2830 pickup, insert_pickup_after, insert_pickup_before, vehicle);
2832 delivery, insert_delivery_after, insert_delivery_before, vehicle);
2833 int64_t total_cost =
CapAdd(pickup_cost, delivery_cost);
2834 if (MustUpdateBinCapacities()) {
2835 const int64_t old_cost = bin_capacities_->
TotalCost();
2838 const int64_t new_cost = bin_capacities_->
TotalCost();
2839 total_cost =
CapAdd(total_cost,
CapSub(new_cost, old_cost));
2843 sorted_pickup_delivery_insertions.push_back({insert_pickup_after,
2844 insert_delivery_after,
2845 total_cost, vehicle});
2847 insert_delivery_after = insert_delivery_before;
2849 insert_pickup_after = insert_pickup_before;
2852 absl::c_sort(sorted_pickup_delivery_insertions);
2853 return sorted_pickup_delivery_insertions;
2859 RoutingModel*
model, std::function<
bool()> stop_search,
2865 const int num_nexts =
model()->Nexts().size();
2866 std::vector<std::vector<int64_t>> deliveries(num_nexts);
2867 std::vector<std::vector<int64_t>> pickups(num_nexts);
2868 for (
const auto& [pickup_alternatives, delivery_alternatives] :
2869 model()->GetPickupAndDeliveryPairs()) {
2870 for (
int pickup : pickup_alternatives) {
2871 for (
int delivery : delivery_alternatives) {
2872 deliveries[pickup].push_back(delivery);
2873 pickups[delivery].push_back(pickup);
2880 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
2881 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
2882 sorted_vehicles[vehicle] = vehicle;
2884 absl::c_sort(sorted_vehicles,
2885 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
2887 for (
const int vehicle : sorted_vehicles) {
2889 bool extend_route =
true;
2894 while (extend_route) {
2895 extend_route =
false;
2897 int64_t
index = last_node;
2906 std::vector<int64_t> neighbors;
2908 std::unique_ptr<IntVarIterator> it(
2909 model()->Nexts()[
index]->MakeDomainIterator(
false));
2911 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
2914 for (
int i = 0; !found && i < neighbors.size(); ++i) {
2918 next = FindTopSuccessor(
index, neighbors);
2921 SortSuccessors(
index, &neighbors);
2922 ABSL_FALLTHROUGH_INTENDED;
2924 next = neighbors[i];
2931 bool contains_pickups =
false;
2932 for (int64_t pickup : pickups[
next]) {
2934 contains_pickups =
true;
2938 if (!contains_pickups) {
2942 std::vector<int64_t> next_deliveries;
2943 if (
next < deliveries.size()) {
2944 next_deliveries = GetPossibleNextsFromIterator(
2947 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
2948 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
2953 delivery = FindTopSuccessor(
next, next_deliveries);
2956 SortSuccessors(
next, &next_deliveries);
2957 ABSL_FALLTHROUGH_INTENDED;
2959 delivery = next_deliveries[j];
2977 if (
model()->IsEnd(
end) && last_node != delivery) {
2978 last_node = delivery;
2979 extend_route =
true;
2994bool CheapestAdditionFilteredHeuristic::
2995 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
2996 int vehicle2)
const {
2997 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
2998 builder_.GetStartChainEnd(vehicle1));
2999 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
3000 builder_.GetStartChainEnd(vehicle2));
3001 if (has_partial_route1 == has_partial_route2) {
3002 return vehicle2 < vehicle1;
3004 return has_partial_route2 < has_partial_route1;
3012 RoutingModel*
model, std::function<
bool()> stop_search,
3013 std::function<int64_t(int64_t, int64_t)> evaluator,
3017 evaluator_(
std::move(evaluator)) {}
3019int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
3020 int64_t node,
const std::vector<int64_t>& successors) {
3021 int64_t best_evaluation = std::numeric_limits<int64_t>::max();
3022 int64_t best_successor = -1;
3023 for (int64_t successor : successors) {
3024 const int64_t evaluation = (successor >= 0)
3025 ? evaluator_(node, successor)
3026 : std::numeric_limits<int64_t>::max();
3027 if (evaluation < best_evaluation ||
3028 (evaluation == best_evaluation && successor > best_successor)) {
3029 best_evaluation = evaluation;
3030 best_successor = successor;
3033 return best_successor;
3036void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
3037 int64_t node, std::vector<int64_t>* successors) {
3038 std::vector<std::pair<int64_t, int64_t>> values;
3039 values.reserve(successors->size());
3040 for (int64_t successor : *successors) {
3043 values.push_back({evaluator_(node, successor), successor});
3045 absl::c_sort(values, [](
const std::pair<int64_t, int64_t>& s1,
3046 const std::pair<int64_t, int64_t>& s2) {
3047 return s1.first < s2.first ||
3048 (s1.first == s2.first && s1.second > s2.second);
3050 successors->clear();
3051 for (
auto value : values) {
3052 successors->push_back(
value.second);
3060 RoutingModel*
model, std::function<
bool()> stop_search,
3065 comparator_(
std::move(comparator)) {}
3067int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
3068 int64_t node,
const std::vector<int64_t>& successors) {
3069 return *absl::c_min_element(
3070 successors, [
this, node](
int successor1,
int successor2) {
3071 return comparator_(node, successor1, successor2);
3075void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
3076 int64_t node, std::vector<int64_t>* successors) {
3077 absl::c_sort(*successors, [
this, node](
int successor1,
int successor2) {
3078 return comparator_(node, successor1, successor2);
3130template <
typename Saving>
3131class SavingsFilteredHeuristic::SavingsContainer {
3135 : savings_db_(savings_db),
3136 index_in_sorted_savings_(0),
3137 vehicle_types_(vehicle_types),
3138 single_vehicle_type_(vehicle_types == 1),
3139 using_incoming_reinjected_saving_(false),
3144 sorted_savings_per_vehicle_type_.clear();
3145 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
3146 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
3147 savings.reserve(
size * saving_neighbors);
3150 sorted_savings_.clear();
3151 costs_and_savings_per_arc_.clear();
3152 arc_indices_per_before_node_.clear();
3154 if (!single_vehicle_type_) {
3155 costs_and_savings_per_arc_.reserve(
size * saving_neighbors);
3156 arc_indices_per_before_node_.resize(
size);
3157 for (
int before_node = 0; before_node <
size; before_node++) {
3158 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
3161 skipped_savings_starting_at_.clear();
3162 skipped_savings_starting_at_.resize(
size);
3163 skipped_savings_ending_at_.clear();
3164 skipped_savings_ending_at_.resize(
size);
3165 incoming_reinjected_savings_ =
nullptr;
3166 outgoing_reinjected_savings_ =
nullptr;
3167 incoming_new_reinjected_savings_ =
nullptr;
3168 outgoing_new_reinjected_savings_ =
nullptr;
3172 int64_t before_node, int64_t after_node,
int vehicle_type) {
3173 CHECK(!sorted_savings_per_vehicle_type_.empty())
3174 <<
"Container not initialized!";
3175 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
3176 UpdateArcIndicesCostsAndSavings(before_node, after_node,
3177 {total_cost, saving});
3181 CHECK(!sorted_) <<
"Container already sorted!";
3183 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
3184 absl::c_sort(savings);
3187 if (single_vehicle_type_) {
3188 const auto& savings = sorted_savings_per_vehicle_type_[0];
3189 sorted_savings_.resize(savings.size());
3190 absl::c_transform(savings, sorted_savings_.begin(),
3191 [](
const Saving& saving) {
3192 return SavingAndArc({saving, -1});
3198 sorted_savings_.reserve(vehicle_types_ *
3199 costs_and_savings_per_arc_.size());
3201 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
3203 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
3204 costs_and_savings_per_arc_[arc_index];
3205 DCHECK(!costs_and_savings.empty());
3209 [](
const std::pair<int64_t, Saving>& cs1,
3210 const std::pair<int64_t, Saving>& cs2) {
return cs1 > cs2; });
3215 const int64_t cost = costs_and_savings.back().first;
3216 while (!costs_and_savings.empty() &&
3217 costs_and_savings.back().first == cost) {
3218 sorted_savings_.push_back(
3219 {costs_and_savings.back().second, arc_index});
3220 costs_and_savings.pop_back();
3223 absl::c_sort(sorted_savings_);
3224 next_saving_type_and_index_for_arc_.clear();
3225 next_saving_type_and_index_for_arc_.resize(
3226 costs_and_savings_per_arc_.size(), {-1, -1});
3229 index_in_sorted_savings_ = 0;
3234 return index_in_sorted_savings_ < sorted_savings_.size() ||
3235 HasReinjectedSavings();
3239 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
3241 <<
"Update() should be called between two calls to GetSaving() !";
3245 if (HasReinjectedSavings()) {
3246 if (incoming_reinjected_savings_ !=
nullptr &&
3247 outgoing_reinjected_savings_ !=
nullptr) {
3249 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
3250 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
3251 if (incoming_saving < outgoing_saving) {
3252 current_saving_ = incoming_saving;
3253 using_incoming_reinjected_saving_ =
true;
3255 current_saving_ = outgoing_saving;
3256 using_incoming_reinjected_saving_ =
false;
3259 if (incoming_reinjected_savings_ !=
nullptr) {
3260 current_saving_ = incoming_reinjected_savings_->front();
3261 using_incoming_reinjected_saving_ =
true;
3263 if (outgoing_reinjected_savings_ !=
nullptr) {
3264 current_saving_ = outgoing_reinjected_savings_->front();
3265 using_incoming_reinjected_saving_ =
false;
3269 current_saving_ = sorted_savings_[index_in_sorted_savings_];
3271 return current_saving_.
saving;
3274 void Update(
bool update_best_saving,
int type = -1) {
3275 CHECK(to_update_) <<
"Container already up to date!";
3276 if (update_best_saving) {
3277 const int64_t arc_index = current_saving_.arc_index;
3278 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
3280 if (!HasReinjectedSavings()) {
3281 index_in_sorted_savings_++;
3283 if (index_in_sorted_savings_ == sorted_savings_.size()) {
3284 sorted_savings_.swap(next_savings_);
3286 index_in_sorted_savings_ = 0;
3288 absl::c_sort(sorted_savings_);
3289 next_saving_type_and_index_for_arc_.clear();
3290 next_saving_type_and_index_for_arc_.resize(
3291 costs_and_savings_per_arc_.size(), {-1, -1});
3294 UpdateReinjectedSavings();
3299 CHECK(!single_vehicle_type_);
3300 Update(
true, type);
3304 CHECK(sorted_) <<
"Savings not sorted yet!";
3305 CHECK_LT(type, vehicle_types_);
3306 return sorted_savings_per_vehicle_type_[type];
3310 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
3311 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
3315 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
3316 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
3320 struct SavingAndArc {
3324 bool operator<(
const SavingAndArc& other)
const {
3325 return std::tie(saving, arc_index) <
3326 std::tie(other.saving, other.arc_index);
3332 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
3333 const Saving& saving = saving_and_arc.saving;
3334 const int64_t before_node = saving.before_node;
3335 const int64_t after_node = saving.after_node;
3336 if (!savings_db_->Contains(before_node)) {
3337 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
3339 if (!savings_db_->Contains(after_node)) {
3340 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
3354 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
3355 if (single_vehicle_type_) {
3358 SkipSavingForArc(current_saving_);
3361 CHECK_GE(arc_index, 0);
3362 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
3363 const int previous_index = type_and_index.second;
3364 const int previous_type = type_and_index.first;
3365 bool next_saving_added =
false;
3368 if (previous_index >= 0) {
3370 DCHECK_GE(previous_type, 0);
3371 if (type == -1 || previous_type == type) {
3374 next_saving_added =
true;
3375 next_saving = next_savings_[previous_index].saving;
3379 if (!next_saving_added &&
3380 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
3381 type_and_index.first = next_saving.vehicle_type;
3382 if (previous_index >= 0) {
3384 next_savings_[previous_index] = {next_saving, arc_index};
3387 type_and_index.second = next_savings_.size();
3388 next_savings_.push_back({next_saving, arc_index});
3390 next_saving_added =
true;
3396 SkipSavingForArc(current_saving_);
3400 if (next_saving_added) {
3401 SkipSavingForArc({next_saving, arc_index});
3406 void UpdateReinjectedSavings() {
3407 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
3408 &incoming_reinjected_savings_,
3409 using_incoming_reinjected_saving_);
3410 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
3411 &outgoing_reinjected_savings_,
3412 !using_incoming_reinjected_saving_);
3413 incoming_new_reinjected_savings_ =
nullptr;
3414 outgoing_new_reinjected_savings_ =
nullptr;
3417 void UpdateGivenReinjectedSavings(
3418 std::deque<SavingAndArc>* new_reinjected_savings,
3419 std::deque<SavingAndArc>** reinjected_savings,
3420 bool using_reinjected_savings) {
3421 if (new_reinjected_savings ==
nullptr) {
3423 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
3424 CHECK(!(*reinjected_savings)->empty());
3425 (*reinjected_savings)->pop_front();
3426 if ((*reinjected_savings)->empty()) {
3427 *reinjected_savings =
nullptr;
3436 if (*reinjected_savings !=
nullptr) {
3437 (*reinjected_savings)->clear();
3439 *reinjected_savings =
nullptr;
3440 if (!new_reinjected_savings->empty()) {
3441 *reinjected_savings = new_reinjected_savings;
3445 bool HasReinjectedSavings() {
3446 return outgoing_reinjected_savings_ !=
nullptr ||
3447 incoming_reinjected_savings_ !=
nullptr;
3450 void UpdateArcIndicesCostsAndSavings(
3451 int64_t before_node, int64_t after_node,
3452 const std::pair<int64_t, Saving>& cost_and_saving) {
3453 if (single_vehicle_type_) {
3456 absl::flat_hash_map<int, int>& arc_indices =
3457 arc_indices_per_before_node_[before_node];
3458 const auto& arc_inserted = arc_indices.insert(
3459 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
3460 const int index = arc_inserted.first->second;
3461 if (arc_inserted.second) {
3462 costs_and_savings_per_arc_.push_back({cost_and_saving});
3464 DCHECK_LT(
index, costs_and_savings_per_arc_.size());
3465 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
3469 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
3470 Saving* next_saving) {
3471 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
3472 costs_and_savings_per_arc_[arc_index];
3474 bool found_saving =
false;
3475 while (!costs_and_savings.empty() && !found_saving) {
3476 const Saving& saving = costs_and_savings.back().second;
3477 if (type == -1 || saving.vehicle_type == type) {
3478 *next_saving = saving;
3479 found_saving =
true;
3481 costs_and_savings.pop_back();
3483 return found_saving;
3486 const SavingsFilteredHeuristic*
const savings_db_;
3487 int64_t index_in_sorted_savings_;
3488 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
3489 std::vector<SavingAndArc> sorted_savings_;
3490 std::vector<SavingAndArc> next_savings_;
3491 std::vector<std::pair< int,
int>>
3492 next_saving_type_and_index_for_arc_;
3493 SavingAndArc current_saving_;
3494 std::vector<std::vector<std::pair< int64_t, Saving>>>
3495 costs_and_savings_per_arc_;
3496 std::vector<absl::flat_hash_map< int,
int>>
3497 arc_indices_per_before_node_;
3498 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
3499 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
3500 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
3501 std::deque<SavingAndArc>* incoming_reinjected_savings_;
3502 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
3503 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
3504 const int vehicle_types_;
3505 const bool single_vehicle_type_;
3506 bool using_incoming_reinjected_saving_;
3513SavingsFilteredHeuristic::SavingsFilteredHeuristic(
3514 RoutingModel*
model, std::function<
bool()> stop_search,
3517 vehicle_type_curator_(nullptr),
3530 model()->GetVehicleTypeContainer());
3535 if (!ComputeSavings())
return false;
3540 if (!
Evaluate(
true).has_value())
return false;
3546 int type, int64_t before_node, int64_t after_node) {
3547 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
3554 SetNext(
model()->Start(vehicle), before_node, vehicle);
3555 SetNext(before_node, after_node, vehicle);
3561 ->GetCompatibleVehicleOfType(
3562 type, vehicle_is_compatible,
3563 [](
int) {
return false; })
3567void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
3568 std::vector<std::vector<int64_t>>* adjacency_lists) {
3569 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
3570 for (int64_t neighbor : (*adjacency_lists)[node]) {
3571 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
3574 (*adjacency_lists)[neighbor].push_back(node);
3577 absl::c_transform(*adjacency_lists, adjacency_lists->begin(),
3578 [](std::vector<int64_t> vec) {
3580 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
3596bool SavingsFilteredHeuristic::ComputeSavings() {
3600 std::vector<int64_t> uncontained_non_start_end_nodes;
3601 uncontained_non_start_end_nodes.reserve(
size);
3602 for (
int node = 0; node <
size; node++) {
3604 uncontained_non_start_end_nodes.push_back(node);
3608 const int64_t saving_neighbors =
3609 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
3610 static_cast<int64_t
>(uncontained_non_start_end_nodes.size()));
3613 std::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
3616 std::vector<std::vector<int64_t>> adjacency_lists(
size);
3618 for (
int type = 0; type < num_vehicle_types; ++type) {
3621 if (vehicle < 0)
continue;
3623 const int64_t cost_class =
3624 model()->GetCostClassIndexOfVehicle(vehicle).value();
3625 const int64_t
start =
model()->Start(vehicle);
3626 const int64_t
end =
model()->End(vehicle);
3631 for (
int before_node : uncontained_non_start_end_nodes) {
3632 std::vector<std::pair< int64_t, int64_t>>
3634 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
3636 for (
int after_node : uncontained_non_start_end_nodes) {
3637 if (after_node != before_node) {
3638 costed_after_nodes.push_back(std::make_pair(
3639 model()->GetArcCostForClass(before_node, after_node, cost_class),
3643 if (saving_neighbors < costed_after_nodes.size()) {
3644 std::nth_element(costed_after_nodes.begin(),
3645 costed_after_nodes.begin() + saving_neighbors,
3646 costed_after_nodes.end());
3647 costed_after_nodes.resize(saving_neighbors);
3649 adjacency_lists[before_node].resize(costed_after_nodes.size());
3650 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
3651 adjacency_lists[before_node].begin(),
3652 [](std::pair<int64_t, int64_t> cost_and_node) {
3653 return cost_and_node.second;
3657 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
3662 for (
int before_node : uncontained_non_start_end_nodes) {
3663 const int64_t before_to_end_cost =
3664 model()->GetArcCostForClass(before_node,
end, cost_class);
3665 const int64_t start_to_before_cost =
3669 for (int64_t after_node : adjacency_lists[before_node]) {
3670 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
3671 before_node == after_node ||
Contains(after_node)) {
3674 const int64_t arc_cost =
3675 model()->GetArcCostForClass(before_node, after_node, cost_class);
3676 const int64_t start_to_after_cost =
3679 const int64_t after_to_end_cost =
3680 model()->GetArcCostForClass(after_node,
end, cost_class);
3682 const double weighted_arc_cost_fp =
3684 const int64_t weighted_arc_cost =
3685 weighted_arc_cost_fp < std::numeric_limits<int64_t>::max()
3686 ?
static_cast<int64_t
>(weighted_arc_cost_fp)
3687 :
std::numeric_limits<int64_t>::
max();
3688 const int64_t saving_value =
CapSub(
3689 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
3691 const Saving saving =
3692 BuildSaving(-saving_value, type, before_node, after_node);
3694 const int64_t total_cost =
3695 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
3706int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
3707 int num_vehicle_types)
const {
3710 const int64_t num_neighbors_with_ratio =
3717 const double max_memory_usage_in_savings_unit =
3735 if (num_vehicle_types > 1) {
3736 multiplicative_factor += 1.5;
3738 const double num_savings =
3739 max_memory_usage_in_savings_unit / multiplicative_factor;
3740 const int64_t num_neighbors_with_memory_restriction =
3741 std::max(1.0, num_savings / (num_vehicle_types *
size));
3743 return std::min(num_neighbors_with_ratio,
3744 num_neighbors_with_memory_restriction);
3749void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3751 DCHECK_GT(vehicle_types, 0);
3755 std::vector<std::vector<const Saving*>> in_savings_ptr(
size * vehicle_types);
3756 std::vector<std::vector<const Saving*>> out_savings_ptr(
size * vehicle_types);
3757 for (
int type = 0; type < vehicle_types; type++) {
3758 const int vehicle_type_offset = type *
size;
3759 const std::vector<Saving>& sorted_savings_for_type =
3761 for (
const Saving& saving : sorted_savings_for_type) {
3762 DCHECK_EQ(saving.vehicle_type, type);
3763 const int before_node = saving.before_node;
3764 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
3765 const int after_node = saving.after_node;
3766 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
3775 int before_node = saving.before_node;
3776 int after_node = saving.after_node;
3777 const bool nodes_contained =
Contains(before_node) ||
Contains(after_node);
3779 if (nodes_contained) {
3785 const int type = saving.vehicle_type;
3793 const int64_t
start =
model()->Start(vehicle);
3794 const int64_t
end =
model()->End(vehicle);
3798 const int saving_offset = type *
size;
3800 while (in_index < in_savings_ptr[saving_offset + after_node].
size() ||
3801 out_index < out_savings_ptr[saving_offset + before_node].
size()) {
3804 int before_before_node = -1;
3805 int after_after_node = -1;
3806 if (in_index < in_savings_ptr[saving_offset + after_node].
size()) {
3807 const Saving& in_saving =
3808 *(in_savings_ptr[saving_offset + after_node][in_index]);
3809 if (out_index < out_savings_ptr[saving_offset + before_node].
size()) {
3810 const Saving& out_saving =
3811 *(out_savings_ptr[saving_offset + before_node][out_index]);
3812 if (in_saving.saving < out_saving.saving) {
3813 after_after_node = in_saving.after_node;
3815 before_before_node = out_saving.before_node;
3818 after_after_node = in_saving.after_node;
3821 before_before_node =
3822 out_savings_ptr[saving_offset + before_node][out_index]
3826 if (after_after_node != -1) {
3827 DCHECK_EQ(before_before_node, -1);
3831 SetNext(after_node, after_after_node, vehicle);
3835 after_node = after_after_node;
3840 CHECK_GE(before_before_node, 0);
3842 if (!
Contains(before_before_node)) {
3844 SetNext(before_before_node, before_node, vehicle);
3847 before_node = before_before_node;
3858void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
3862 const int vehicles =
model()->vehicles();
3864 first_node_on_route_.resize(vehicles, -1);
3865 last_node_on_route_.resize(vehicles, -1);
3866 vehicle_of_first_or_last_node_.resize(
size, -1);
3868 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
3869 const int64_t
start =
model()->Start(vehicle);
3870 const int64_t
end =
model()->End(vehicle);
3876 vehicle_of_first_or_last_node_[node] = vehicle;
3877 first_node_on_route_[vehicle] = node;
3884 vehicle_of_first_or_last_node_[node] = vehicle;
3885 last_node_on_route_[vehicle] = node;
3892 const int64_t before_node = saving.before_node;
3893 const int64_t after_node = saving.after_node;
3894 const int type = saving.vehicle_type;
3898 bool committed =
false;
3906 vehicle_of_first_or_last_node_[before_node] = vehicle;
3907 vehicle_of_first_or_last_node_[after_node] = vehicle;
3908 first_node_on_route_[vehicle] = before_node;
3909 last_node_on_route_[vehicle] = after_node;
3921 const int v1 = vehicle_of_first_or_last_node_[before_node];
3922 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
3924 const int v2 = vehicle_of_first_or_last_node_[after_node];
3925 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
3927 if (before_node == last_node && after_node == first_node && v1 != v2 &&
3929 CHECK_EQ(
Value(before_node),
model()->End(v1));
3930 CHECK_EQ(
Value(
model()->Start(v2)), after_node);
3936 MergeRoutes(v1, v2, before_node, after_node);
3941 const int vehicle = vehicle_of_first_or_last_node_[before_node];
3942 const int64_t last_node =
3943 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
3945 if (before_node == last_node) {
3946 const int64_t
end =
model()->End(vehicle);
3950 if (type != route_type) {
3958 SetNext(before_node, after_node, vehicle);
3961 if (first_node_on_route_[vehicle] != before_node) {
3963 DCHECK_NE(
Value(
model()->Start(vehicle)), before_node);
3964 vehicle_of_first_or_last_node_[before_node] = -1;
3966 vehicle_of_first_or_last_node_[after_node] = vehicle;
3967 last_node_on_route_[vehicle] = after_node;
3974 const int vehicle = vehicle_of_first_or_last_node_[after_node];
3975 const int64_t first_node =
3976 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
3978 if (after_node == first_node) {
3979 const int64_t
start =
model()->Start(vehicle);
3983 if (type != route_type) {
3991 SetNext(before_node, after_node, vehicle);
3994 if (last_node_on_route_[vehicle] != after_node) {
3996 DCHECK_NE(
Value(after_node),
model()->End(vehicle));
3997 vehicle_of_first_or_last_node_[after_node] = -1;
3999 vehicle_of_first_or_last_node_[before_node] = vehicle;
4000 first_node_on_route_[vehicle] = before_node;
4009void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
4011 int64_t before_node,
4012 int64_t after_node) {
4014 const int64_t new_first_node = first_node_on_route_[first_vehicle];
4015 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
4016 CHECK_EQ(
Value(
model()->Start(first_vehicle)), new_first_node);
4017 const int64_t new_last_node = last_node_on_route_[second_vehicle];
4018 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
4019 CHECK_EQ(
Value(new_last_node),
model()->End(second_vehicle));
4022 int used_vehicle = first_vehicle;
4023 int unused_vehicle = second_vehicle;
4024 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
4025 model()->GetFixedCostOfVehicle(second_vehicle)) {
4026 used_vehicle = second_vehicle;
4027 unused_vehicle = first_vehicle;
4030 SetNext(before_node, after_node, used_vehicle);
4033 if (used_vehicle == first_vehicle) {
4034 SetNext(new_last_node,
model()->End(used_vehicle), used_vehicle);
4036 SetNext(
model()->Start(used_vehicle), new_first_node, used_vehicle);
4038 bool committed =
Evaluate(
true).has_value();
4040 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
4041 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
4043 std::swap(used_vehicle, unused_vehicle);
4044 SetNext(before_node, after_node, used_vehicle);
4047 if (used_vehicle == first_vehicle) {
4048 SetNext(new_last_node,
model()->End(used_vehicle), used_vehicle);
4050 SetNext(
model()->Start(used_vehicle), new_first_node, used_vehicle);
4052 committed =
Evaluate(
true).has_value();
4058 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
4059 model()->GetFixedCostOfVehicle(unused_vehicle));
4062 first_node_on_route_[unused_vehicle] = -1;
4063 last_node_on_route_[unused_vehicle] = -1;
4064 vehicle_of_first_or_last_node_[before_node] = -1;
4065 vehicle_of_first_or_last_node_[after_node] = -1;
4066 first_node_on_route_[used_vehicle] = new_first_node;
4067 last_node_on_route_[used_vehicle] = new_last_node;
4068 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
4069 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
4076 RoutingModel*
model, std::function<
bool()> stop_search,
4079 use_minimum_matching_(use_minimum_matching) {}
4089 std::vector<int> indices(1, 0);
4090 for (
int i = 1; i <
size; ++i) {
4091 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
4092 indices.push_back(i);
4095 const int num_cost_classes =
model()->GetCostClassesCount();
4096 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
4097 std::vector<bool> class_covered(num_cost_classes,
false);
4098 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
4099 const int64_t cost_class =
4100 model()->GetCostClassIndexOfVehicle(vehicle).value();
4101 if (!class_covered[cost_class]) {
4102 class_covered[cost_class] =
true;
4103 const int64_t
start =
model()->Start(vehicle);
4104 const int64_t
end =
model()->End(vehicle);
4105 auto cost = [
this, &indices,
start,
end, cost_class](
int from,
int to) {
4106 DCHECK_LT(from, indices.size());
4107 DCHECK_LT(
to, indices.size());
4108 const int from_index = (from == 0) ?
start : indices[from];
4109 const int to_index = (
to == 0) ?
end : indices[
to];
4110 const int64_t cost =
4111 model()->GetArcCostForClass(from_index, to_index, cost_class);
4116 return std::min(cost, std::numeric_limits<int64_t>::max() / 2);
4118 using Cost =
decltype(cost);
4120 indices.size(), cost);
4121 if (use_minimum_matching_) {
4124 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
4126 if (christofides_solver.
Solve()) {
4127 path_per_cost_class[cost_class] =
4133 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
4134 const int64_t cost_class =
4135 model()->GetCostClassIndexOfVehicle(vehicle).value();
4136 const std::vector<int>& path = path_per_cost_class[cost_class];
4137 if (path.empty())
continue;
4138 DCHECK_EQ(0, path[0]);
4139 DCHECK_EQ(0, path.back());
4142 const int end =
model()->End(vehicle);
4143 for (
int i = 1; i < path.size() - 1 && prev !=
end; ++i) {
4145 int next = indices[path[i]];
4164 SweepIndex(
const int64_t index,
const double angle,
const double distance)
4173struct SweepIndexSortAngle {
4174 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
4175 return (node1.angle < node2.angle);
4177} SweepIndexAngleComparator;
4179struct SweepIndexSortDistance {
4180 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
4181 return (node1.distance < node2.distance);
4183} SweepIndexDistanceComparator;
4187 const std::vector<std::pair<int64_t, int64_t>>& points)
4188 : coordinates_(2 * points.
size(), 0), sectors_(1) {
4189 for (int64_t i = 0; i < points.size(); ++i) {
4190 coordinates_[2 * i] = points[i].first;
4191 coordinates_[2 * i + 1] = points[i].second;
4198 const double pi_rad = 3.14159265;
4200 const int x0 = coordinates_[0];
4201 const int y0 = coordinates_[1];
4203 std::vector<SweepIndex> sweep_indices;
4206 const int x = coordinates_[2 *
index];
4207 const int y = coordinates_[2 *
index + 1];
4208 const double x_delta =
x - x0;
4209 const double y_delta =
y - y0;
4210 double square_distance = x_delta * x_delta + y_delta * y_delta;
4211 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
4213 SweepIndex sweep_index(
index,
angle, square_distance);
4214 sweep_indices.push_back(sweep_index);
4216 absl::c_sort(sweep_indices, SweepIndexDistanceComparator);
4218 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
4219 for (
int sector = 0; sector < sectors_; ++sector) {
4220 std::vector<SweepIndex> cluster;
4221 std::vector<SweepIndex>::iterator begin =
4222 sweep_indices.begin() + sector *
size;
4223 std::vector<SweepIndex>::iterator
end =
4224 sector == sectors_ - 1 ? sweep_indices.end()
4225 : sweep_indices.begin() + (sector + 1) *
size;
4226 std::sort(begin,
end, SweepIndexAngleComparator);
4228 for (
const SweepIndex& sweep_index : sweep_indices) {
4229 indices->push_back(sweep_index.index);
4236 Link(std::pair<int, int> link,
double value,
int vehicle_class,
4237 int64_t start_depot, int64_t end_depot)
4257class RouteConstructor {
4259 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
4260 bool check_assignment, int64_t num_indices,
4261 const std::vector<Link>& links_list)
4262 : assignment_(assignment),
4264 check_assignment_(check_assignment),
4265 solver_(model_->solver()),
4266 num_indices_(num_indices),
4267 links_list_(links_list),
4268 nexts_(model_->Nexts()),
4269 in_route_(num_indices_, -1),
4271 index_to_chain_index_(num_indices, -1),
4272 index_to_vehicle_class_index_(num_indices, -1) {
4274 const std::vector<std::string> dimension_names =
4275 model_->GetAllDimensionNames();
4276 dimensions_.assign(dimension_names.size(),
nullptr);
4277 for (
int i = 0;
i < dimension_names.size(); ++
i) {
4278 dimensions_[
i] = &model_->GetDimensionOrDie(dimension_names[
i]);
4281 cumuls_.resize(dimensions_.size());
4282 for (std::vector<int64_t>& cumuls :
cumuls_) {
4283 cumuls.resize(num_indices_);
4285 new_possible_cumuls_.resize(dimensions_.size());
4288 ~RouteConstructor() {}
4291 model_->solver()->TopPeriodicCheck();
4294 if (!model_->IsStart(
index) && !model_->IsEnd(
index)) {
4295 std::vector<int> route(1,
index);
4296 routes_.push_back(route);
4297 in_route_[
index] = routes_.size() - 1;
4301 for (
const Link&
link : links_list_) {
4302 model_->solver()->TopPeriodicCheck();
4303 const int index1 =
link.link.first;
4304 const int index2 =
link.link.second;
4310 if (index_to_vehicle_class_index_[index1] < 0) {
4311 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4312 ++dimension_index) {
4313 cumuls_[dimension_index][index1] =
4314 std::max(dimensions_[dimension_index]->GetTransitValue(
4316 dimensions_[dimension_index]->CumulVar(index1)->Min());
4319 if (index_to_vehicle_class_index_[index2] < 0) {
4320 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4321 ++dimension_index) {
4322 cumuls_[dimension_index][index2] =
4323 std::max(dimensions_[dimension_index]->GetTransitValue(
4325 dimensions_[dimension_index]->CumulVar(index2)->Min());
4329 const int route_index1 = in_route_[index1];
4330 const int route_index2 = in_route_[index2];
4332 route_index1 >= 0 && route_index2 >= 0 &&
4333 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
4336 if (Merge(merge, route_index1, route_index2)) {
4342 model_->solver()->TopPeriodicCheck();
4346 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4347 if (!deleted_chains_.contains(chain_index)) {
4348 final_chains_.push_back(chains_[chain_index]);
4351 absl::c_sort(final_chains_, ChainComparator);
4352 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
4353 if (!deleted_routes_.contains(route_index)) {
4354 final_routes_.push_back(routes_[route_index]);
4357 absl::c_sort(final_routes_, RouteComparator);
4359 const int extra_vehicles = std::max(
4360 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
4362 int chain_index = 0;
4363 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
4365 if (chain_index - extra_vehicles >= model_->vehicles()) {
4368 const int start = final_chains_[chain_index].head;
4369 const int end = final_chains_[chain_index].tail;
4371 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
4372 assignment_->SetValue(
4373 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
start);
4374 assignment_->Add(nexts_[
end]);
4375 assignment_->SetValue(nexts_[
end],
4376 model_->End(chain_index - extra_vehicles));
4380 for (
int route_index = 0; route_index < final_routes_.size();
4382 if (chain_index - extra_vehicles >= model_->vehicles()) {
4385 DCHECK_LT(route_index, final_routes_.size());
4386 const int head = final_routes_[route_index].front();
4387 const int tail = final_routes_[route_index].back();
4390 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
4391 assignment_->SetValue(
4392 model_->NextVar(model_->Start(chain_index - extra_vehicles)),
head);
4393 assignment_->Add(nexts_[
tail]);
4394 assignment_->SetValue(nexts_[
tail],
4395 model_->End(chain_index - extra_vehicles));
4403 if (!assignment_->Contains(
next)) {
4404 assignment_->Add(
next);
4413 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
4416 bool operator()(
const std::vector<int>& route1,
4417 const std::vector<int>& route2)
const {
4418 return (route1.size() < route2.size());
4429 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
4430 return (chain1.nodes < chain2.nodes);
4434 bool Head(
int node)
const {
4435 return (node == routes_[in_route_[node]].front());
4438 bool Tail(
int node)
const {
4439 return (node == routes_[in_route_[node]].back());
4442 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
4443 int dimension_index) {
4444 const RoutingDimension& dimension = *dimensions_[dimension_index];
4445 std::vector<int>::const_iterator it = route.begin();
4446 int64_t cumul = route_cumul;
4447 while (it != route.end()) {
4448 const int previous = *it;
4449 const int64_t cumul_previous = cumul;
4453 if (it == route.end()) {
4456 const int next = *it;
4457 int64_t available_from_previous =
4458 cumul_previous + dimension.GetTransitValue(previous,
next, 0);
4459 int64_t available_cumul_next =
4460 std::max(cumuls_[dimension_index][
next], available_from_previous);
4462 const int64_t slack = available_cumul_next - available_from_previous;
4463 if (slack > dimension.SlackVar(previous)->Max()) {
4464 available_cumul_next =
4465 available_from_previous + dimension.SlackVar(previous)->Max();
4468 if (available_cumul_next > dimension.CumulVar(
next)->Max()) {
4471 if (available_cumul_next <= cumuls_[dimension_index][
next]) {
4474 cumul = available_cumul_next;
4479 bool CheckRouteConnection(
const std::vector<int>& route1,
4480 const std::vector<int>& route2,
int dimension_index,
4482 const int tail1 = route1.back();
4483 const int head2 = route2.front();
4484 const int tail2 = route2.back();
4485 const RoutingDimension& dimension = *dimensions_[dimension_index];
4486 int non_depot_node = -1;
4487 for (
int node = 0; node < num_indices_; ++node) {
4488 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
4489 non_depot_node = node;
4493 CHECK_GE(non_depot_node, 0);
4494 const int64_t depot_threshold =
4495 std::max(dimension.SlackVar(non_depot_node)->Max(),
4496 dimension.CumulVar(non_depot_node)->Max());
4498 int64_t available_from_tail1 =
cumuls_[dimension_index][tail1] +
4499 dimension.GetTransitValue(tail1, head2, 0);
4500 int64_t new_available_cumul_head2 =
4501 std::max(cumuls_[dimension_index][head2], available_from_tail1);
4503 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
4504 if (slack > dimension.SlackVar(tail1)->Max()) {
4505 new_available_cumul_head2 =
4506 available_from_tail1 + dimension.SlackVar(tail1)->Max();
4509 bool feasible_route =
true;
4510 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
4513 if (new_available_cumul_head2 <= cumuls_[dimension_index][head2]) {
4518 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
4519 const int64_t new_possible_cumul_tail2 =
4520 new_possible_cumuls_[dimension_index].contains(tail2)
4521 ? new_possible_cumuls_[dimension_index][tail2]
4522 :
cumuls_[dimension_index][tail2];
4524 if (!feasible_route || (new_possible_cumul_tail2 +
4525 dimension.GetTransitValue(tail2,
end_depot, 0) >
4532 bool FeasibleMerge(
const std::vector<int>& route1,
4533 const std::vector<int>& route2,
int node1,
int node2,
4536 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
4541 if (!((index_to_vehicle_class_index_[node1] == -1 &&
4542 index_to_vehicle_class_index_[node2] == -1) ||
4544 index_to_vehicle_class_index_[node2] == -1) ||
4545 (index_to_vehicle_class_index_[node1] == -1 &&
4554 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4555 ++dimension_index) {
4556 new_possible_cumuls_[dimension_index].clear();
4557 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
4566 bool CheckTempAssignment(Assignment*
const temp_assignment,
4567 int new_chain_index,
int old_chain_index,
int head1,
4568 int tail1,
int head2,
int tail2) {
4571 if (new_chain_index >= model_->vehicles())
return false;
4572 const int start = head1;
4573 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
4574 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
4576 temp_assignment->Add(nexts_[tail1]);
4577 temp_assignment->SetValue(nexts_[tail1], head2);
4578 temp_assignment->Add(nexts_[tail2]);
4579 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
4580 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4581 if ((chain_index != new_chain_index) &&
4582 (chain_index != old_chain_index) &&
4583 (!deleted_chains_.contains(chain_index))) {
4584 const int start = chains_[chain_index].head;
4585 const int end = chains_[chain_index].tail;
4586 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
4587 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
4589 temp_assignment->Add(nexts_[
end]);
4590 temp_assignment->SetValue(nexts_[
end], model_->End(chain_index));
4593 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
4596 bool UpdateAssignment(
const std::vector<int>& route1,
4597 const std::vector<int>& route2) {
4598 bool feasible =
true;
4599 const int head1 = route1.front();
4600 const int tail1 = route1.back();
4601 const int head2 = route2.front();
4602 const int tail2 = route2.back();
4603 const int chain_index1 = index_to_chain_index_[head1];
4604 const int chain_index2 = index_to_chain_index_[head2];
4605 if (chain_index1 < 0 && chain_index2 < 0) {
4606 const int chain_index = chains_.size();
4607 if (check_assignment_) {
4608 Assignment*
const temp_assignment =
4609 solver_->MakeAssignment(assignment_);
4610 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
4611 tail1, head2, tail2);
4618 index_to_chain_index_[head1] = chain_index;
4619 index_to_chain_index_[tail2] = chain_index;
4620 chains_.push_back(chain);
4622 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
4623 if (check_assignment_) {
4624 Assignment*
const temp_assignment =
4625 solver_->MakeAssignment(assignment_);
4627 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4628 head1, tail1, head2, tail2);
4631 index_to_chain_index_[tail2] = chain_index1;
4632 chains_[chain_index1].head = head1;
4633 chains_[chain_index1].tail = tail2;
4634 ++chains_[chain_index1].nodes;
4636 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
4637 if (check_assignment_) {
4638 Assignment*
const temp_assignment =
4639 solver_->MakeAssignment(assignment_);
4641 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
4642 head1, tail1, head2, tail2);
4645 index_to_chain_index_[head1] = chain_index2;
4646 chains_[chain_index2].head = head1;
4647 chains_[chain_index2].tail = tail2;
4648 ++chains_[chain_index2].nodes;
4651 if (check_assignment_) {
4652 Assignment*
const temp_assignment =
4653 solver_->MakeAssignment(assignment_);
4655 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
4656 head1, tail1, head2, tail2);
4659 index_to_chain_index_[tail2] = chain_index1;
4660 chains_[chain_index1].head = head1;
4661 chains_[chain_index1].tail = tail2;
4662 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
4663 deleted_chains_.insert(chain_index2);
4667 assignment_->Add(nexts_[tail1]);
4668 assignment_->SetValue(nexts_[tail1], head2);
4673 bool Merge(
bool merge,
int index1,
int index2) {
4675 if (UpdateAssignment(routes_[index1], routes_[index2])) {
4677 for (
const int node : routes_[index2]) {
4678 in_route_[node] = index1;
4679 routes_[index1].push_back(node);
4681 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4682 ++dimension_index) {
4683 for (
const std::pair<int, int64_t> new_possible_cumul :
4684 new_possible_cumuls_[dimension_index]) {
4685 cumuls_[dimension_index][new_possible_cumul.first] =
4686 new_possible_cumul.second;
4689 deleted_routes_.insert(index2);
4696 Assignment*
const assignment_;
4697 RoutingModel*
const model_;
4698 const bool check_assignment_;
4699 Solver*
const solver_;
4700 const int64_t num_indices_;
4701 const std::vector<Link> links_list_;
4702 std::vector<IntVar*> nexts_;
4703 std::vector<const RoutingDimension*> dimensions_;
4704 std::vector<std::vector<int64_t>>
cumuls_;
4705 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
4706 std::vector<std::vector<int>> routes_;
4707 std::vector<int> in_route_;
4708 absl::flat_hash_set<int> deleted_routes_;
4709 std::vector<std::vector<int>> final_routes_;
4710 std::vector<Chain> chains_;
4711 absl::flat_hash_set<int> deleted_chains_;
4712 std::vector<Chain> final_chains_;
4713 std::vector<int> index_to_chain_index_;
4714 std::vector<int> index_to_vehicle_class_index_;
4720class SweepBuilder :
public DecisionBuilder {
4722 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
4723 : model_(
model), check_assignment_(check_assignment) {}
4724 ~SweepBuilder()
override {}
4726 Decision*
Next(Solver*
const solver)
override {
4731 Assignment*
const assignment = solver->MakeAssignment();
4732 route_constructor_ = std::make_unique<RouteConstructor>(
4733 assignment, model_, check_assignment_, num_indices_, links_);
4735 route_constructor_->Construct();
4736 route_constructor_.reset(
nullptr);
4738 assignment->Restore();
4745 const int depot = model_->GetDepot();
4746 num_indices_ = model_->Size() + model_->vehicles();
4747 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
4748 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
4749 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
4751 std::vector<int64_t> indices;
4752 model_->sweep_arranger()->ArrangeIndices(&indices);
4753 for (
int i = 0;
i < indices.size() - 1; ++
i) {
4754 const int64_t first = indices[
i];
4755 const int64_t second = indices[
i + 1];
4756 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
4757 (model_->IsStart(second) || !model_->IsEnd(second))) {
4758 if (first != depot && second != depot) {
4759 Link
link(std::make_pair(first, second), 0, 0, depot, depot);
4760 links_.push_back(
link);
4766 RoutingModel*
const model_;
4767 std::unique_ptr<RouteConstructor> route_constructor_;
4768 const bool check_assignment_;
4769 int64_t num_indices_;
4770 std::vector<Link> links_;
4775 bool check_assignment) {
4776 return model->solver()->RevAlloc(
new SweepBuilder(
model, check_assignment));
4785class AllUnperformed :
public DecisionBuilder {
4788 explicit AllUnperformed(RoutingModel*
const model) : model_(
model) {}
4789 ~AllUnperformed()
override {}
4790 Decision*
Next(Solver*
const )
override {
4793 model_->CostVar()->FreezeQueue();
4794 for (
int i = 0;
i < model_->Size(); ++
i) {
4795 if (!model_->IsStart(i)) {
4796 model_->ActiveVar(i)->SetValue(0);
4799 model_->CostVar()->UnfreezeQueue();
4804 RoutingModel*
const model_;
4809 return model->solver()->RevAlloc(
new AllUnperformed(
model));
4814class GuidedSlackFinalizer :
public DecisionBuilder {
4816 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel*
model,
4817 std::function<int64_t(int64_t)> initializer);
4820 GuidedSlackFinalizer(
const GuidedSlackFinalizer&) =
delete;
4821 GuidedSlackFinalizer& operator=(
const GuidedSlackFinalizer&) =
delete;
4823 Decision*
Next(Solver* solver)
override;
4826 int64_t SelectValue(int64_t
index);
4827 int64_t ChooseVariable();
4829 const RoutingDimension*
const dimension_;
4830 RoutingModel*
const model_;
4831 const std::function<int64_t(int64_t)> initializer_;
4832 RevArray<bool> is_initialized_;
4833 std::vector<int64_t> initial_values_;
4834 Rev<int64_t> current_index_;
4835 Rev<int64_t> current_route_;
4836 RevArray<int64_t> last_delta_used_;
4839GuidedSlackFinalizer::GuidedSlackFinalizer(
4840 const RoutingDimension* dimension, RoutingModel*
model,
4841 std::function<int64_t(int64_t)> initializer)
4842 : dimension_(ABSL_DIE_IF_NULL(dimension)),
4843 model_(ABSL_DIE_IF_NULL(
model)),
4844 initializer_(
std::move(initializer)),
4845 is_initialized_(dimension->slacks().
size(),
false),
4846 initial_values_(dimension->slacks().
size(),
4847 std::numeric_limits<int64_t>::
min()),
4848 current_index_(model_->Start(0)),
4850 last_delta_used_(dimension->slacks().
size(), 0) {}
4852Decision* GuidedSlackFinalizer::Next(Solver* solver) {
4853 CHECK_EQ(solver, model_->solver());
4854 const int node_idx = ChooseVariable();
4855 CHECK(node_idx == -1 ||
4856 (node_idx >= 0 && node_idx < dimension_->slacks().
size()));
4857 if (node_idx != -1) {
4858 if (!is_initialized_[node_idx]) {
4859 initial_values_[node_idx] = initializer_(node_idx);
4860 is_initialized_.SetValue(solver, node_idx,
true);
4862 const int64_t
value = SelectValue(node_idx);
4863 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
4864 return solver->MakeAssignVariableValue(slack_variable,
value);
4869int64_t GuidedSlackFinalizer::SelectValue(int64_t
index) {
4870 const IntVar*
const slack_variable = dimension_->SlackVar(
index);
4871 const int64_t center = initial_values_[
index];
4872 const int64_t max_delta =
4873 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
4879 while (std::abs(
delta) < max_delta &&
4880 !slack_variable->Contains(center +
delta)) {
4887 last_delta_used_.SetValue(model_->solver(),
index,
delta);
4888 return center +
delta;
4891int64_t GuidedSlackFinalizer::ChooseVariable() {
4892 int64_t int_current_node = current_index_.Value();
4893 int64_t int_current_route = current_route_.Value();
4895 while (int_current_route < model_->vehicles()) {
4896 while (!model_->IsEnd(int_current_node) &&
4897 dimension_->SlackVar(int_current_node)->Bound()) {
4898 int_current_node = model_->NextVar(int_current_node)->Value();
4900 if (!model_->IsEnd(int_current_node)) {
4903 int_current_route += 1;
4904 if (int_current_route < model_->vehicles()) {
4905 int_current_node = model_->Start(int_current_route);
4909 CHECK(int_current_route == model_->vehicles() ||
4910 !dimension_->SlackVar(int_current_node)->Bound());
4911 current_index_.SetValue(model_->solver(), int_current_node);
4912 current_route_.SetValue(model_->solver(), int_current_route);
4913 if (int_current_route < model_->vehicles()) {
4914 return int_current_node;
4921DecisionBuilder* RoutingModel::MakeGuidedSlackFinalizer(
4922 const RoutingDimension* dimension,
4923 std::function<int64_t(int64_t)> initializer) {
4924 return solver_->RevAlloc(
4925 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
4928int64_t RoutingDimension::ShortestTransitionSlack(int64_t node)
const {
4929 CHECK_EQ(base_dimension_,
this);
4930 CHECK(!model_->IsEnd(node));
4934 const int64_t
next = model_->NextVar(node)->Value();
4935 if (model_->IsEnd(
next)) {
4936 return SlackVar(node)->Min();
4938 const int64_t next_next = model_->NextVar(
next)->Value();
4939 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
4940 CHECK_EQ(serving_vehicle, model_->VehicleVar(
next)->Value());
4941 const RoutingModel::StateDependentTransit transit_from_next =
4942 model_->StateDependentTransitCallback(
4943 state_dependent_class_evaluators_
4944 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
4947 const int64_t next_cumul_min = CumulVar(
next)->Min();
4948 const int64_t next_cumul_max = CumulVar(
next)->Max();
4949 const int64_t optimal_next_cumul =
4950 transit_from_next.transit_plus_identity->RangeMinArgument(
4951 next_cumul_min, next_cumul_max + 1);
4953 DCHECK_LE(next_cumul_min, optimal_next_cumul);
4954 DCHECK_LE(optimal_next_cumul, next_cumul_max);
4959 const int64_t current_cumul = CumulVar(node)->Value();
4960 const int64_t current_state_independent_transit = model_->TransitCallback(
4961 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
4962 const int64_t current_state_dependent_transit =
4964 ->StateDependentTransitCallback(
4965 state_dependent_class_evaluators_
4966 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
4968 .transit->Query(current_cumul);
4969 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
4970 current_state_independent_transit -
4971 current_state_dependent_transit;
4972 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
4973 CHECK_LE(optimal_slack, SlackVar(node)->Max());
4974 return optimal_slack;
4978class GreedyDescentLSOperator :
public LocalSearchOperator {
4980 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
4983 GreedyDescentLSOperator(
const GreedyDescentLSOperator&) =
delete;
4984 GreedyDescentLSOperator& operator=(
const GreedyDescentLSOperator&) =
delete;
4986 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
4987 void Start(
const Assignment* assignment)
override;
4990 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
4992 const std::vector<IntVar*> variables_;
4993 const Assignment* center_;
4994 int64_t current_step_;
5001 int64_t current_direction_;
5004GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
5005 : variables_(
std::move(variables)),
5008 current_direction_(0) {}
5010bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
5012 static const int64_t sings[] = {1, -1};
5013 for (; 1 <= current_step_; current_step_ /= 2) {
5014 for (; current_direction_ < 2 * variables_.size();) {
5015 const int64_t variable_idx = current_direction_ / 2;
5016 IntVar*
const variable = variables_[variable_idx];
5017 const int64_t sign_index = current_direction_ % 2;
5018 const int64_t sign = sings[sign_index];
5019 const int64_t offset = sign * current_step_;
5020 const int64_t new_value = center_->Value(variable) + offset;
5021 ++current_direction_;
5022 if (variable->Contains(new_value)) {
5023 delta->Add(variable);
5024 delta->SetValue(variable, new_value);
5028 current_direction_ = 0;
5033void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
5034 CHECK(assignment !=
nullptr);
5035 current_step_ = FindMaxDistanceToDomain(assignment);
5036 center_ = assignment;
5039int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
5040 const Assignment* assignment) {
5041 int64_t result = std::numeric_limits<int64_t>::min();
5042 for (
const IntVar*
const var : variables_) {
5043 result = std::max(result, std::abs(
var->Max() - assignment->Value(
var)));
5044 result = std::max(result, std::abs(
var->Min() - assignment->Value(
var)));
5050std::unique_ptr<LocalSearchOperator> RoutingModel::MakeGreedyDescentLSOperator(
5051 std::vector<IntVar*> variables) {
5052 return std::unique_ptr<LocalSearchOperator>(
5053 new GreedyDescentLSOperator(std::move(variables)));
5056DecisionBuilder* RoutingModel::MakeSelfDependentDimensionFinalizer(
5057 const RoutingDimension* dimension) {
5058 CHECK(dimension !=
nullptr);
5059 CHECK(dimension->base_dimension() == dimension);
5060 std::function<int64_t(int64_t)> slack_guide = [dimension](int64_t
index) {
5061 return dimension->ShortestTransitionSlack(
index);
5063 DecisionBuilder*
const guided_finalizer =
5064 MakeGuidedSlackFinalizer(dimension, slack_guide);
5065 DecisionBuilder*
const slacks_finalizer =
5066 solver_->MakeSolveOnce(guided_finalizer);
5067 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
5068 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
5069 start_cumuls[vehicle_idx] = dimension->CumulVar(Start(vehicle_idx));
5071 LocalSearchOperator*
const hill_climber =
5072 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
5073 LocalSearchPhaseParameters*
const parameters =
5074 solver_->MakeLocalSearchPhaseParameters(CostVar(), hill_climber,
5076 Assignment*
const first_solution = solver_->MakeAssignment();
5077 first_solution->Add(start_cumuls);
5078 for (IntVar*
const cumul : start_cumuls) {
5079 first_solution->SetValue(cumul, cumul->Min());
5081 DecisionBuilder*
const finalizer =
5082 solver_->MakeLocalSearchPhase(first_solution,
parameters);
const std::vector< IntVar * > vars_
bool Contains(const T *val) const
void NoteChangedPriority(T *val)
const E & Element(const V *const var) const
E * AddAtPosition(V *var, int position)
IntContainer * MutableIntVarContainer()
const IntContainer & IntVarContainer() const
void ClearItems()
Removes all items from all bins.
bool RemoveItemFromBin(int item, int bin)
Removes item from bin, return whether the bin is feasible.
bool CheckAdditionsFeasibility(const std::vector< int > &items, int bin) const
bool CheckAdditionFeasibility(int item, int bin) const
Checks whether adding item(s) is feasible w.r.t. dimensions.
int64_t TotalCost() const
Returns the total cost incurred by violating soft costs.
bool AddItemToBin(int item, int bin)
IndexType size() const
Returns how many bits this Bitset64 can hold.
void Set(IndexType i)
Sets the bit at position i to 1.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
CheapestAdditionFilteredHeuristic.
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
void InitializeSeedQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, SeedQueue *sq)
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(const std::vector< int > &vehicles)
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void AppendInsertionPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int vehicle, bool ignore_cost, std::vector< NodeInsertion > *node_insertions)
int64_t GetUnperformedValue(int64_t node_to_insert) const
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor, int vehicle=-1)
std::function< int64_t(int64_t)> penalty_evaluator_
void AddSeedNodeToQueue(int node, std::vector< StartEndValue > *start_end_distances, SeedQueue *sq)
clang-format on
ChristofidesFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
ChristofidesFilteredHeuristic.
bool BuildSolutionInternal() override
void SetMatchingAlgorithm(MatchingAlgorithm matching)
std::vector< NodeIndex > TravelingSalesmanPath()
Returns the approximate TSP tour.
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
NodeEntryQueue(int num_nodes)
void PushInsertion(int64_t node, int64_t insert_after, int vehicle, int bucket, int64_t value)
bool IsEmpty(int64_t insert_after) const
void ClearInsertions(int64_t insert_after)
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void AddInsertionSequence(int vehicle, std::initializer_list< Insertion > insertion_sequence)
void AppendPickupDeliveryMultitourInsertions(int pickup, int delivery, int vehicle, const std::vector< int > &path, const std::vector< bool > &path_node_is_pickup, const std::vector< bool > &path_node_is_delivery, InsertionSequenceContainer &insertions)
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64_t Min() const =0
int64_t number_of_rejects() const
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
std::string DebugString() const override
-------— Decision Builder -------—
Decision * Next(Solver *solver) override
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
— First solution decision builder —
Generic filter-based heuristic applied to IntVars.
bool HasSecondaryVars() const
Returns true if there are secondary variables.
Assignment *const assignment_
void ResetSolution()
Resets the data members for a new solution.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, LocalSearchFilterManager *filter_manager)
— First solution heuristics —
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
virtual void Initialize()
Initialize the heuristic; called before starting to build a new solution.
Assignment * BuildSolution()
int64_t SecondaryVarIndex(int64_t index) const
Returns the index of a secondary var.
int64_t Value(int64_t index) const
void SetValue(int64_t index, int64_t value)
virtual bool InitializeSolution()
Virtual method to initialize the solution.
const std::vector< int > & delta_indices() const
Returns the indices of the nodes currently in the insertion delta.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
std::optional< int64_t > Evaluate(bool commit)
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
bool IsSecondaryVar(int64_t index) const
Returns true if 'index' is a secondary variable index.
virtual int64_t Value() const =0
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, LocalSearchFilterManager *filter_manager, BinCapacities *bin_capacities=nullptr, std::function< bool(const std::vector< RoutingModel::VariableValuePair > &, std::vector< RoutingModel::VariableValuePair > *)> optimize_on_insertion=nullptr)
Takes ownership of evaluator.
void Initialize() override
Initialize the heuristic; called before starting to build a new solution.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
int64_t GetAcceptedObjectiveValue() const
void Synchronize(const Assignment *assignment, const Assignment *delta)
Synchronizes all filters to assignment.
bool Accept(LocalSearchMonitor *monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
Filter-based heuristic dedicated to routing.
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
virtual void ResetVehicleIndices()
virtual void SetVehicleIndex(int64_t, int)
Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
void SetNext(int64_t node, int64_t next, int vehicle)
bool VehicleIsEmpty(int vehicle) const
void MakeDisjunctionNodesUnperformed(int64_t node)
bool MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed, always returns true.
RoutingFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
RoutingFilteredHeuristic.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
RoutingModel * model() const
bool StopSearch() override
Returns true if the search must be stopped.
void MakePartiallyPerformedPairsUnperformed()
void ReinjectSkippedSavingsStartingAt(int64_t node)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
void Update(bool update_best_saving, int type=-1)
void ReinjectSkippedSavingsEndingAt(int64_t node)
void InitializeContainer(int64_t size, int64_t saving_neighbors)
void UpdateWithType(int type)
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
virtual void BuildRoutesFromSavings()=0
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
~SavingsFilteredHeuristic() override
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
std::unique_ptr< SavingsContainer< Saving > > savings_container_
clang-format off
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
clang-format on
For the time being, Solver is neither MT_SAFE nor MT_HOT.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
int NumberOfSetCallsWithDifferentArguments() const
SweepArranger(const std::vector< std::pair< int64_t, int64_t > > &points)
void ArrangeIndices(std::vector< int64_t > *indices)
void Update(const std::function< bool(int)> &remove_vehicle)
bool HasCompatibleVehicleOfType(int type, const std::function< bool(int)> &vehicle_is_compatible) const
std::pair< int, int > GetCompatibleVehicleOfType(int type, const std::function< bool(int)> &vehicle_is_compatible, const std::function< bool(int)> &stop_and_return_vehicle)
void Reset(const std::function< bool(int)> &store_vehicle)
— VehicleTypeCurator —
const std::vector< IntVar * > cumuls_
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
void STLClearObject(T *obj)
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
In SWIG mode, we don't want anything besides these top-level includes.
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
Returns a DecisionBuilder making all nodes unperformed.
int64_t CapAdd(int64_t x, int64_t y)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
std::vector< int64_t > ComputeVehicleEndChainStarts(const RoutingModel &model)
int64_t CapSub(int64_t x, int64_t y)
static const int kUnassigned
--— Routing model --—
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
int64_t CapOpp(int64_t v)
Note(user): -kint64min != kint64max, but kint64max == ~kint64min.
trees with all degrees equal to
int64_t fixed_cost
Contrarily to CostClass, here we need strict equivalence.
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::optional< int64_t > end
const bool prioritize_farthest_nodes
std::priority_queue< Seed, std::vector< Seed >, std::greater< Seed > > priority_queue
bool add_unperformed_entries
bool use_neighbors_ratio_for_initialization
double farthest_seeds_ratio
bool is_sequential
Whether the routes are constructed sequentially or in parallel.
bool operator<(const Entry &other) const
double max_memory_usage_bytes
static const int64_t kint64max