37#include "absl/algorithm/container.h"
38#include "absl/base/attributes.h"
39#include "absl/container/btree_set.h"
40#include "absl/container/flat_hash_map.h"
41#include "absl/container/flat_hash_set.h"
42#include "absl/container/inlined_vector.h"
43#include "absl/flags/flag.h"
44#include "absl/log/check.h"
45#include "absl/log/die_if_null.h"
46#include "absl/log/log.h"
47#include "absl/strings/str_cat.h"
48#include "absl/strings/string_view.h"
49#include "absl/time/time.h"
50#include "absl/types/span.h"
76ABSL_FLAG(
bool, routing_shift_insertion_cost_by_penalty,
true,
77 "Shift insertion costs by the penalty of the inserted node(s).");
80 "The number of sectors the space is divided into before it is sweeped"
86void ConvertAssignment(
const RoutingModel* src_model,
const Assignment* src,
87 const RoutingModel* dst_model, Assignment* dst) {
88 DCHECK_EQ(src_model->Nexts().size(), dst_model->Nexts().size());
89 DCHECK_NE(src,
nullptr);
90 DCHECK_EQ(src_model->solver(), src->solver());
91 DCHECK_EQ(dst_model->solver(), dst->solver());
92 for (
int i = 0; i < src_model->Nexts().size(); i++) {
93 IntVar*
const dst_next_var = dst_model->NextVar(i);
94 if (!dst->Contains(dst_next_var)) {
95 dst->Add(dst_next_var);
97 dst->SetValue(dst_next_var, src->Value(src_model->NextVar(i)));
104 for (
IntVar* next_var : model.Nexts()) {
105 if (assignment1.Value(next_var) != assignment2.Value(next_var)) {
113 return search_parameters.has_time_limit()
116 : absl::InfiniteDuration();
122bool UpdateTimeLimits(
Solver* solver, int64_t start_time_ms,
123 absl::Duration time_limit,
125 if (!search_parameters.has_time_limit())
return true;
126 const absl::Duration elapsed_time =
127 absl::Milliseconds(solver->wall_time() - start_time_ms);
128 const absl::Duration time_left =
129 std::min(time_limit - elapsed_time, GetTimeLimit(search_parameters));
130 if (time_left <= absl::ZeroDuration())
return false;
132 time_left, search_parameters.mutable_time_limit());
140 const std::vector<RoutingModel*>& alternative_models,
142 int max_non_improving_iterations) {
144 nullptr, primary_model, alternative_models, parameters,
145 max_non_improving_iterations);
150 const std::vector<RoutingModel*>& alternative_models,
152 int max_non_improving_iterations) {
154 assignment, primary_model, parameters, alternative_models, {},
155 max_non_improving_iterations);
161 const std::vector<RoutingModel*>& alternative_models,
162 const std::vector<RoutingSearchParameters>& alternative_parameters,
163 int max_non_improving_iterations) {
165 if (max_non_improving_iterations < 0)
return nullptr;
167 if (max_non_improving_iterations == 0 || alternative_models.empty()) {
177 const bool run_metaheuristic_phase =
180 if (run_metaheuristic_phase) {
184 std::vector<RoutingSearchParameters> mutable_alternative_parameters =
185 alternative_parameters;
186 DCHECK(mutable_alternative_parameters.empty() ||
187 mutable_alternative_parameters.size() == alternative_models.size());
189 mutable_alternative_parameters) {
190 if (!mutable_alternative_parameter.has_time_limit() &&
192 *mutable_alternative_parameter.mutable_time_limit() =
196 const absl::Duration time_limit = GetTimeLimit(mutable_search_parameters);
198 std::vector<Assignment*> first_solutions;
199 first_solutions.reserve(alternative_models.size());
201 first_solutions.push_back(model->solver()->MakeAssignment());
206 DCHECK(assignment ==
nullptr ||
209 assignment, mutable_search_parameters);
210 if (
solution ==
nullptr)
return nullptr;
213 while (iteration < max_non_improving_iterations) {
218 for (
int i = 0; i < alternative_models.size(); ++i) {
220 mutable_alternative_parameters.empty()
221 ? mutable_search_parameters
222 : mutable_alternative_parameters[i];
223 if (!UpdateTimeLimits(primary_model->
solver(), start_time_ms, time_limit,
224 mutable_alternative_parameter)) {
225 return best_assignment;
227 ConvertAssignment(primary_model,
solution, alternative_models[i],
229 solution = alternative_models[i]->SolveFromAssignmentWithParameters(
230 first_solutions[i], mutable_alternative_parameter);
234 if (!UpdateTimeLimits(primary_model->
solver(), start_time_ms, time_limit,
235 mutable_search_parameters)) {
236 return best_assignment;
238 ConvertAssignment(alternative_models[i],
solution, primary_model,
239 primary_first_solution);
241 primary_first_solution, mutable_search_parameters);
242 if (
solution ==
nullptr)
return best_assignment;
245 if (AreAssignmentsEquivalent(*primary_model, *
solution,
246 *current_solution)) {
251 if (AreAssignmentsEquivalent(*primary_model, *
solution, *best_assignment)) {
258 if (run_metaheuristic_phase &&
259 UpdateTimeLimits(primary_model->
solver(), start_time_ms, time_limit,
260 mutable_search_parameters)) {
263 best_assignment, mutable_search_parameters);
265 return best_assignment;
271 const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
272 vehicle_type_container_->sorted_vehicle_classes_per_type;
273 sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
274 const std::vector<std::deque<int>>& all_vehicles_per_class =
275 vehicle_type_container_->vehicles_per_vehicle_class;
276 vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
278 for (
int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
279 std::set<VehicleClassEntry>& stored_class_entries =
280 sorted_vehicle_classes_per_type_[type];
281 stored_class_entries.clear();
282 for (VehicleClassEntry class_entry : all_vehicle_classes_per_type[type]) {
283 const int vehicle_class = class_entry.vehicle_class;
284 std::vector<int>& stored_vehicles =
285 vehicles_per_vehicle_class_[vehicle_class];
286 stored_vehicles.clear();
287 for (
int vehicle : all_vehicles_per_class[vehicle_class]) {
288 if (store_vehicle(vehicle)) {
289 stored_vehicles.push_back(vehicle);
292 if (!stored_vehicles.empty()) {
293 stored_class_entries.insert(class_entry);
300 const std::function<
bool(
int)>& remove_vehicle) {
301 for (std::set<VehicleClassEntry>& class_entries :
302 sorted_vehicle_classes_per_type_) {
303 auto class_entry_it = class_entries.begin();
304 while (class_entry_it != class_entries.end()) {
305 const int vehicle_class = class_entry_it->vehicle_class;
306 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
307 vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
308 [&remove_vehicle](
int vehicle) {
309 return remove_vehicle(vehicle);
312 if (vehicles.empty()) {
313 class_entry_it = class_entries.erase(class_entry_it);
322 int type,
const std::function<
bool(
int)>& vehicle_is_compatible)
const {
323 for (
const VehicleClassEntry& vehicle_class_entry :
324 sorted_vehicle_classes_per_type_[type]) {
326 vehicles_per_vehicle_class_[vehicle_class_entry.vehicle_class]) {
327 if (vehicle_is_compatible(vehicle))
return true;
334 int type,
const std::function<
bool(
int)>& vehicle_is_compatible,
335 const std::function<
bool(
int)>& stop_and_return_vehicle) {
336 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
337 sorted_vehicle_classes_per_type_[type];
338 auto vehicle_class_it = sorted_classes.begin();
340 while (vehicle_class_it != sorted_classes.end()) {
341 const int vehicle_class = vehicle_class_it->vehicle_class;
342 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
343 DCHECK(!vehicles.empty());
345 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
347 const int vehicle = *vehicle_it;
348 if (vehicle_is_compatible(vehicle)) {
349 vehicles.erase(vehicle_it);
350 if (vehicles.empty()) {
351 sorted_classes.erase(vehicle_class_it);
353 return {vehicle, -1};
355 if (stop_and_return_vehicle(vehicle)) {
356 return {-1, vehicle};
375 bool has_pickup_deliveries,
bool has_node_precedences,
376 bool has_single_vehicle_node) {
377 if (has_pickup_deliveries || has_node_precedences) {
380 if (has_single_vehicle_node) {
387 const int64_t size = model.
Size();
388 const int num_vehicles = model.
vehicles();
396 std::vector<int64_t> starts(size + num_vehicles, -1);
397 std::vector<int64_t> ends(size + num_vehicles, -1);
398 for (
int node = 0; node < size + num_vehicles; ++node) {
403 std::vector<bool> touched(size,
false);
404 for (
int node = 0; node < size; ++node) {
406 while (!model.
IsEnd(current) && !touched[current]) {
407 touched[current] =
true;
409 if (next_var->
Bound()) {
410 current = next_var->
Value();
415 starts[ends[current]] = starts[node];
416 ends[starts[node]] = ends[current];
420 std::vector<int64_t> end_chain_starts(num_vehicles);
421 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
422 end_chain_starts[vehicle] = starts[model.
End(vehicle)];
424 return end_chain_starts;
432 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
433 : heuristic_(
std::move(heuristic)) {}
436 Assignment*
const assignment = heuristic_->BuildSolution();
437 if (assignment !=
nullptr) {
438 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
439 VLOG(2) <<
"Number of rejected decisions: "
440 << heuristic_->number_of_rejects();
449 return heuristic_->number_of_decisions();
453 return heuristic_->number_of_rejects();
457 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
458 heuristic_->DebugString(),
")");
466 Solver* solver,
const std::vector<IntVar*>& vars,
467 const std::vector<IntVar*>& secondary_vars,
472 base_vars_size_(vars.size()),
473 delta_(solver->MakeAssignment()),
474 empty_(solver->MakeAssignment()),
475 filter_manager_(filter_manager),
476 objective_upper_bound_(
std::numeric_limits<int64_t>::max()),
477 number_of_decisions_(0),
478 number_of_rejects_(0) {
479 if (!secondary_vars.empty()) {
480 vars_.insert(vars_.end(), secondary_vars.begin(), secondary_vars.end());
482 assignment_->MutableIntVarContainer()->Resize(vars_.size());
483 is_in_delta_.resize(vars_.size(),
false);
484 delta_indices_.reserve(vars_.size());
488 number_of_decisions_ = 0;
489 number_of_rejects_ = 0;
492 delta_->MutableIntVarContainer()->Clear();
494 assignment_->MutableIntVarContainer()->Resize(vars_.size());
511 const std::function<int64_t(int64_t)>& next_accessor) {
522 for (
int v = 0; v < model_->vehicles(); v++) {
523 int64_t node = model_->Start(v);
524 while (!model_->IsEnd(node)) {
525 const int64_t next = next_accessor(node);
526 DCHECK_NE(next, node);
543 bool commit,
bool ignore_upper_bound,
bool update_upper_bound) {
544 ++number_of_decisions_;
545 const bool accept = FilterAccept(ignore_upper_bound);
546 int64_t objective_upper_bound = objective_upper_bound_;
548 if (filter_manager_ !=
nullptr) {
556 DCHECK(ignore_upper_bound ||
557 filter_manager_->GetAcceptedObjectiveValue() <=
558 objective_upper_bound_);
559 objective_upper_bound = filter_manager_->GetAcceptedObjectiveValue();
560 if (update_upper_bound) {
561 objective_upper_bound_ = objective_upper_bound;
566 delta_->IntVarContainer();
567 const int delta_size = delta_container.
Size();
570 for (
int i = 0; i < delta_size; ++i) {
573 DCHECK_EQ(var, vars_[delta_indices_[i]]);
580 ++number_of_rejects_;
583 for (
const int delta_index : delta_indices_) {
584 is_in_delta_[delta_index] =
false;
587 delta_indices_.clear();
588 return accept ? std::optional<int64_t>{objective_upper_bound} : std::nullopt;
592 if (filter_manager_) filter_manager_->Synchronize(
assignment_, delta_);
594 objective_upper_bound_ = std::numeric_limits<int64_t>::max();
597bool IntVarFilteredHeuristic::FilterAccept(
bool ignore_upper_bound) {
598 if (!filter_manager_)
return true;
600 return filter_manager_->
Accept(
601 monitor, delta_, empty_, std::numeric_limits<int64_t>::min(),
602 ignore_upper_bound ? std::numeric_limits<int64_t>::max()
603 : objective_upper_bound_);
612 model->CostsAreHomogeneousAcrossVehicles()
614 :
model->VehicleVars(),
617 stop_search_(
std::move(stop_search)) {}
624 start_chain_ends_.resize(
model()->vehicles());
625 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
627 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
628 const int64_t next =
Var(node)->
Min();
633 start_chain_ends_[vehicle] = node;
640 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
641 int64_t node = start_chain_ends_[vehicle];
642 if (!
model()->IsEnd(node)) {
643 int64_t next = end_chain_starts_[vehicle];
647 while (!
model()->IsEnd(node)) {
665 node, 1, [
this, node](
int alternate) {
666 if (node != alternate && !
Contains(alternate)) {
667 SetNext(alternate, alternate, -1);
675 absl::btree_set<std::pair<int64_t, int>> empty_vehicles;
676 for (
int vehicle = 0; vehicle < model_->vehicles(); ++vehicle) {
681 for (
int index = 0; index < model_->Size(); ++index) {
682 if (
StopSearch() || empty_vehicles.empty())
return;
685 for (
auto [cost, vehicle] : empty_vehicles) {
686 SetNext(model_->Start(vehicle), index, vehicle);
687 SetNext(index, model_->End(vehicle), vehicle);
689 empty_vehicles.erase({cost, vehicle});
699 for (
int index = 0; index < model_->Size(); ++index) {
709 const int num_nexts =
model()->
Nexts().size();
710 std::vector<bool> to_make_unperformed(num_nexts,
false);
711 for (
const auto& [pickups, deliveries] :
712 model()->GetPickupAndDeliveryPairs()) {
713 int64_t performed_pickup = -1;
714 for (int64_t pickup : pickups) {
716 performed_pickup = pickup;
720 int64_t performed_delivery = -1;
721 for (int64_t delivery : deliveries) {
723 performed_delivery = delivery;
727 if ((performed_pickup == -1) != (performed_delivery == -1)) {
728 if (performed_pickup != -1) {
729 to_make_unperformed[performed_pickup] =
true;
731 if (performed_delivery != -1) {
732 to_make_unperformed[performed_delivery] =
true;
736 for (
int index = 0; index < num_nexts; ++index) {
737 if (to_make_unperformed[index] || !
Contains(index))
continue;
740 int64_t next =
Value(index);
741 while (next < num_nexts && to_make_unperformed[next]) {
742 const int64_t next_of_next =
Value(next);
743 SetNext(index, next_of_next, vehicle);
754 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
755 std::function<int64_t(int64_t)> penalty_evaluator,
763void ProcessVehicleStartEndCosts(
765 const std::function<
void(int64_t,
int)>& process_cost,
767 bool ignore_end =
false) {
768 const auto start_end_cost = [&model, ignore_start, ignore_end](int64_t node,
770 const int64_t start_cost =
772 const int64_t end_cost =
774 return CapAdd(start_cost, end_cost);
780 const IntVar*
const vehicle_var = model.
VehicleVar(node);
781 if (vehicle_var->Size() < vehicle_set.
size()) {
782 std::unique_ptr<IntVarIterator> it(vehicle_var->MakeDomainIterator(
false));
783 for (
const int v : InitAndGetValues(it.get())) {
784 if (v < 0 || !vehicle_set[v]) {
787 process_cost(start_end_cost(node, v), v);
790 for (
const int v : vehicle_set) {
791 if (!vehicle_var->Contains(v))
continue;
792 process_cost(start_end_cost(node, v), v);
798std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
800 absl::Span<const int> vehicles) {
803 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
807 for (
int v : vehicles) vehicle_set.
Set(v);
809 for (
int node = 0; node <
model.Size(); node++) {
811 std::vector<StartEndValue>& start_end_distances =
812 start_end_distances_per_node[node];
813 start_end_distances.reserve(
814 std::min(
model.VehicleVar(node)->Size(),
815 static_cast<uint64_t
>(vehicles.size())));
817 ProcessVehicleStartEndCosts(
819 [&start_end_distances](int64_t dist,
int v) {
820 start_end_distances.push_back({dist, v});
826 absl::c_sort(start_end_distances,
828 return second < first;
831 return start_end_distances_per_node;
835 int node, std::vector<StartEndValue>* start_end_distances,
SeedQueue* sq) {
836 if (start_end_distances->empty()) {
841 StartEndValue& start_end_value = start_end_distances->back();
846 const int64_t neg_penalty =
CapOpp(
model()->UnperformedPenalty(node));
847 sq->
priority_queue.push({.properties = {num_allowed_vehicles, neg_penalty,
849 .vehicle = start_end_value.
vehicle,
850 .is_node_index =
true,
852 start_end_distances->pop_back();
856 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
859 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
861 for (
int node = 0; node < num_nodes; node++) {
883 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
887 if (cache_entry.
node != insert_before || cache_entry.
vehicle != vehicle) {
888 cache_entry = {.value =
evaluator_(insert_after, insert_before, vehicle),
889 .node = insert_before,
893 evaluator_(node_to_insert, insert_before, vehicle)),
897std::optional<int64_t>
899 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
900 int vehicle,
int hint_weight) {
903 node_to_insert, insert_after, insert_before, vehicle);
905 InsertBetween(node_to_insert, insert_after, insert_before, vehicle);
906 return Evaluate(
false, hint_weight > 0,
910std::optional<int64_t>
912 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
913 int64_t delivery_insert_after,
int vehicle,
int hint_weight) {
914 DCHECK_GE(pickup_insert_after, 0);
915 const int64_t pickup_insert_before =
Value(pickup_insert_after);
916 DCHECK_GE(delivery_insert_after, 0);
917 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
918 ? pickup_insert_before
919 :
Value(delivery_insert_after);
922 pickup, pickup_insert_after, pickup_insert_before, vehicle);
924 delivery, delivery_insert_after, delivery_insert_before, vehicle);
925 return CapAdd(pickup_cost, delivery_cost);
928 InsertBetween(pickup, pickup_insert_after, pickup_insert_before, vehicle);
929 InsertBetween(delivery, delivery_insert_after, delivery_insert_before,
937 int64_t node_to_insert)
const {
941 return std::numeric_limits<int64_t>::max();
949 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
950 std::function<int64_t(int64_t)> penalty_evaluator,
954 model,
std::move(stop_search),
std::move(evaluator),
955 std::move(penalty_evaluator), filter_manager),
956 gci_params_(
std::move(parameters)),
957 is_sequential_(is_sequential),
958 node_index_to_vehicle_(
model->Size(), -1),
959 node_index_to_neighbors_by_cost_class_(nullptr),
960 empty_vehicle_type_curator_(nullptr),
961 temp_inserted_nodes_(
model->Size()) {
962 CHECK_GT(gci_params_.neighbors_ratio(), 0);
963 CHECK_LE(gci_params_.neighbors_ratio(), 1);
964 CHECK_GE(gci_params_.min_neighbors(), 1);
967bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
968 std::vector<bool> node_is_visited(
model()->Size(),
false);
971 node =
Value(node)) {
972 if (node_index_to_vehicle_[node] != v) {
975 node_is_visited[node] =
true;
979 for (
int node = 0; node <
model()->
Size(); node++) {
980 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
990 double neighbors_ratio_used = 1;
991 node_index_to_neighbors_by_cost_class_ =
993 gci_params_.neighbors_ratio(), gci_params_.min_neighbors(),
994 neighbors_ratio_used);
995 if (neighbors_ratio_used == 1) {
996 gci_params_.set_use_neighbors_ratio_for_initialization(
false);
999 if (empty_vehicle_type_curator_ ==
nullptr) {
1000 empty_vehicle_type_curator_ = std::make_unique<VehicleTypeCurator>(
1001 model()->GetVehicleTypeContainer());
1004 empty_vehicle_type_curator_->Reset(
1007 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1009 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
1010 absl::flat_hash_map<int, std::map<int64_t, std::vector<int>>>
1011 vehicle_to_pair_nodes;
1012 for (
int index = 0; index < pickup_delivery_pairs.size(); index++) {
1014 pickup_delivery_pairs[index];
1015 const auto& [pickups, deliveries] = pickup_delivery_pair;
1016 int pickup_vehicle = -1;
1017 for (int64_t pickup : pickups) {
1019 pickup_vehicle = node_index_to_vehicle_[pickup];
1023 int delivery_vehicle = -1;
1024 for (int64_t delivery : deliveries) {
1026 delivery_vehicle = node_index_to_vehicle_[delivery];
1030 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
1031 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pair)]
1034 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
1035 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle][1];
1036 for (int64_t delivery : deliveries) {
1037 pair_nodes.push_back(delivery);
1040 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
1041 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle][1];
1042 for (int64_t pickup : pickups) {
1043 pair_nodes.push_back(pickup);
1048 const auto unperform_unassigned_and_check = [
this]() {
1052 for (
const auto& [vehicle, nodes] : vehicle_to_pair_nodes) {
1053 if (!InsertNodesOnRoutes(nodes, {vehicle})) {
1054 return unperform_unassigned_and_check();
1058 if (!InsertPairsAndNodesByRequirementTopologicalOrder()) {
1059 return unperform_unassigned_and_check();
1061 if (!InsertPairsAndNodesByPrecedenceTopologicalOrder()) {
1062 return unperform_unassigned_and_check();
1067 if (!InsertPairs(pairs_to_insert_by_bucket)) {
1068 return unperform_unassigned_and_check();
1070 std::map<int64_t, std::vector<int>> nodes_by_bucket;
1071 for (
int node = 0; node <
model()->
Size(); ++node) {
1073 !
model()->IsDelivery(node)) {
1074 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
1077 InsertFarthestNodesAsSeeds();
1078 if (is_sequential_) {
1079 if (!SequentialInsertNodes(nodes_by_bucket)) {
1080 return unperform_unassigned_and_check();
1082 }
else if (!InsertNodesOnRoutes(nodes_by_bucket, {})) {
1083 return unperform_unassigned_and_check();
1085 DCHECK(CheckVehicleIndices());
1086 return unperform_unassigned_and_check();
1089bool GlobalCheapestInsertionFilteredHeuristic::
1090 InsertPairsAndNodesByRequirementTopologicalOrder() {
1091 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1093 for (
const std::vector<int>& types :
1094 model()->GetTopologicallySortedVisitTypes()) {
1095 for (
int type : types) {
1096 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
1097 for (
int index :
model()->GetPairIndicesOfType(type)) {
1098 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pairs[index])]
1101 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
1102 std::map<int64_t, std::vector<int>> nodes_by_bucket;
1103 for (
int node :
model()->GetSingleNodesOfType(type)) {
1104 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
1106 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
1112bool GlobalCheapestInsertionFilteredHeuristic::
1113 InsertPairsAndNodesByPrecedenceTopologicalOrder() {
1114 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1116 for (
const std::vector<std::vector<int>>& ordered_nodes :
1117 model()->GetTopologicallySortedNodePrecedences()) {
1118 for (
const std::vector<int>& nodes : ordered_nodes) {
1119 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
1120 for (
int node : nodes) {
1122 if (!
model()->IsPickup(node) && !
model()->IsDelivery(node))
continue;
1123 const std::optional<RoutingModel::PickupDeliveryPosition>
1125 if (pickup_position.has_value()) {
1126 const int index = pickup_position->pd_pair_index;
1127 pairs_to_insert_by_bucket[GetBucketOfPair(
1128 pickup_delivery_pairs[index])]
1131 const std::optional<RoutingModel::PickupDeliveryPosition>
1133 if (delivery_position.has_value()) {
1134 const int index = delivery_position->pd_pair_index;
1135 pairs_to_insert_by_bucket[GetBucketOfPair(
1136 pickup_delivery_pairs[index])]
1140 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
1141 std::map<int64_t, std::vector<int>> nodes_by_bucket;
1142 for (
int node : nodes) {
1144 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
1146 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
1152bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
1153 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket) {
1154 AdjustablePriorityQueue<PairEntry> priority_queue;
1155 std::vector<PairEntries> pickup_to_entries;
1156 std::vector<PairEntries> delivery_to_entries;
1157 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1159 auto pair_is_performed = [
this, &pickup_delivery_pairs](
int pair_index) {
1160 const auto& [pickups, deliveries] = pickup_delivery_pairs[pair_index];
1161 for (int64_t pickup : pickups) {
1164 for (int64_t delivery : deliveries) {
1165 if (
Contains(delivery))
return true;
1169 absl::flat_hash_set<int> pair_indices_to_insert;
1170 for (
const auto& [bucket, pair_indices] : pair_indices_by_bucket) {
1171 for (
const int pair_index : pair_indices) {
1172 if (!pair_is_performed(pair_index)) {
1173 pair_indices_to_insert.insert(pair_index);
1176 if (!InitializePairPositions(pair_indices_to_insert, &priority_queue,
1177 &pickup_to_entries, &delivery_to_entries)) {
1180 while (!priority_queue.
IsEmpty()) {
1181 if (StopSearchAndCleanup(&priority_queue)) {
1184 PairEntry*
const entry = priority_queue.
Top();
1185 const int64_t pickup = entry->pickup_to_insert();
1186 const int64_t delivery = entry->delivery_to_insert();
1188 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1189 &delivery_to_entries);
1193 const int entry_vehicle = entry->vehicle();
1194 if (entry_vehicle == -1) {
1197 SetNext(delivery, delivery, -1);
1199 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1200 &delivery_to_entries);
1206 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle)) {
1207 if (!InsertPairEntryUsingEmptyVehicleTypeCurator(
1208 pair_indices_to_insert, entry, &priority_queue,
1209 &pickup_to_entries, &delivery_to_entries)) {
1217 const int64_t pickup_insert_after = entry->pickup_insert_after();
1218 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1219 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
1221 const int64_t delivery_insert_after = entry->delivery_insert_after();
1222 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1223 ? pickup_insert_before
1224 :
Value(delivery_insert_after);
1225 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
1227 if (!UpdateAfterPairInsertion(
1228 pair_indices_to_insert, entry_vehicle, pickup,
1229 pickup_insert_after, delivery, delivery_insert_after,
1230 &priority_queue, &pickup_to_entries, &delivery_to_entries)) {
1234 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1235 &delivery_to_entries);
1240 for (
auto it = pair_indices_to_insert.begin(),
1241 last = pair_indices_to_insert.end();
1243 if (pair_is_performed(*it)) {
1244 pair_indices_to_insert.erase(it++);
1253bool GlobalCheapestInsertionFilteredHeuristic::
1254 InsertPairEntryUsingEmptyVehicleTypeCurator(
1255 const absl::flat_hash_set<int>& pair_indices,
1256 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1257 AdjustablePriorityQueue<
1258 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1260 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1262 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1263 delivery_to_entries) {
1264 const int entry_vehicle = pair_entry->vehicle();
1265 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle));
1271 const int64_t pickup = pair_entry->pickup_to_insert();
1272 const int64_t delivery = pair_entry->delivery_to_insert();
1273 const int64_t entry_fixed_cost =
1275 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
1276 delivery](
int vehicle) {
1277 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1291 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1294 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1295 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1296 empty_vehicle_type_curator_->Type(entry_vehicle),
1297 vehicle_is_compatible, stop_and_return_vehicle);
1298 if (compatible_vehicle >= 0) {
1300 const int64_t vehicle_start =
model()->
Start(compatible_vehicle);
1301 const int num_previous_vehicle_entries =
1302 pickup_to_entries->at(vehicle_start).size() +
1303 delivery_to_entries->at(vehicle_start).size();
1304 if (!UpdateAfterPairInsertion(
1305 pair_indices, compatible_vehicle, pickup, vehicle_start, delivery,
1306 pickup, priority_queue, pickup_to_entries, delivery_to_entries)) {
1309 if (compatible_vehicle != entry_vehicle) {
1316 num_previous_vehicle_entries == 0 ||
1317 model()->GetVehicleClassIndexOfVehicle(compatible_vehicle).value() !=
1318 model()->GetVehicleClassIndexOfVehicle(entry_vehicle).value());
1324 const int new_empty_vehicle =
1325 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1326 empty_vehicle_type_curator_->Type(compatible_vehicle));
1328 if (new_empty_vehicle >= 0) {
1335 const int64_t new_empty_vehicle_start =
model()->
Start(new_empty_vehicle);
1336 const std::vector<PairEntry*> to_remove(
1337 pickup_to_entries->at(new_empty_vehicle_start).begin(),
1338 pickup_to_entries->at(new_empty_vehicle_start).end());
1339 for (PairEntry* entry : to_remove) {
1340 DeletePairEntry(entry, priority_queue, pickup_to_entries,
1341 delivery_to_entries);
1343 if (!AddPairEntriesWithPickupAfter(
1344 pair_indices, new_empty_vehicle, new_empty_vehicle_start,
1346 pickup_to_entries, delivery_to_entries)) {
1350 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1357 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1358 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1359 pair_entry->set_pickup_insert_after(
1360 model()->Start(next_fixed_cost_empty_vehicle));
1361 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1362 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1363 if (!UpdatePairEntry(pair_entry, priority_queue)) {
1364 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1365 delivery_to_entries);
1368 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1369 delivery_to_entries);
1399 : entries_(num_nodes), touched_entries_(num_nodes) {}
1401 priority_queue_.Clear();
1402 for (Entries& entries : entries_) entries.Clear();
1403 touched_entries_.ResetAllToFalse();
1406 return priority_queue_.IsEmpty() &&
1407 touched_entries_.NumberOfSetCallsWithDifferentArguments() == 0;
1410 return insert_after >= entries_.size() ||
1411 entries_[insert_after].entries.empty();
1415 for (
int touched : touched_entries_.PositionsSetAtLeastOnce()) {
1416 SortInsertions(&entries_[touched]);
1418 touched_entries_.ResetAllToFalse();
1419 DCHECK(!priority_queue_.IsEmpty());
1420 Entries* entries = priority_queue_.Top();
1421 DCHECK(!entries->entries.empty());
1422 return entries->Top();
1426 CHECK_EQ(touched_entries_.NumberOfSetCallsWithDifferentArguments(), 0);
1427 Entries* top = priority_queue_.Top();
1428 if (top->IncrementTop()) {
1429 priority_queue_.NoteChangedPriority(top);
1431 priority_queue_.Remove(top);
1436 if (
IsEmpty(insert_after))
return;
1437 Entries& entries = entries_[insert_after];
1438 if (priority_queue_.Contains(&entries)) {
1439 priority_queue_.Remove(&entries);
1444 int bucket, int64_t value) {
1445 entries_[insert_after].entries.push_back(
1446 {value, node, insert_after, vehicle, bucket});
1447 touched_entries_.Set(insert_after);
1452 bool operator<(
const Entries& other)
const {
1453 DCHECK(!entries.empty());
1454 DCHECK(!other.entries.empty());
1455 return other.entries[other.top] < entries[top];
1462 void SetHeapIndex(
int index) { heap_index = index; }
1463 int GetHeapIndex()
const {
return heap_index; }
1464 bool IncrementTop() {
1466 return top < entries.size();
1468 Entry*
Top() {
return &entries[top]; }
1470 std::vector<Entry> entries;
1472 int heap_index = -1;
1475 void SortInsertions(Entries* entries) {
1477 if (entries->entries.empty())
return;
1478 absl::c_sort(entries->entries);
1479 if (!priority_queue_.Contains(entries)) {
1480 priority_queue_.Add(entries);
1482 priority_queue_.NoteChangedPriority(entries);
1486 AdjustablePriorityQueue<Entries> priority_queue_;
1487 std::vector<Entries> entries_;
1488 SparseBitset<int> touched_entries_;
1491bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1492 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
1493 const absl::flat_hash_set<int>& vehicles) {
1495 SparseBitset<int> nodes_to_insert(
model()->Size());
1496 for (
const auto& [bucket, nodes] : nodes_by_bucket) {
1497 for (
int node : nodes) {
1498 nodes_to_insert.Set(node);
1500 if (!InitializePositions(nodes_to_insert, vehicles, &queue)) {
1509 const bool all_vehicles =
1512 while (!queue.IsEmpty()) {
1521 const int entry_vehicle = node_entry->vehicle;
1522 if (entry_vehicle == -1) {
1523 DCHECK(all_vehicles);
1525 SetNext(node_to_insert, node_to_insert, -1);
1533 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles)) {
1534 DCHECK(all_vehicles);
1535 if (!InsertNodeEntryUsingEmptyVehicleTypeCurator(
1536 nodes_to_insert, all_vehicles, &queue)) {
1542 const int64_t insert_after = node_entry->insert_after;
1545 if (!UpdateAfterNodeInsertion(nodes_to_insert, entry_vehicle,
1546 node_to_insert, insert_after,
1547 all_vehicles, &queue)) {
1556 std::vector<int> non_inserted_nodes;
1557 non_inserted_nodes.reserve(
1558 nodes_to_insert.NumberOfSetCallsWithDifferentArguments());
1559 for (
int node : nodes_to_insert.PositionsSetAtLeastOnce()) {
1560 if (!
Contains(node)) non_inserted_nodes.push_back(node);
1562 nodes_to_insert.ResetAllToFalse();
1563 for (
int node : non_inserted_nodes) {
1564 nodes_to_insert.Set(node);
1570bool GlobalCheapestInsertionFilteredHeuristic::
1573 NodeEntryQueue* queue) {
1575 const int entry_vehicle = node_entry->
vehicle;
1576 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles));
1584 const int64_t node_to_insert = node_entry->node_to_insert;
1585 const int bucket = node_entry->bucket;
1586 const int64_t entry_fixed_cost =
1588 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1589 node_to_insert](
int vehicle) {
1590 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1596 model()->End(vehicle), vehicle);
1603 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1606 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1607 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1608 empty_vehicle_type_curator_->Type(entry_vehicle),
1609 vehicle_is_compatible, stop_and_return_vehicle);
1610 if (compatible_vehicle >= 0) {
1612 const int64_t compatible_start =
model()->
Start(compatible_vehicle);
1613 const bool no_prior_entries_for_this_vehicle =
1614 queue->IsEmpty(compatible_start);
1615 if (!UpdateAfterNodeInsertion(nodes, compatible_vehicle, node_to_insert,
1616 compatible_start, all_vehicles, queue)) {
1619 if (compatible_vehicle != entry_vehicle) {
1626 no_prior_entries_for_this_vehicle ||
1627 model()->GetVehicleClassIndexOfVehicle(compatible_vehicle).value() !=
1628 model()->GetVehicleClassIndexOfVehicle(entry_vehicle).value());
1634 const int new_empty_vehicle =
1635 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1636 empty_vehicle_type_curator_->Type(compatible_vehicle));
1638 if (new_empty_vehicle >= 0) {
1645 const int64_t new_empty_vehicle_start =
model()->
Start(new_empty_vehicle);
1646 queue->ClearInsertions(new_empty_vehicle_start);
1647 if (!AddNodeEntriesAfter(nodes, new_empty_vehicle,
1648 new_empty_vehicle_start, all_vehicles, queue)) {
1652 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1660 const int64_t insert_after =
model()->
Start(next_fixed_cost_empty_vehicle);
1662 node_to_insert, insert_after,
Value(insert_after),
1663 next_fixed_cost_empty_vehicle);
1664 const int64_t penalty_shift =
1665 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1668 queue->PushInsertion(node_to_insert, insert_after,
1669 next_fixed_cost_empty_vehicle, bucket,
1670 CapSub(insertion_cost, penalty_shift));
1678bool GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1679 const std::map<int64_t, std::vector<int>>& nodes_by_bucket) {
1680 std::vector<bool> is_vehicle_used;
1681 absl::flat_hash_set<int> used_vehicles;
1682 std::vector<int> unused_vehicles;
1684 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1685 if (!used_vehicles.empty() &&
1686 !InsertNodesOnRoutes(nodes_by_bucket, used_vehicles)) {
1690 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1695 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1698 while (vehicle >= 0) {
1699 if (!InsertNodesOnRoutes(nodes_by_bucket, {vehicle})) {
1702 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1708void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1709 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1710 absl::flat_hash_set<int>* used_vehicles) {
1711 is_vehicle_used->clear();
1712 is_vehicle_used->resize(
model()->vehicles());
1714 used_vehicles->clear();
1715 used_vehicles->reserve(
model()->vehicles());
1717 unused_vehicles->clear();
1718 unused_vehicles->reserve(
model()->vehicles());
1720 for (
int vehicle = 0; vehicle <
model()->
vehicles(); vehicle++) {
1722 (*is_vehicle_used)[vehicle] =
true;
1723 used_vehicles->insert(vehicle);
1725 (*is_vehicle_used)[vehicle] =
false;
1726 unused_vehicles->push_back(vehicle);
1731bool GlobalCheapestInsertionFilteredHeuristic::IsCheapestClassRepresentative(
1732 int vehicle)
const {
1742 const int curator_vehicle =
1743 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1744 empty_vehicle_type_curator_->Type(vehicle));
1745 return curator_vehicle == vehicle ||
1750void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1752 if (gci_params_.farthest_seeds_ratio() <= 0)
return;
1754 const int num_seeds =
static_cast<int>(
1755 std::ceil(gci_params_.farthest_seeds_ratio() *
model()->vehicles()));
1757 std::vector<bool> is_vehicle_used;
1758 absl::flat_hash_set<int> used_vehicles;
1759 std::vector<int> unused_vehicles;
1760 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1761 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1769 int inserted_seeds = 0;
1770 while (inserted_seeds++ < num_seeds) {
1771 if (InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1772 &is_vehicle_used) < 0) {
1781 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1782 empty_vehicle_type_curator_->Update(
1786int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1787 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1788 SeedQueue* sq, std::vector<bool>* is_vehicle_used) {
1789 auto& priority_queue = sq->priority_queue;
1790 while (!priority_queue.empty()) {
1792 const Seed& seed = priority_queue.top();
1793 const int seed_node = seed.index;
1794 DCHECK(seed.is_node_index);
1795 const int seed_vehicle = seed.vehicle;
1796 priority_queue.pop();
1798 std::vector<StartEndValue>& other_start_end_values =
1799 (*start_end_distances_per_node)[seed_node];
1804 other_start_end_values.clear();
1807 if (!(*is_vehicle_used)[seed_vehicle]) {
1809 const int64_t start =
model()->
Start(seed_vehicle);
1814 (*is_vehicle_used)[seed_vehicle] =
true;
1815 other_start_end_values.clear();
1816 SetVehicleIndex(seed_node, seed_vehicle);
1817 return seed_vehicle;
1829bool GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1830 const absl::flat_hash_set<int>& pair_indices,
1831 AdjustablePriorityQueue<
1832 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1833 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1835 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1836 delivery_to_entries) {
1837 priority_queue->
Clear();
1838 pickup_to_entries->clear();
1839 pickup_to_entries->resize(
model()->Size());
1840 delivery_to_entries->clear();
1841 delivery_to_entries->resize(
model()->Size());
1842 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1844 for (
int index : pair_indices) {
1845 const auto& [pickups, deliveries] = pickup_delivery_pairs[index];
1846 for (int64_t pickup : pickups) {
1848 for (int64_t delivery : deliveries) {
1850 if (StopSearchAndCleanup(priority_queue))
return false;
1855 if (gci_params_.add_unperformed_entries() && pickups.size() == 1 &&
1856 deliveries.size() == 1 &&
1858 std::numeric_limits<int64_t>::max() &&
1860 std::numeric_limits<int64_t>::max()) {
1861 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1865 InitializeInsertionEntriesPerformingPair(
1866 pickup, delivery, priority_queue, pickup_to_entries,
1867 delivery_to_entries);
1874void GlobalCheapestInsertionFilteredHeuristic::
1875 InitializeInsertionEntriesPerformingPair(
1876 int64_t pickup, int64_t delivery,
1877 AdjustablePriorityQueue<
1878 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1880 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1882 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1883 delivery_to_entries) {
1884 if (!gci_params_.use_neighbors_ratio_for_initialization()) {
1885 std::unique_ptr<IntVarIterator> vehicle_it(
1886 model()->VehicleVar(pickup)->MakeDomainIterator(
false));
1887 for (
const int vehicle : InitAndGetValues(vehicle_it.get())) {
1888 if (vehicle < 0 || !
model()->VehicleVar(delivery)->
Contains(vehicle)) {
1891 if (!IsCheapestClassRepresentative(vehicle))
continue;
1893 int64_t pickup_insert_after =
model()->
Start(vehicle);
1894 while (!
model()->IsEnd(pickup_insert_after)) {
1895 int64_t delivery_insert_after = pickup;
1896 while (!
model()->IsEnd(delivery_insert_after)) {
1897 AddPairEntry(pickup, pickup_insert_after, delivery,
1898 delivery_insert_after, vehicle, priority_queue,
1899 pickup_to_entries, delivery_to_entries);
1900 delivery_insert_after = (delivery_insert_after == pickup)
1901 ?
Value(pickup_insert_after)
1902 :
Value(delivery_insert_after);
1904 pickup_insert_after =
Value(pickup_insert_after);
1914 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1915 existing_insertion_positions;
1917 for (
const int64_t pickup_insert_after :
1918 node_index_to_neighbors_by_cost_class_
1919 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, pickup)) {
1920 if (!
Contains(pickup_insert_after)) {
1923 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1925 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1929 if (!IsCheapestClassRepresentative(vehicle))
continue;
1935 int64_t delivery_insert_after = pickup;
1936 while (!
model()->IsEnd(delivery_insert_after)) {
1937 const std::pair<int64_t, int64_t> insertion_position = {
1938 pickup_insert_after, delivery_insert_after};
1939 DCHECK(!existing_insertion_positions.contains(insertion_position));
1940 existing_insertion_positions.insert(insertion_position);
1942 AddPairEntry(pickup, pickup_insert_after, delivery,
1943 delivery_insert_after, vehicle, priority_queue,
1944 pickup_to_entries, delivery_to_entries);
1945 delivery_insert_after = (delivery_insert_after == pickup)
1946 ?
Value(pickup_insert_after)
1947 :
Value(delivery_insert_after);
1952 for (
const int64_t delivery_insert_after :
1953 node_index_to_neighbors_by_cost_class_
1954 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, delivery)) {
1955 if (!
Contains(delivery_insert_after)) {
1958 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1960 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1969 delivery_insert_after ==
model()->Start(vehicle));
1971 int64_t pickup_insert_after =
model()->
Start(vehicle);
1972 while (pickup_insert_after != delivery_insert_after) {
1973 if (!existing_insertion_positions.contains(
1974 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1975 AddPairEntry(pickup, pickup_insert_after, delivery,
1976 delivery_insert_after, vehicle, priority_queue,
1977 pickup_to_entries, delivery_to_entries);
1979 pickup_insert_after =
Value(pickup_insert_after);
1985bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1986 const absl::flat_hash_set<int>& pair_indices,
int vehicle, int64_t pickup,
1987 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1988 AdjustablePriorityQueue<PairEntry>* priority_queue,
1989 std::vector<PairEntries>* pickup_to_entries,
1990 std::vector<PairEntries>* delivery_to_entries) {
1993 const std::vector<PairEntry*> to_remove(
1994 delivery_to_entries->at(pickup).begin(),
1995 delivery_to_entries->at(pickup).end());
1996 for (PairEntry* pair_entry : to_remove) {
1997 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1998 delivery_to_entries);
2000 DCHECK(pickup_to_entries->at(pickup).empty());
2001 DCHECK(pickup_to_entries->at(delivery).empty());
2002 DCHECK(delivery_to_entries->at(pickup).empty());
2003 DCHECK(delivery_to_entries->at(delivery).empty());
2006 if (!UpdateExistingPairEntriesAfter(pickup_position, priority_queue,
2007 pickup_to_entries, delivery_to_entries) ||
2008 !UpdateExistingPairEntriesAfter(delivery_position, priority_queue,
2009 pickup_to_entries, delivery_to_entries)) {
2015 if (!AddPairEntriesAfter(pair_indices, vehicle, pickup,
2017 priority_queue, pickup_to_entries,
2018 delivery_to_entries) ||
2019 !AddPairEntriesAfter(pair_indices, vehicle, delivery,
2021 priority_queue, pickup_to_entries,
2022 delivery_to_entries)) {
2025 SetVehicleIndex(pickup, vehicle);
2026 SetVehicleIndex(delivery, vehicle);
2030bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingPairEntriesAfter(
2031 int64_t insert_after,
2032 AdjustablePriorityQueue<
2033 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2034 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2036 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2037 delivery_to_entries) {
2038 DCHECK(!
model()->IsEnd(insert_after));
2041 std::vector<PairEntry*> to_remove;
2042 for (
const PairEntries* pair_entries :
2043 {&pickup_to_entries->at(insert_after),
2044 &delivery_to_entries->at(insert_after)}) {
2045 if (StopSearchAndCleanup(priority_queue))
return false;
2046 for (PairEntry*
const pair_entry : *pair_entries) {
2047 DCHECK(priority_queue->
Contains(pair_entry));
2048 if (
Contains(pair_entry->pickup_to_insert()) ||
2049 Contains(pair_entry->delivery_to_insert())) {
2050 to_remove.push_back(pair_entry);
2052 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
2053 .contains(pair_entry));
2054 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
2055 .contains(pair_entry));
2056 if (!UpdatePairEntry(pair_entry, priority_queue)) {
2057 to_remove.push_back(pair_entry);
2062 for (PairEntry*
const pair_entry : to_remove) {
2063 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
2064 delivery_to_entries);
2069bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithPickupAfter(
2070 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
2071 int64_t insert_after, int64_t skip_entries_inserting_delivery_after,
2072 AdjustablePriorityQueue<
2073 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2074 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2076 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2077 delivery_to_entries) {
2079 const int64_t pickup_insert_before =
Value(insert_after);
2080 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
2082 DCHECK(pickup_to_entries->at(insert_after).empty());
2083 for (
const int64_t pickup :
2084 node_index_to_neighbors_by_cost_class_
2085 ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after)) {
2086 if (StopSearchAndCleanup(priority_queue))
return false;
2090 if (
const std::optional<RoutingModel::PickupDeliveryPosition> pickup_pos =
2091 model()->GetPickupPosition(pickup);
2092 pickup_pos.has_value()) {
2093 const int pair_index = pickup_pos->pd_pair_index;
2094 if (!pair_indices.contains(pair_index))
continue;
2095 for (
const int64_t delivery :
2096 pickup_delivery_pairs[pair_index].delivery_alternatives) {
2101 int64_t delivery_insert_after = pickup;
2102 while (!
model()->IsEnd(delivery_insert_after)) {
2103 if (delivery_insert_after != skip_entries_inserting_delivery_after) {
2104 AddPairEntry(pickup, insert_after, delivery, delivery_insert_after,
2105 vehicle, priority_queue, pickup_to_entries,
2106 delivery_to_entries);
2108 if (delivery_insert_after == pickup) {
2109 delivery_insert_after = pickup_insert_before;
2111 delivery_insert_after =
Value(delivery_insert_after);
2120bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithDeliveryAfter(
2121 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
2122 int64_t insert_after,
2123 AdjustablePriorityQueue<
2124 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2125 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2127 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2128 delivery_to_entries) {
2130 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
2132 for (
const int64_t delivery :
2133 node_index_to_neighbors_by_cost_class_
2134 ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after)) {
2135 if (StopSearchAndCleanup(priority_queue))
return false;
2140 if (
const std::optional<RoutingModel::PickupDeliveryPosition> delivery_pos =
2141 model()->GetDeliveryPosition(delivery);
2142 delivery_pos.has_value()) {
2143 const int pair_index = delivery_pos->pd_pair_index;
2144 if (!pair_indices.contains(pair_index))
continue;
2145 for (
const int64_t pickup :
2146 pickup_delivery_pairs[pair_index].pickup_alternatives) {
2151 int64_t pickup_insert_after =
model()->
Start(vehicle);
2152 while (pickup_insert_after != insert_after) {
2153 AddPairEntry(pickup, pickup_insert_after, delivery, insert_after,
2154 vehicle, priority_queue, pickup_to_entries,
2155 delivery_to_entries);
2156 pickup_insert_after =
Value(pickup_insert_after);
2164void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
2165 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
2166 AdjustablePriorityQueue<
2167 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2168 std::vector<PairEntries>* pickup_to_entries,
2169 std::vector<PairEntries>* delivery_to_entries) {
2170 priority_queue->
Remove(entry);
2171 if (entry->pickup_insert_after() != -1) {
2172 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
2174 if (entry->delivery_insert_after() != -1) {
2175 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
2177 pair_entry_allocator_.FreeEntry(entry);
2180void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
2181 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
2182 int64_t delivery_insert_after,
int vehicle,
2183 AdjustablePriorityQueue<
2184 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2185 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2187 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2191 if (!pickup_vehicle_var->Contains(vehicle) ||
2192 !delivery_vehicle_var->Contains(vehicle)) {
2196 const auto vehicle_is_compatible = [pickup_vehicle_var,
2197 delivery_vehicle_var](
int vehicle) {
2198 return pickup_vehicle_var->Contains(vehicle) &&
2199 delivery_vehicle_var->Contains(vehicle);
2201 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2202 empty_vehicle_type_curator_->Type(vehicle),
2203 vehicle_is_compatible)) {
2207 const int num_allowed_vehicles =
2208 std::min(pickup_vehicle_var->Size(), delivery_vehicle_var->Size());
2209 const int64_t pair_penalty =
2211 const int64_t penalty_shift =
2212 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2215 if (pickup_insert_after == -1) {
2216 DCHECK_EQ(delivery_insert_after, -1);
2217 DCHECK_EQ(vehicle, -1);
2218 PairEntry* pair_entry = pair_entry_allocator_.NewEntry(
2219 pickup, -1, delivery, -1, -1, num_allowed_vehicles);
2220 pair_entry->set_value(
CapSub(pair_penalty, penalty_shift));
2221 priority_queue->
Add(pair_entry);
2224 const std::optional<int64_t> insertion_cost =
2226 delivery_insert_after, vehicle);
2228 if (!insertion_cost.has_value())
return;
2230 PairEntry*
const pair_entry = pair_entry_allocator_.NewEntry(
2231 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle,
2232 num_allowed_vehicles);
2233 pair_entry->set_value(
CapSub(*insertion_cost, penalty_shift));
2236 DCHECK(!priority_queue->
Contains(pair_entry));
2237 pickup_entries->at(pickup_insert_after).insert(pair_entry);
2238 delivery_entries->at(delivery_insert_after).insert(pair_entry);
2239 priority_queue->
Add(pair_entry);
2242bool GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
2243 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
2244 AdjustablePriorityQueue<
2245 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue) {
2246 const int64_t pickup = pair_entry->pickup_to_insert();
2247 const int64_t delivery = pair_entry->delivery_to_insert();
2248 const std::optional<int64_t> insertion_cost =
2250 pickup, pair_entry->pickup_insert_after(), delivery,
2251 pair_entry->delivery_insert_after(), pair_entry->vehicle());
2252 const int64_t penalty_shift =
2253 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty) &&
2254 insertion_cost.has_value()
2258 if (!insertion_cost.has_value()) {
2262 pair_entry->set_value(
CapSub(*insertion_cost, penalty_shift));
2265 DCHECK(priority_queue->
Contains(pair_entry));
2270bool GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
2272 NodeEntryQueue* queue) {
2275 const int num_vehicles =
2277 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
2279 for (
int node : nodes.PositionsSetAtLeastOnce()) {
2284 if (gci_params_.add_unperformed_entries() &&
2286 AddNodeEntry(node, node, -1, all_vehicles, queue);
2289 InitializeInsertionEntriesPerformingNode(node, vehicles, queue);
2294void GlobalCheapestInsertionFilteredHeuristic::
2295 InitializeInsertionEntriesPerformingNode(
2296 int64_t node,
const absl::flat_hash_set<int>& vehicles,
2297 NodeEntryQueue* queue) {
2298 const int num_vehicles =
2300 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
2302 if (!gci_params_.use_neighbors_ratio_for_initialization()) {
2303 auto vehicles_it = vehicles.begin();
2309 for (
int v = 0; v < num_vehicles; v++) {
2310 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
2311 if (!
model()->VehicleVar(node)->
Contains(vehicle))
continue;
2312 if (all_vehicles && !IsCheapestClassRepresentative(vehicle))
continue;
2314 int64_t insert_after =
model()->
Start(vehicle);
2315 while (insert_after !=
model()->End(vehicle)) {
2316 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2317 insert_after =
Value(insert_after);
2327 for (
const int64_t insert_after :
2328 node_index_to_neighbors_by_cost_class_
2329 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, node)) {
2333 const int vehicle = node_index_to_vehicle_[insert_after];
2334 if (vehicle == -1 ||
2335 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
2339 if (!IsCheapestClassRepresentative(vehicle))
continue;
2340 }
else if (!vehicles.contains(vehicle)) {
2343 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2348bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterNodeInsertion(
2350 int64_t insert_after,
bool all_vehicles, NodeEntryQueue* queue) {
2353 if (!AddNodeEntriesAfter(nodes, vehicle, insert_after, all_vehicles, queue)) {
2361 if (!AddNodeEntriesAfter(nodes, vehicle, node, all_vehicles, queue)) {
2364 SetVehicleIndex(node, vehicle);
2368bool GlobalCheapestInsertionFilteredHeuristic::AddNodeEntriesAfter(
2370 bool all_vehicles, NodeEntryQueue* queue) {
2371 temp_inserted_nodes_.ResetAllToFalse();
2375 queue->ClearInsertions(insert_after);
2377 const auto add_node_entries_for_neighbors =
2378 [
this, &nodes, &queue, insert_after, vehicle, all_vehicles](
2379 absl::Span<const int> neighbors,
2380 const std::function<bool(int64_t)>& is_neighbor) {
2381 if (neighbors.size() < nodes.NumberOfSetCallsWithDifferentArguments()) {
2383 for (
int node : neighbors) {
2385 if (!
Contains(node) && nodes[node] && !temp_inserted_nodes_[node]) {
2386 temp_inserted_nodes_.Set(node);
2387 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2392 for (
int node : nodes.PositionsSetAtLeastOnce()) {
2394 if (!
Contains(node) && is_neighbor(node) &&
2395 !temp_inserted_nodes_[node]) {
2396 temp_inserted_nodes_.Set(node);
2397 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2404 if (!add_node_entries_for_neighbors(
2405 node_index_to_neighbors_by_cost_class_
2406 ->GetOutgoingNeighborsOfNodeForCostClass(cost_class,
2408 [
this, insert_after, cost_class](int64_t node) {
2409 return node_index_to_neighbors_by_cost_class_
2410 ->IsNeighborhoodArcForCostClass(cost_class, insert_after, node);
2415 if (!node_index_to_neighbors_by_cost_class_->IsFullNeighborhood()) {
2418 const int64_t insert_before =
Value(insert_after);
2419 if (
model()->IsEnd(insert_before)) {
2423 return add_node_entries_for_neighbors(
2424 node_index_to_neighbors_by_cost_class_
2425 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, insert_before),
2426 [
this, insert_before, cost_class](int64_t node) {
2427 return node_index_to_neighbors_by_cost_class_
2428 ->IsNeighborhoodArcForCostClass(cost_class, node, insert_before);
2434void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2435 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2436 NodeEntryQueue* queue) {
2438 if (!vehicle_var->Contains(vehicle)) {
2439 if (!UseEmptyVehicleTypeCuratorForVehicle(vehicle, all_vehicles))
return;
2442 const auto vehicle_is_compatible = [vehicle_var](
int vehicle) {
2443 return vehicle_var->Contains(vehicle);
2445 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2446 empty_vehicle_type_curator_->Type(vehicle),
2447 vehicle_is_compatible)) {
2451 const int num_allowed_vehicles = vehicle_var->
Size();
2453 const int64_t penalty_shift =
2454 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2457 if (vehicle == -1) {
2458 DCHECK_EQ(node, insert_after);
2459 if (!all_vehicles) {
2465 queue->PushInsertion(node, node, -1, num_allowed_vehicles,
2466 CapSub(node_penalty, penalty_shift));
2470 const std::optional<int64_t> insertion_cost =
2473 if (!insertion_cost.has_value())
return;
2474 if (!all_vehicles && *insertion_cost > node_penalty) {
2480 queue->PushInsertion(node, insert_after, vehicle, num_allowed_vehicles,
2481 CapSub(*insertion_cost, penalty_shift));
2485 int pickup,
int delivery,
int vehicle, absl::Span<const int> path,
2486 const std::vector<bool>& path_node_is_pickup,
2487 const std::vector<bool>& path_node_is_delivery,
2489 const int num_nodes = path.size();
2490 DCHECK_GE(num_nodes, 2);
2491 const int kNoPrevIncrease = -1;
2492 const int kNoNextDecrease = num_nodes;
2494 prev_decrease_.resize(num_nodes - 1);
2495 prev_increase_.resize(num_nodes - 1);
2496 int prev_decrease = 0;
2497 int prev_increase = kNoPrevIncrease;
2498 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2499 if (path_node_is_delivery[pos]) prev_decrease = pos;
2500 prev_decrease_[pos] = prev_decrease;
2501 if (path_node_is_pickup[pos]) prev_increase = pos;
2502 prev_increase_[pos] = prev_increase;
2506 next_decrease_.resize(num_nodes - 1);
2507 next_increase_.resize(num_nodes - 1);
2508 int next_increase = num_nodes - 1;
2509 int next_decrease = kNoNextDecrease;
2510 for (
int pos = num_nodes - 2; pos >= 0; --pos) {
2511 next_decrease_[pos] = next_decrease;
2512 if (path_node_is_delivery[pos]) next_decrease = pos;
2513 next_increase_[pos] = next_increase;
2514 if (path_node_is_pickup[pos]) next_increase = pos;
2518 auto append = [pickup, delivery, vehicle, num_nodes, path, &insertions](
2519 int pickup_pos,
int delivery_pos) {
2520 if (pickup_pos < 0 || num_nodes - 1 <= pickup_pos)
return;
2521 if (delivery_pos < 0 || num_nodes - 1 <= delivery_pos)
return;
2522 const int delivery_pred =
2523 pickup_pos == delivery_pos ? pickup : path[delivery_pos];
2525 vehicle, {{.pred = path[pickup_pos], .node = pickup},
2526 {.pred = delivery_pred, .node = delivery}});
2530 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2531 const bool is_after_decrease = prev_increase_[pos] < prev_decrease_[pos];
2532 const bool is_before_increase = next_increase_[pos] < next_decrease_[pos];
2533 if (is_after_decrease) {
2534 append(prev_increase_[pos], pos);
2535 if (is_before_increase) {
2536 append(pos, next_increase_[pos] - 1);
2537 append(pos, next_decrease_[pos] - 1);
2545 if (next_increase_[pos] - 1 != pos) {
2547 if (prev_decrease_[pos] != pos) append(prev_decrease_[pos], pos);
2551 append(pos, next_decrease_[pos] - 1);
2552 if (!is_before_increase && next_decrease_[pos] - 1 != pos) {
2561 if (prev_increase_[pos] != pos) append(prev_increase_[pos], pos);
2572 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
2576 std::function<
bool(
const std::vector<RoutingModel::VariableValuePair>&,
2577 std::vector<RoutingModel::VariableValuePair>*)>
2578 optimize_on_insertion)
2580 std::move(evaluator), nullptr,
2582 pair_insertion_strategy_(lci_params.pickup_delivery_strategy()),
2584 lci_params.insertion_sorting_properties())),
2585 use_first_solution_hint_(use_first_solution_hint),
2586 bin_capacities_(bin_capacities),
2587 optimize_on_insertion_(
std::move(optimize_on_insertion)),
2588 use_random_insertion_order_(
2589 !insertion_sorting_properties_.empty() &&
2590 insertion_sorting_properties_.front() ==
2592 DCHECK(!insertion_sorting_properties_.empty());
2598 synchronize_insertion_optimizer_ =
true;
2602 if (hint !=
nullptr && use_first_solution_hint_) {
2604 for (
int i = 0; i <
model()->
Nexts().size(); ++i) {
2607 if (element !=
nullptr && element->
Bound()) {
2613 ComputeInsertionOrder();
2616bool LocalCheapestInsertionFilteredHeuristic::OptimizeOnInsertion(
2617 std::vector<int> delta_indices) {
2618 if (optimize_on_insertion_ ==
nullptr)
return false;
2619 std::vector<RoutingModel::VariableValuePair> in_state;
2620 if (synchronize_insertion_optimizer_) {
2621 for (
int i = 0; i <
model()->
Nexts().size(); ++i) {
2623 in_state.push_back({i,
Value(i)});
2626 synchronize_insertion_optimizer_ =
false;
2629 in_state.push_back({index,
Value(index)});
2632 std::vector<RoutingModel::VariableValuePair> out_state;
2633 optimize_on_insertion_(in_state, &out_state);
2634 if (out_state.empty())
return false;
2635 for (
const auto& [var, value] : out_state) {
2645std::vector<std::vector<int64_t>> ComputeStartToPickupCosts(
2646 const RoutingModel& model, absl::Span<const int64_t> pickups,
2648 std::vector<std::vector<int64_t>> pickup_costs(model.Size());
2649 for (int64_t pickup : pickups) {
2650 std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2651 cost_from_start.resize(model.vehicles(), -1);
2653 ProcessVehicleStartEndCosts(
2655 [&cost_from_start](int64_t cost,
int v) { cost_from_start[v] = cost; },
2656 vehicle_set,
false,
true);
2658 return pickup_costs;
2662std::vector<std::vector<int64_t>> ComputeDeliveryToEndCosts(
2663 const RoutingModel& model, absl::Span<const int64_t> deliveries,
2665 std::vector<std::vector<int64_t>> delivery_costs(model.Size());
2666 for (int64_t delivery : deliveries) {
2667 std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2668 cost_to_end.resize(model.vehicles(), -1);
2670 ProcessVehicleStartEndCosts(
2672 [&cost_to_end](int64_t cost,
int v) { cost_to_end[v] = cost; },
2673 vehicle_set,
true,
false);
2675 return delivery_costs;
2682int64_t GetNegMaxDistanceFromVehicles(
const RoutingModel& model,
2684 const auto& [pickups, deliveries] =
2685 model.GetPickupAndDeliveryPairs()[pair_index];
2688 for (
int v = 0; v < model.vehicles(); ++v) vehicle_set.Set(v);
2691 const std::vector<std::vector<int64_t>> pickup_costs =
2692 ComputeStartToPickupCosts(model, pickups, vehicle_set);
2695 const std::vector<std::vector<int64_t>> delivery_costs =
2696 ComputeDeliveryToEndCosts(model, deliveries, vehicle_set);
2698 int64_t max_pair_distance = 0;
2699 for (int64_t pickup : pickups) {
2700 const std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2701 for (int64_t delivery : deliveries) {
2702 const std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2703 int64_t closest_vehicle_distance = std::numeric_limits<int64_t>::max();
2704 for (
int v = 0; v < model.vehicles(); v++) {
2705 if (cost_from_start[v] < 0 || cost_to_end[v] < 0) {
2709 closest_vehicle_distance =
2710 std::min(closest_vehicle_distance,
2711 CapAdd(cost_from_start[v], cost_to_end[v]));
2713 max_pair_distance = std::max(max_pair_distance, closest_vehicle_distance);
2716 return CapOpp(max_pair_distance);
2724std::optional<int64_t> GetAvgPickupDeliveryPairDistanceFromVehicles(
2727 const auto& [pickups, deliveries] =
2728 model.GetPickupAndDeliveryPairs()[pair_index];
2731 const std::vector<std::vector<int64_t>> pickup_costs =
2732 ComputeStartToPickupCosts(model, pickups, vehicle_set);
2735 const std::vector<std::vector<int64_t>> delivery_costs =
2736 ComputeDeliveryToEndCosts(model, deliveries, vehicle_set);
2738 int64_t avg_pair_distance_sum = 0;
2739 int valid_pairs = 0;
2740 for (int64_t pickup : pickups) {
2741 const std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2742 for (int64_t delivery : deliveries) {
2743 const std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2744 int64_t distance_from_vehicle_sum = 0;
2745 int valid_vehicles = 0;
2746 for (
int v = 0; v < model.vehicles(); v++) {
2747 if (cost_from_start[v] < 0 || cost_to_end[v] < 0) {
2751 distance_from_vehicle_sum =
2752 CapAdd(distance_from_vehicle_sum,
2753 CapAdd(cost_from_start[v], cost_to_end[v]));
2756 if (valid_vehicles > 0) {
2757 avg_pair_distance_sum =
CapAdd(
2758 distance_from_vehicle_sum / valid_vehicles, avg_pair_distance_sum);
2763 return valid_pairs > 0
2764 ? std::make_optional<int64_t>(avg_pair_distance_sum / valid_pairs)
2770int64_t GetMaxFiniteDimensionCapacity(
2772 const std::vector<RoutingDimension*>& dimensions) {
2773 int64_t max_capacity = 0;
2775 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
2776 const int64_t capacity = dimension->vehicle_capacities()[vehicle];
2778 max_capacity = std::max(max_capacity, capacity);
2781 return max_capacity;
2786int64_t GetAvgNodeUnaryDimensionUsage(
2787 const RoutingModel& model,
const std::vector<RoutingDimension*>& dimensions,
2788 int64_t max_vehicle_capacity, int64_t node) {
2789 if (dimensions.empty() || max_vehicle_capacity == 0) {
2793 double dimension_usage_sum = 0;
2796 DCHECK(dimension->IsUnary());
2797 double dimension_usage_per_vehicle_sum = 0;
2798 int valid_vehicles = 0;
2801 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
2802 const int64_t capacity = dimension->vehicle_capacities()[vehicle];
2803 if (capacity == 0)
continue;
2804 DCHECK_GE(capacity, 0);
2810 dimension->GetUnaryTransitEvaluator(vehicle);
2811 dimension_usage_per_vehicle_sum +=
2812 1.0 * std::abs(transit_evaluator(node)) / capacity;
2815 if (valid_vehicles > 0) {
2818 dimension_usage_sum +=
2819 std::min(1.0, dimension_usage_per_vehicle_sum / valid_vehicles);
2826 dimension_usage_sum);
2831int64_t GetAvgPickupDeliveryPairUnaryDimensionUsage(
2832 const RoutingModel& model,
const std::vector<RoutingDimension*>& dimensions,
2833 int64_t max_vehicle_capacity,
int pair_index) {
2834 if (dimensions.empty()) {
2838 const auto& [pickups, deliveries] =
2839 model.GetPickupAndDeliveryPairs()[pair_index];
2840 if (pickups.empty() || deliveries.empty()) {
2844 double dimension_usage_sum = 0;
2846 double dimension_usage_per_vehicle_sum = 0;
2847 int valid_vehicles = 0;
2849 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
2850 const int64_t capacity = dimension->vehicle_capacities()[vehicle];
2851 if (capacity == 0)
continue;
2857 dimension->GetUnaryTransitEvaluator(vehicle);
2859 double pickup_sum = 0;
2860 for (int64_t pickup : pickups) {
2861 pickup_sum += 1.0 * std::abs(transit_evaluator(pickup)) / capacity;
2863 const double avg_pickup_usage = pickup_sum / pickups.size();
2864 double delivery_sum = 0;
2865 for (int64_t delivery : deliveries) {
2866 delivery_sum += 1.0 * std::abs(transit_evaluator(delivery)) / capacity;
2868 const double avg_delivery_usage = delivery_sum / deliveries.size();
2869 dimension_usage_per_vehicle_sum +=
2870 std::max(avg_pickup_usage, avg_delivery_usage);
2873 if (valid_vehicles > 0) {
2876 dimension_usage_sum +=
2877 std::min(1.0, dimension_usage_per_vehicle_sum / valid_vehicles);
2884 dimension_usage_sum);
2888void LocalCheapestInsertionFilteredHeuristic::ComputeInsertionOrder() {
2889 if (use_random_insertion_order_) {
2890 if (insertion_order_.empty()) {
2892 insertion_order_.reserve(
model.Size() +
2893 model.GetPickupAndDeliveryPairs().size());
2894 for (
int pair_index = 0;
2895 pair_index <
model.GetPickupAndDeliveryPairs().size();
2897 insertion_order_.push_back(
2898 {.is_node_index =
false, .index = pair_index});
2900 for (
int node = 0; node <
model.Size(); ++node) {
2901 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
2902 insertion_order_.push_back({.is_node_index =
true, .index = node});
2906 absl::c_shuffle(insertion_order_, rnd_);
2910 if (!insertion_order_.empty())
return;
2919 insertion_order_.reserve(
model.Size() +
2920 model.GetPickupAndDeliveryPairs().size());
2922 auto get_insertion_properties =
2923 [
this](int64_t penalty, int64_t num_allowed_vehicles,
2924 int64_t avg_distance_to_vehicle,
2925 int64_t neg_min_distance_to_vehicles,
int hint_weight,
2926 int reversed_hint_weight, int64_t avg_dimension_usage) {
2927 DCHECK_NE(0, num_allowed_vehicles);
2928 absl::InlinedVector<int64_t, 8> properties;
2929 properties.reserve(insertion_sorting_properties_.size());
2935 properties.push_back(-hint_weight);
2936 properties.push_back(-reversed_hint_weight);
2938 bool neg_min_distance_to_vehicles_appended =
false;
2939 for (
const int property : insertion_sorting_properties_) {
2941 case LocalCheapestInsertionParameters::
2942 SORTING_PROPERTY_ALLOWED_VEHICLES:
2943 properties.push_back(num_allowed_vehicles);
2946 properties.push_back(
CapOpp(penalty));
2948 case LocalCheapestInsertionParameters::
2949 SORTING_PROPERTY_PENALTY_OVER_ALLOWED_VEHICLES_RATIO:
2950 properties.push_back(
CapOpp(penalty / num_allowed_vehicles));
2952 case LocalCheapestInsertionParameters::
2953 SORTING_PROPERTY_HIGHEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS:
2954 properties.push_back(
CapOpp(avg_distance_to_vehicle));
2956 case LocalCheapestInsertionParameters::
2957 SORTING_PROPERTY_LOWEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS:
2958 properties.push_back(avg_distance_to_vehicle);
2960 case LocalCheapestInsertionParameters::
2961 SORTING_PROPERTY_LOWEST_MIN_ARC_COST_TO_VEHICLE_START_ENDS:
2962 properties.push_back(neg_min_distance_to_vehicles);
2963 neg_min_distance_to_vehicles_appended =
true;
2965 case LocalCheapestInsertionParameters::
2966 SORTING_PROPERTY_HIGHEST_DIMENSION_USAGE:
2967 properties.push_back(
CapOpp(avg_dimension_usage));
2971 <<
"Unknown RoutingSearchParameter::InsertionSortingProperty "
2981 if (!neg_min_distance_to_vehicles_appended) {
2982 properties.push_back(neg_min_distance_to_vehicles);
2990 bool compute_avg_pickup_delivery_pair_distance_from_vehicles =
false;
2991 bool compute_avg_dimension_usage =
false;
2993 property : insertion_sorting_properties_) {
2995 LocalCheapestInsertionParameters::
2996 SORTING_PROPERTY_HIGHEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS ||
2998 LocalCheapestInsertionParameters::
2999 SORTING_PROPERTY_LOWEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS) {
3000 compute_avg_pickup_delivery_pair_distance_from_vehicles =
true;
3002 if (property == LocalCheapestInsertionParameters::
3003 SORTING_PROPERTY_HIGHEST_DIMENSION_USAGE) {
3004 compute_avg_dimension_usage =
true;
3008 Bitset64<int> vehicle_set(
model.vehicles());
3009 for (
int v = 0; v <
model.vehicles(); ++v) vehicle_set.Set(v);
3011 const std::vector<RoutingDimension*> unary_dimensions =
3012 compute_avg_dimension_usage ?
model.GetUnaryDimensions()
3013 : std::vector<RoutingDimension*>();
3015 const int64_t max_dimension_capacity =
3016 compute_avg_dimension_usage
3017 ? GetMaxFiniteDimensionCapacity(
model, unary_dimensions)
3021 const std::vector<PickupDeliveryPair>& pairs =
3022 model.GetPickupAndDeliveryPairs();
3024 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
3025 const auto& [pickups, deliveries] = pairs[pair_index];
3026 int64_t num_allowed_vehicles = std::numeric_limits<int64_t>::max();
3027 int64_t pickup_penalty = 0;
3028 int hint_weight = 0;
3029 int reversed_hint_weight = 0;
3030 for (int64_t pickup : pickups) {
3031 num_allowed_vehicles =
3032 std::min(num_allowed_vehicles,
3033 static_cast<int64_t
>(
model.VehicleVar(pickup)->Size()));
3035 std::max(pickup_penalty,
model.UnperformedPenalty(pickup));
3039 int64_t delivery_penalty = 0;
3040 for (int64_t delivery : deliveries) {
3041 num_allowed_vehicles =
3042 std::min(num_allowed_vehicles,
3043 static_cast<int64_t
>(
model.VehicleVar(delivery)->Size()));
3045 std::max(delivery_penalty,
model.UnperformedPenalty(delivery));
3050 const std::optional<int64_t> maybe_avg_pair_to_vehicle_cost =
3051 compute_avg_pickup_delivery_pair_distance_from_vehicles
3052 ? GetAvgPickupDeliveryPairDistanceFromVehicles(
model, pair_index,
3054 : std::make_optional<int64_t>(0);
3056 if (!maybe_avg_pair_to_vehicle_cost.has_value()) {
3062 const int64_t avg_pair_dimension_usage =
3063 compute_avg_dimension_usage
3064 ? GetAvgPickupDeliveryPairUnaryDimensionUsage(
3065 model, unary_dimensions, max_dimension_capacity, pair_index)
3068 absl::InlinedVector<int64_t, 8> properties = get_insertion_properties(
3069 CapAdd(pickup_penalty, delivery_penalty), num_allowed_vehicles,
3070 maybe_avg_pair_to_vehicle_cost.value(),
3071 GetNegMaxDistanceFromVehicles(
model, pair_index), hint_weight,
3072 reversed_hint_weight, avg_pair_dimension_usage);
3074 insertion_order_.push_back({.properties = std::move(properties),
3076 .is_node_index =
false,
3077 .index = pair_index});
3080 for (
int node = 0; node <
model.Size(); ++node) {
3081 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
3083 int64_t min_distance = std::numeric_limits<int64_t>::max();
3084 int64_t sum_distance = 0;
3085 ProcessVehicleStartEndCosts(
3087 [&min_distance, &sum_distance](int64_t dist,
int) {
3088 min_distance = std::min(min_distance, dist);
3089 sum_distance =
CapAdd(dist, sum_distance);
3092 DCHECK_GT(vehicle_set.size(), 0);
3094 const int64_t avg_dimension_usage =
3095 compute_avg_dimension_usage
3096 ? GetAvgNodeUnaryDimensionUsage(
model, unary_dimensions,
3097 max_dimension_capacity, node)
3100 absl::InlinedVector<int64_t, 8> properties = get_insertion_properties(
3101 model.UnperformedPenalty(node),
model.VehicleVar(node)->Size(),
3102 sum_distance / vehicle_set.size(),
CapOpp(min_distance),
3104 insertion_order_.push_back({.properties = std::move(properties),
3106 .is_node_index =
true,
3110 absl::c_sort(insertion_order_, std::greater<Seed>());
3111 absl::c_reverse(insertion_order_);
3114bool LocalCheapestInsertionFilteredHeuristic::InsertPair(
3115 int64_t pickup, int64_t insert_pickup_after, int64_t delivery,
3116 int64_t insert_delivery_after,
int vehicle) {
3117 const int64_t insert_pickup_before =
Value(insert_pickup_after);
3118 InsertBetween(pickup, insert_pickup_after, insert_pickup_before, vehicle);
3119 DCHECK_NE(insert_delivery_after, insert_pickup_after);
3120 const int64_t insert_delivery_before = (insert_delivery_after == pickup)
3121 ? insert_pickup_before
3122 :
Value(insert_delivery_after);
3123 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
3127 if (
Evaluate(
true,
true).has_value()) {
3128 OptimizeOnInsertion(std::move(indices));
3134void LocalCheapestInsertionFilteredHeuristic::InsertBestPickupThenDelivery(
3136 for (
int pickup : index_pair.pickup_alternatives) {
3137 std::vector<NodeInsertion> pickup_insertions =
3138 ComputeEvaluatorSortedPositions(pickup);
3139 for (
int delivery : index_pair.delivery_alternatives) {
3141 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
3142 const int vehicle = pickup_insertion.vehicle;
3143 if (!
model()->VehicleVar(delivery)->
Contains(vehicle))
continue;
3144 if (MustUpdateBinCapacities() &&
3145 !bin_capacities_->CheckAdditionsFeasibility({pickup, delivery},
3149 for (
const NodeInsertion& delivery_insertion :
3150 ComputeEvaluatorSortedPositionsOnRouteAfter(
3151 delivery, pickup,
Value(pickup_insertion.insert_after),
3153 if (InsertPair(pickup, pickup_insertion.insert_after, delivery,
3154 delivery_insertion.insert_after, vehicle)) {
3155 if (MustUpdateBinCapacities()) {
3156 bin_capacities_->AddItemToBin(pickup, vehicle);
3157 bin_capacities_->AddItemToBin(delivery, vehicle);
3168void LocalCheapestInsertionFilteredHeuristic::InsertBestPair(
3170 for (
int pickup : pair.pickup_alternatives) {
3171 for (
int delivery : pair.delivery_alternatives) {
3173 std::vector<PickupDeliveryInsertion> sorted_pair_positions =
3174 ComputeEvaluatorSortedPairPositions(pickup, delivery);
3175 if (sorted_pair_positions.empty())
continue;
3176 for (
const auto [insert_pickup_after, insert_delivery_after,
3177 unused_hint_weight, unused_value, vehicle] :
3178 sorted_pair_positions) {
3179 if (InsertPair(pickup, insert_pickup_after, delivery,
3180 insert_delivery_after, vehicle)) {
3181 if (MustUpdateBinCapacities()) {
3182 bin_capacities_->AddItemToBin(pickup, vehicle);
3183 bin_capacities_->AddItemToBin(delivery, vehicle);
3193void LocalCheapestInsertionFilteredHeuristic::InsertBestPairMultitour(
3195 using InsertionSequence = InsertionSequenceContainer::InsertionSequence;
3196 using Insertion = InsertionSequenceContainer::Insertion;
3198 std::vector<int> path;
3199 std::vector<bool> path_node_is_pickup;
3200 std::vector<bool> path_node_is_delivery;
3202 auto fill_path = [&path, &path_node_is_pickup, &path_node_is_delivery,
3203 this](
int vehicle) {
3205 path_node_is_pickup.clear();
3206 path_node_is_delivery.clear();
3209 for (
int node = start; node !=
end; node =
Value(node)) {
3210 path.push_back(node);
3211 path_node_is_pickup.push_back(
model()->IsPickup(node));
3212 path_node_is_delivery.push_back(
model()->IsDelivery(node));
3214 path.push_back(
end);
3218 auto price_insertion_sequences = [
this]() {
3219 for (InsertionSequence sequence : insertion_container_) {
3220 int64_t sequence_cost = 0;
3221 int previous_node = -1;
3222 int previous_succ = -1;
3223 int hint_weight = 0;
3224 for (
const Insertion& insertion : sequence) {
3225 const int succ = previous_node == insertion.pred
3227 :
Value(insertion.pred);
3228 hint_weight +=
IsHint(insertion.pred, insertion.node);
3229 hint_weight +=
IsHint(insertion.node, succ);
3232 sequence.Vehicle());
3235 insertion.node, insertion.pred, succ, sequence.Vehicle());
3238 previous_node = insertion.node;
3239 previous_succ = succ;
3246 sequence.Cost() = sequence_cost;
3247 sequence.SetHintWeight(hint_weight);
3249 if (bin_capacities_ ==
nullptr ||
evaluator_ ==
nullptr)
return;
3250 for (InsertionSequence sequence : insertion_container_) {
3251 const int64_t old_cost = bin_capacities_->TotalCost();
3252 for (
const Insertion& insertion : sequence) {
3253 bin_capacities_->AddItemToBin(insertion.node, sequence.Vehicle());
3255 const int64_t new_cost = bin_capacities_->TotalCost();
3256 const int64_t delta_cost =
CapSub(new_cost, old_cost);
3257 CapAddTo(delta_cost, &sequence.Cost());
3258 for (
const Insertion& insertion : sequence) {
3259 bin_capacities_->RemoveItemFromBin(insertion.node, sequence.Vehicle());
3264 for (
int pickup : pair.pickup_alternatives) {
3267 for (
int delivery : pair.delivery_alternatives) {
3269 insertion_container_.Clear();
3270 std::unique_ptr<IntVarIterator> pickup_vehicles(
3271 pickup_vehicle_var->MakeDomainIterator(
false));
3272 for (
const int vehicle : InitAndGetValues(pickup_vehicles.get())) {
3273 if (vehicle == -1)
continue;
3274 if (!delivery_vehicle_var->Contains(vehicle))
continue;
3275 if (MustUpdateBinCapacities() &&
3276 !bin_capacities_->CheckAdditionsFeasibility({pickup, delivery},
3281 insertion_generator_.AppendPickupDeliveryMultitourInsertions(
3282 pickup, delivery, vehicle, path, path_node_is_pickup,
3283 path_node_is_delivery, insertion_container_);
3286 price_insertion_sequences();
3288 insertion_container_.RemoveIf(
3289 [](
const InsertionSequence& sequence) ->
bool {
3292 insertion_container_.Sort();
3293 for (InsertionSequence sequence : insertion_container_) {
3295 int previous_node = -1;
3296 int previous_succ = -1;
3297 const int vehicle = sequence.Vehicle();
3298 for (
const Insertion& insertion : sequence) {
3299 const int succ = previous_node == insertion.pred
3301 :
Value(insertion.pred);
3302 InsertBetween(insertion.node, insertion.pred, succ, vehicle);
3303 previous_node = insertion.node;
3304 previous_succ = succ;
3309 if (MustUpdateBinCapacities()) {
3310 bin_capacities_->AddItemToBin(pickup, vehicle);
3311 bin_capacities_->AddItemToBin(delivery, vehicle);
3322 std::vector<bool>* data) {
3323 for (
const int64_t pickup : pair.pickup_alternatives) {
3324 data->at(pickup) =
false;
3326 for (
const int64_t delivery : pair.delivery_alternatives) {
3327 data->at(delivery) =
false;
3336 if (MustUpdateBinCapacities()) {
3337 bin_capacities_->ClearItems();
3338 for (
int vehicle = 0; vehicle <
model.vehicles(); ++vehicle) {
3339 const int start =
Value(
model.Start(vehicle));
3340 for (
int node = start; !
model.IsEnd(node); node =
Value(node)) {
3341 bin_capacities_->AddItemToBin(node, vehicle);
3346 const std::vector<PickupDeliveryPair>& pairs =
3347 model.GetPickupAndDeliveryPairs();
3348 std::vector<bool> ignore_pair_index(pairs.size(),
false);
3349 std::vector<bool> insert_as_single_node(
model.Size(),
true);
3350 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
3351 const auto& [pickups, deliveries] = pairs[pair_index];
3352 bool pickup_contained =
false;
3353 for (int64_t pickup : pickups) {
3355 pickup_contained =
true;
3359 bool delivery_contained =
false;
3360 for (int64_t delivery : deliveries) {
3362 delivery_contained =
true;
3366 ignore_pair_index[pair_index] = pickup_contained || delivery_contained;
3367 if (pickup_contained == delivery_contained) {
3372 SetFalseForAllAlternatives(pairs[pair_index], &insert_as_single_node);
3376 for (
const Seed& seed : insertion_order_) {
3377 const int index = seed.index;
3378 if (!seed.is_node_index) {
3379 if (ignore_pair_index[index])
continue;
3381 const auto& pair = pairs[index];
3382 switch (pair_insertion_strategy_) {
3385 InsertBestPair(pair);
3388 InsertBestPickupThenDelivery(pair);
3390 case LocalCheapestInsertionParameters::
3391 BEST_PICKUP_DELIVERY_PAIR_MULTITOUR:
3392 InsertBestPairMultitour(pair);
3395 LOG(ERROR) <<
"Unknown pair insertion strategy value.";
3402 if (
Contains(index) || !insert_as_single_node[index]) {
3405 for (
const NodeInsertion& insertion :
3406 ComputeEvaluatorSortedPositions(index)) {
3411 Value(insertion.insert_after), insertion.vehicle);
3416 if (MustUpdateBinCapacities()) {
3417 bin_capacities_->AddItemToBin(index, insertion.vehicle);
3419 OptimizeOnInsertion(std::move(indices));
3428void LocalCheapestInsertionFilteredHeuristic::AppendInsertionPositionsAfter(
3429 int64_t node_to_insert, int64_t start, int64_t next_after_start,
3430 int vehicle, std::vector<NodeInsertion>* node_insertions) {
3431 DCHECK(node_insertions !=
nullptr);
3432 int64_t insert_after = start;
3433 if (!
model()->VehicleVar(node_to_insert)->
Contains(vehicle))
return;
3434 while (!
model()->IsEnd(insert_after)) {
3435 const int64_t insert_before =
3436 (insert_after == start) ? next_after_start :
Value(insert_after);
3437 const int hint_weight =
IsHint(insert_after, node_to_insert) +
3438 IsHint(node_to_insert, insert_before) -
3439 IsHint(insert_after, insert_before);
3441 node_to_insert, insert_after, insert_before, vehicle, hint_weight);
3442 if (insertion_cost.has_value()) {
3443 node_insertions->push_back(
3444 {insert_after, vehicle, -hint_weight, *insertion_cost});
3446 insert_after = insert_before;
3450std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
3451LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
3455 if (node >= size)
return {};
3456 std::vector<NodeInsertion> sorted_insertions;
3458 std::unique_ptr<IntVarIterator> node_vehicles(
3459 vehicle_var->MakeDomainIterator(
false));
3460 for (
const int vehicle : InitAndGetValues(node_vehicles.get())) {
3461 if (vehicle == -1)
continue;
3462 if (MustUpdateBinCapacities() &&
3463 !bin_capacities_->CheckAdditionFeasibility(node, vehicle)) {
3466 const int64_t start =
model()->
Start(vehicle);
3467 const size_t old_num_insertions = sorted_insertions.size();
3468 AppendInsertionPositionsAfter(node, start,
Value(start), vehicle,
3469 &sorted_insertions);
3470 if (MustUpdateBinCapacities() &&
evaluator_) {
3472 const int64_t old_cost = bin_capacities_->TotalCost();
3473 bin_capacities_->AddItemToBin(node, vehicle);
3474 const int64_t new_cost = bin_capacities_->TotalCost();
3475 bin_capacities_->RemoveItemFromBin(node, vehicle);
3476 const int64_t delta_cost =
CapSub(new_cost, old_cost);
3478 for (
size_t i = old_num_insertions;
i < sorted_insertions.size(); ++
i) {
3479 CapAddTo(delta_cost, &sorted_insertions[i].value);
3483 absl::c_sort(sorted_insertions);
3484 return sorted_insertions;
3487std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
3488LocalCheapestInsertionFilteredHeuristic::
3489 ComputeEvaluatorSortedPositionsOnRouteAfter(int64_t node, int64_t start,
3490 int64_t next_after_start,
3494 if (node >= size)
return {};
3495 std::vector<NodeInsertion> sorted_insertions;
3496 AppendInsertionPositionsAfter(node, start, next_after_start, vehicle,
3497 &sorted_insertions);
3498 absl::c_sort(sorted_insertions);
3499 return sorted_insertions;
3502std::vector<PickupDeliveryInsertion>
3503LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions(
3504 int pickup,
int delivery) {
3505 std::vector<PickupDeliveryInsertion> sorted_pickup_delivery_insertions;
3507 DCHECK_LT(pickup, size);
3508 DCHECK_LT(delivery, size);
3511 std::unique_ptr<IntVarIterator> pickup_vehicles(
3512 pickup_vehicle_var->MakeDomainIterator(
false));
3513 for (
const int vehicle : InitAndGetValues(pickup_vehicles.get())) {
3514 if (vehicle == -1)
continue;
3515 if (!delivery_vehicle_var->Contains(vehicle))
continue;
3516 if (MustUpdateBinCapacities() &&
3517 !bin_capacities_->CheckAdditionsFeasibility({pickup, delivery},
3521 int64_t insert_pickup_after =
model()->
Start(vehicle);
3522 while (!
model()->IsEnd(insert_pickup_after)) {
3523 const int64_t insert_pickup_before =
Value(insert_pickup_after);
3524 int64_t insert_delivery_after = pickup;
3525 while (!
model()->IsEnd(insert_delivery_after)) {
3527 const int64_t insert_delivery_before =
3528 insert_delivery_after == pickup ? insert_pickup_before
3529 :
Value(insert_delivery_after);
3530 const int hint_weight =
IsHint(insert_pickup_after, pickup) +
3531 IsHint(insert_delivery_after, delivery) +
3532 IsHint(pickup, insert_pickup_before) +
3533 IsHint(delivery, insert_delivery_before);
3535 pickup, insert_pickup_after, delivery, insert_delivery_after,
3536 vehicle, hint_weight);
3537 if (insertion_cost.has_value()) {
3538 int64_t total_cost = *insertion_cost;
3539 if (
evaluator_ && MustUpdateBinCapacities()) {
3540 const int64_t old_cost = bin_capacities_->TotalCost();
3541 bin_capacities_->AddItemToBin(pickup, vehicle);
3542 bin_capacities_->AddItemToBin(delivery, vehicle);
3543 const int64_t new_cost = bin_capacities_->TotalCost();
3545 bin_capacities_->RemoveItemFromBin(pickup, vehicle);
3546 bin_capacities_->RemoveItemFromBin(delivery, vehicle);
3548 sorted_pickup_delivery_insertions.push_back(
3549 {insert_pickup_after, insert_delivery_after, -hint_weight,
3550 total_cost, vehicle});
3552 insert_delivery_after = insert_delivery_before;
3554 insert_pickup_after = insert_pickup_before;
3557 absl::c_sort(sorted_pickup_delivery_insertions);
3558 return sorted_pickup_delivery_insertions;
3570 const int num_nexts =
model()->
Nexts().size();
3571 std::vector<std::vector<int64_t>> deliveries(num_nexts);
3572 std::vector<std::vector<int64_t>> pickups(num_nexts);
3573 for (
const auto& [pickup_alternatives, delivery_alternatives] :
3574 model()->GetPickupAndDeliveryPairs()) {
3575 for (
int pickup : pickup_alternatives) {
3576 for (
int delivery : delivery_alternatives) {
3577 deliveries[pickup].push_back(delivery);
3578 pickups[delivery].push_back(pickup);
3585 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
3586 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3587 sorted_vehicles[vehicle] = vehicle;
3589 absl::c_sort(sorted_vehicles,
3590 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
3592 for (
const int vehicle : sorted_vehicles) {
3594 bool extend_route =
true;
3599 while (extend_route) {
3600 extend_route =
false;
3602 int64_t index = last_node;
3609 while (found && !
model()->IsEnd(index)) {
3611 std::vector<int64_t> neighbors;
3612 if (index <
model()->Nexts().size()) {
3613 std::unique_ptr<IntVarIterator> it(
3614 model()->Nexts()[index]->MakeDomainIterator(
false));
3616 neighbors = GetPossibleNextsFromIterator(index, next_values.begin(),
3619 for (
int i = 0; !found && i < neighbors.size(); ++i) {
3623 next = FindTopSuccessor(index, neighbors);
3626 SortSuccessors(index, &neighbors);
3627 ABSL_FALLTHROUGH_INTENDED;
3629 next = neighbors[i];
3631 if (
model()->IsEnd(next) && next !=
end) {
3635 if (!
model()->IsEnd(next) && !pickups[next].empty()) {
3636 bool contains_pickups =
false;
3637 for (int64_t pickup : pickups[next]) {
3639 contains_pickups =
true;
3643 if (!contains_pickups) {
3647 std::vector<int64_t> next_deliveries;
3648 if (next < deliveries.size()) {
3649 next_deliveries = GetPossibleNextsFromIterator(
3650 next, deliveries[next].
begin(), deliveries[next].
end());
3652 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
3653 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
3658 delivery = FindTopSuccessor(next, next_deliveries);
3661 SortSuccessors(next, &next_deliveries);
3662 ABSL_FALLTHROUGH_INTENDED;
3664 delivery = next_deliveries[j];
3668 SetNext(index, next, vehicle);
3669 if (!
model()->IsEnd(next)) {
3673 SetNext(next, delivery, vehicle);
3682 if (
model()->IsEnd(
end) && last_node != delivery) {
3683 last_node = delivery;
3684 extend_route =
true;
3699bool CheapestAdditionFilteredHeuristic::
3700 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
3701 int vehicle2)
const {
3702 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
3703 builder_.GetStartChainEnd(vehicle1));
3704 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
3705 builder_.GetStartChainEnd(vehicle2));
3706 if (has_partial_route1 == has_partial_route2) {
3707 return vehicle2 < vehicle1;
3709 return has_partial_route2 < has_partial_route1;
3718 std::function<int64_t(int64_t, int64_t)> evaluator,
3722 evaluator_(
std::move(evaluator)) {}
3724int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
3725 int64_t node,
const std::vector<int64_t>& successors) {
3726 int64_t best_evaluation = std::numeric_limits<int64_t>::max();
3727 int64_t best_successor = -1;
3728 for (int64_t successor : successors) {
3729 const int64_t evaluation = (successor >= 0)
3730 ? evaluator_(node, successor)
3731 : std::numeric_limits<int64_t>::max();
3732 if (evaluation < best_evaluation ||
3733 (evaluation == best_evaluation && successor > best_successor)) {
3734 best_evaluation = evaluation;
3735 best_successor = successor;
3738 return best_successor;
3741void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
3742 int64_t node, std::vector<int64_t>* successors) {
3743 std::vector<std::pair<int64_t, int64_t>> values;
3744 values.reserve(successors->size());
3745 for (int64_t successor : *successors) {
3748 values.push_back({evaluator_(node, successor), successor});
3750 absl::c_sort(values, [](
const std::pair<int64_t, int64_t>& s1,
3751 const std::pair<int64_t, int64_t>& s2) {
3752 return s1.first < s2.first ||
3753 (s1.first == s2.first && s1.second > s2.second);
3755 successors->clear();
3756 for (
auto value : values) {
3757 successors->push_back(value.second);
3770 comparator_(
std::move(comparator)) {}
3772int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
3773 int64_t node,
const std::vector<int64_t>& successors) {
3774 return *absl::c_min_element(
3775 successors, [
this, node](
int successor1,
int successor2) {
3776 return comparator_(node, successor1, successor2);
3780void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
3781 int64_t node, std::vector<int64_t>* successors) {
3782 absl::c_sort(*successors, [
this, node](
int successor1,
int successor2) {
3783 return comparator_(node, successor1, successor2);
3835template <
typename Saving>
3840 : savings_db_(savings_db),
3841 index_in_sorted_savings_(0),
3842 vehicle_types_(vehicle_types),
3843 single_vehicle_type_(vehicle_types == 1),
3844 using_incoming_reinjected_saving_(false),
3849 sorted_savings_per_vehicle_type_.clear();
3850 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
3851 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
3852 savings.reserve(size * saving_neighbors);
3855 sorted_savings_.clear();
3856 costs_and_savings_per_arc_.clear();
3857 arc_indices_per_before_node_.clear();
3859 if (!single_vehicle_type_) {
3860 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
3861 arc_indices_per_before_node_.resize(size);
3862 for (
int before_node = 0; before_node < size; before_node++) {
3863 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
3866 skipped_savings_starting_at_.clear();
3867 skipped_savings_starting_at_.resize(size);
3868 skipped_savings_ending_at_.clear();
3869 skipped_savings_ending_at_.resize(size);
3870 incoming_reinjected_savings_ =
nullptr;
3871 outgoing_reinjected_savings_ =
nullptr;
3872 incoming_new_reinjected_savings_ =
nullptr;
3873 outgoing_new_reinjected_savings_ =
nullptr;
3877 int64_t before_node, int64_t after_node,
int vehicle_type) {
3878 CHECK(!sorted_savings_per_vehicle_type_.empty())
3879 <<
"Container not initialized!";
3880 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
3881 UpdateArcIndicesCostsAndSavings(before_node, after_node,
3882 {total_cost, saving});
3886 CHECK(!sorted_) <<
"Container already sorted!";
3888 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
3889 absl::c_sort(savings);
3892 if (single_vehicle_type_) {
3893 const auto& savings = sorted_savings_per_vehicle_type_[0];
3894 sorted_savings_.resize(savings.size());
3895 absl::c_transform(savings, sorted_savings_.begin(),
3896 [](
const Saving& saving) {
3897 return SavingAndArc({saving, -1});
3903 sorted_savings_.reserve(vehicle_types_ *
3904 costs_and_savings_per_arc_.size());
3906 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
3908 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
3909 costs_and_savings_per_arc_[arc_index];
3910 DCHECK(!costs_and_savings.empty());
3914 [](
const std::pair<int64_t, Saving>& cs1,
3915 const std::pair<int64_t, Saving>& cs2) {
return cs1 > cs2; });
3920 const int64_t cost = costs_and_savings.back().first;
3921 while (!costs_and_savings.empty() &&
3922 costs_and_savings.back().first == cost) {
3923 sorted_savings_.push_back(
3924 {costs_and_savings.back().second, arc_index});
3925 costs_and_savings.pop_back();
3928 absl::c_sort(sorted_savings_);
3929 next_saving_type_and_index_for_arc_.clear();
3930 next_saving_type_and_index_for_arc_.resize(
3931 costs_and_savings_per_arc_.size(), {-1, -1});
3934 index_in_sorted_savings_ = 0;
3939 return index_in_sorted_savings_ < sorted_savings_.size() ||
3940 HasReinjectedSavings();
3944 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
3946 <<
"Update() should be called between two calls to GetSaving() !";
3950 if (HasReinjectedSavings()) {
3951 if (incoming_reinjected_savings_ !=
nullptr &&
3952 outgoing_reinjected_savings_ !=
nullptr) {
3954 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
3955 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
3956 if (incoming_saving < outgoing_saving) {
3957 current_saving_ = incoming_saving;
3958 using_incoming_reinjected_saving_ =
true;
3960 current_saving_ = outgoing_saving;
3961 using_incoming_reinjected_saving_ =
false;
3964 if (incoming_reinjected_savings_ !=
nullptr) {
3965 current_saving_ = incoming_reinjected_savings_->front();
3966 using_incoming_reinjected_saving_ =
true;
3968 if (outgoing_reinjected_savings_ !=
nullptr) {
3969 current_saving_ = outgoing_reinjected_savings_->front();
3970 using_incoming_reinjected_saving_ =
false;
3974 current_saving_ = sorted_savings_[index_in_sorted_savings_];
3976 return current_saving_.
saving;
3979 void Update(
bool update_best_saving,
int type = -1) {
3980 CHECK(to_update_) <<
"Container already up to date!";
3981 if (update_best_saving) {
3982 const int64_t arc_index = current_saving_.arc_index;
3983 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
3985 if (!HasReinjectedSavings()) {
3986 index_in_sorted_savings_++;
3988 if (index_in_sorted_savings_ == sorted_savings_.size()) {
3989 sorted_savings_.swap(next_savings_);
3991 index_in_sorted_savings_ = 0;
3993 absl::c_sort(sorted_savings_);
3994 next_saving_type_and_index_for_arc_.clear();
3995 next_saving_type_and_index_for_arc_.resize(
3996 costs_and_savings_per_arc_.size(), {-1, -1});
3999 UpdateReinjectedSavings();
4004 CHECK(!single_vehicle_type_);
4009 CHECK(sorted_) <<
"Savings not sorted yet!";
4010 CHECK_LT(type, vehicle_types_);
4011 return sorted_savings_per_vehicle_type_[type];
4015 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
4016 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
4020 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
4021 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
4025 struct SavingAndArc {
4029 bool operator<(
const SavingAndArc& other)
const {
4030 return std::tie(saving, arc_index) <
4031 std::tie(other.saving, other.arc_index);
4037 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
4038 const Saving& saving = saving_and_arc.saving;
4039 const int64_t before_node = saving.before_node;
4040 const int64_t after_node = saving.after_node;
4041 if (!savings_db_->Contains(before_node)) {
4042 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
4044 if (!savings_db_->Contains(after_node)) {
4045 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
4059 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
4060 if (single_vehicle_type_) {
4063 SkipSavingForArc(current_saving_);
4066 CHECK_GE(arc_index, 0);
4067 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
4068 const int previous_index = type_and_index.second;
4069 const int previous_type = type_and_index.first;
4070 bool next_saving_added =
false;
4073 if (previous_index >= 0) {
4075 DCHECK_GE(previous_type, 0);
4076 if (type == -1 || previous_type == type) {
4079 next_saving_added =
true;
4080 next_saving = next_savings_[previous_index].saving;
4084 if (!next_saving_added &&
4085 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
4086 type_and_index.first = next_saving.vehicle_type;
4087 if (previous_index >= 0) {
4089 next_savings_[previous_index] = {next_saving, arc_index};
4092 type_and_index.second = next_savings_.size();
4093 next_savings_.push_back({next_saving, arc_index});
4095 next_saving_added =
true;
4101 SkipSavingForArc(current_saving_);
4105 if (next_saving_added) {
4106 SkipSavingForArc({next_saving, arc_index});
4111 void UpdateReinjectedSavings() {
4112 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
4113 &incoming_reinjected_savings_,
4114 using_incoming_reinjected_saving_);
4115 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
4116 &outgoing_reinjected_savings_,
4117 !using_incoming_reinjected_saving_);
4118 incoming_new_reinjected_savings_ =
nullptr;
4119 outgoing_new_reinjected_savings_ =
nullptr;
4122 void UpdateGivenReinjectedSavings(
4123 std::deque<SavingAndArc>* new_reinjected_savings,
4124 std::deque<SavingAndArc>** reinjected_savings,
4125 bool using_reinjected_savings) {
4126 if (new_reinjected_savings ==
nullptr) {
4128 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
4129 CHECK(!(*reinjected_savings)->empty());
4130 (*reinjected_savings)->pop_front();
4131 if ((*reinjected_savings)->empty()) {
4132 *reinjected_savings =
nullptr;
4141 if (*reinjected_savings !=
nullptr) {
4142 (*reinjected_savings)->clear();
4144 *reinjected_savings =
nullptr;
4145 if (!new_reinjected_savings->empty()) {
4146 *reinjected_savings = new_reinjected_savings;
4150 bool HasReinjectedSavings() {
4151 return outgoing_reinjected_savings_ !=
nullptr ||
4152 incoming_reinjected_savings_ !=
nullptr;
4155 void UpdateArcIndicesCostsAndSavings(
4156 int64_t before_node, int64_t after_node,
4157 const std::pair<int64_t, Saving>& cost_and_saving) {
4158 if (single_vehicle_type_) {
4161 absl::flat_hash_map<int, int>& arc_indices =
4162 arc_indices_per_before_node_[before_node];
4163 const auto& arc_inserted = arc_indices.insert(
4164 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
4165 const int index = arc_inserted.first->second;
4166 if (arc_inserted.second) {
4167 costs_and_savings_per_arc_.push_back({cost_and_saving});
4169 DCHECK_LT(index, costs_and_savings_per_arc_.size());
4170 costs_and_savings_per_arc_[index].push_back(cost_and_saving);
4174 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
4175 Saving* next_saving) {
4176 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
4177 costs_and_savings_per_arc_[arc_index];
4179 bool found_saving =
false;
4180 while (!costs_and_savings.empty() && !found_saving) {
4181 const Saving& saving = costs_and_savings.back().second;
4182 if (type == -1 || saving.vehicle_type == type) {
4183 *next_saving = saving;
4184 found_saving =
true;
4186 costs_and_savings.pop_back();
4188 return found_saving;
4192 int64_t index_in_sorted_savings_;
4193 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
4194 std::vector<SavingAndArc> sorted_savings_;
4195 std::vector<SavingAndArc> next_savings_;
4196 std::vector<std::pair< int,
int>>
4197 next_saving_type_and_index_for_arc_;
4198 SavingAndArc current_saving_;
4199 std::vector<std::vector<std::pair< int64_t, Saving>>>
4200 costs_and_savings_per_arc_;
4201 std::vector<absl::flat_hash_map< int,
int>>
4202 arc_indices_per_before_node_;
4203 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
4204 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
4205 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
4206 std::deque<SavingAndArc>* incoming_reinjected_savings_;
4207 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
4208 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
4209 const int vehicle_types_;
4210 const bool single_vehicle_type_;
4211 bool using_incoming_reinjected_saving_;
4223 savings_params_(
std::move(parameters)) {
4224 DCHECK_GT(savings_params_.neighbors_ratio(), 0);
4225 DCHECK_LE(savings_params_.neighbors_ratio(), 1);
4226 DCHECK_GT(savings_params_.max_memory_usage_bytes(), 0);
4227 DCHECK_GT(savings_params_.arc_coefficient(), 0);
4235 model()->GetVehicleTypeContainer());
4240 if (!ComputeSavings())
return false;
4246 if (!
Evaluate(
true).has_value())
return false;
4252 int type, int64_t before_node, int64_t after_node) {
4253 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
4260 SetNext(
model()->Start(vehicle), before_node, vehicle);
4261 SetNext(before_node, after_node, vehicle);
4267 ->GetCompatibleVehicleOfType(
4268 type, vehicle_is_compatible,
4269 [](
int) {
return false; })
4273void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
4274 std::vector<std::vector<int64_t>>* adjacency_lists) {
4275 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
4276 for (int64_t neighbor : (*adjacency_lists)[node]) {
4277 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
4280 (*adjacency_lists)[neighbor].push_back(node);
4283 absl::c_transform(*adjacency_lists, adjacency_lists->begin(),
4284 [](std::vector<int64_t> vec) {
4286 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
4302bool SavingsFilteredHeuristic::ComputeSavings() {
4306 std::vector<int64_t> uncontained_non_start_end_nodes;
4307 uncontained_non_start_end_nodes.reserve(size);
4308 for (
int node = 0; node < size; node++) {
4310 uncontained_non_start_end_nodes.push_back(node);
4314 const int64_t saving_neighbors =
4315 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
4316 static_cast<int64_t
>(uncontained_non_start_end_nodes.size()));
4319 std::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
4322 std::vector<std::vector<int64_t>> adjacency_lists(size);
4324 for (
int type = 0; type < num_vehicle_types; ++type) {
4327 if (vehicle < 0)
continue;
4329 const int64_t cost_class =
4331 const int64_t start =
model()->
Start(vehicle);
4337 for (
int before_node : uncontained_non_start_end_nodes) {
4338 std::vector<std::pair< int64_t, int64_t>>
4340 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
4342 for (
int after_node : uncontained_non_start_end_nodes) {
4343 if (after_node != before_node) {
4344 costed_after_nodes.push_back(std::make_pair(
4345 model()->GetArcCostForClass(before_node, after_node, cost_class),
4349 if (saving_neighbors < costed_after_nodes.size()) {
4350 std::nth_element(costed_after_nodes.begin(),
4351 costed_after_nodes.begin() + saving_neighbors,
4352 costed_after_nodes.end());
4353 costed_after_nodes.resize(saving_neighbors);
4355 adjacency_lists[before_node].resize(costed_after_nodes.size());
4356 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
4357 adjacency_lists[before_node].begin(),
4358 [](std::pair<int64_t, int64_t> cost_and_node) {
4359 return cost_and_node.second;
4362 if (savings_params_.add_reverse_arcs()) {
4363 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
4368 for (
int before_node : uncontained_non_start_end_nodes) {
4369 const int64_t before_to_end_cost =
4371 const int64_t start_to_before_cost =
4372 CapSub(
model()->GetArcCostForClass(start, before_node, cost_class),
4375 for (int64_t after_node : adjacency_lists[before_node]) {
4376 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
4377 before_node == after_node ||
Contains(after_node)) {
4380 const int64_t arc_cost =
4382 const int64_t start_to_after_cost =
4383 CapSub(
model()->GetArcCostForClass(start, after_node, cost_class),
4385 const int64_t after_to_end_cost =
4388 const double weighted_arc_cost_fp =
4389 savings_params_.arc_coefficient() * arc_cost;
4390 const int64_t weighted_arc_cost =
4391 weighted_arc_cost_fp < std::numeric_limits<int64_t>::max()
4392 ?
static_cast<int64_t
>(weighted_arc_cost_fp)
4393 : std::numeric_limits<int64_t>::max();
4394 const int64_t saving_value =
CapSub(
4395 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
4398 BuildSaving(-saving_value, type, before_node, after_node);
4400 const int64_t total_cost =
4401 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
4412int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
4413 int num_vehicle_types)
const {
4416 const int64_t num_neighbors_with_ratio =
4417 std::max(1.0, size * savings_params_.neighbors_ratio());
4423 const double max_memory_usage_in_savings_unit =
4424 savings_params_.max_memory_usage_bytes() / 16;
4441 if (num_vehicle_types > 1) {
4442 multiplicative_factor += 1.5;
4444 const double num_savings =
4445 max_memory_usage_in_savings_unit / multiplicative_factor;
4446 const int64_t num_neighbors_with_memory_restriction =
4447 std::max(1.0, num_savings / (num_vehicle_types * size));
4449 return std::min(num_neighbors_with_ratio,
4450 num_neighbors_with_memory_restriction);
4457 DCHECK_GT(vehicle_types, 0);
4461 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
4462 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
4463 for (
int type = 0; type < vehicle_types; type++) {
4464 const int vehicle_type_offset = type * size;
4465 const std::vector<Saving>& sorted_savings_for_type =
4467 for (
const Saving& saving : sorted_savings_for_type) {
4468 DCHECK_EQ(saving.vehicle_type, type);
4469 const int before_node = saving.before_node;
4470 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
4471 const int after_node = saving.after_node;
4472 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
4481 int before_node = saving.before_node;
4482 int after_node = saving.after_node;
4483 const bool nodes_contained =
Contains(before_node) ||
Contains(after_node);
4485 if (nodes_contained) {
4491 const int type = saving.vehicle_type;
4499 const int64_t start =
model()->
Start(vehicle);
4504 const int saving_offset = type * size;
4506 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
4507 out_index < out_savings_ptr[saving_offset + before_node].size()) {
4510 int before_before_node = -1;
4511 int after_after_node = -1;
4512 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
4513 const Saving& in_saving =
4514 *(in_savings_ptr[saving_offset + after_node][in_index]);
4515 if (out_index < out_savings_ptr[saving_offset + before_node].size()) {
4516 const Saving& out_saving =
4517 *(out_savings_ptr[saving_offset + before_node][out_index]);
4518 if (in_saving.saving < out_saving.saving) {
4519 after_after_node = in_saving.after_node;
4521 before_before_node = out_saving.before_node;
4524 after_after_node = in_saving.after_node;
4527 before_before_node =
4528 out_savings_ptr[saving_offset + before_node][out_index]
4532 if (after_after_node != -1) {
4533 DCHECK_EQ(before_before_node, -1);
4537 SetNext(after_node, after_after_node, vehicle);
4541 after_node = after_after_node;
4546 CHECK_GE(before_before_node, 0);
4548 if (!
Contains(before_before_node)) {
4549 SetNext(start, before_before_node, vehicle);
4550 SetNext(before_before_node, before_node, vehicle);
4553 before_node = before_before_node;
4570 first_node_on_route_.resize(vehicles, -1);
4571 last_node_on_route_.resize(vehicles, -1);
4572 vehicle_of_first_or_last_node_.resize(size, -1);
4574 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
4575 const int64_t start =
model()->
Start(vehicle);
4580 int64_t node =
Value(start);
4582 vehicle_of_first_or_last_node_[node] = vehicle;
4583 first_node_on_route_[vehicle] = node;
4585 int64_t next =
Value(node);
4586 while (next !=
end) {
4590 vehicle_of_first_or_last_node_[node] = vehicle;
4591 last_node_on_route_[vehicle] = node;
4598 const int64_t before_node = saving.before_node;
4599 const int64_t after_node = saving.after_node;
4600 const int type = saving.vehicle_type;
4604 bool committed =
false;
4612 vehicle_of_first_or_last_node_[before_node] = vehicle;
4613 vehicle_of_first_or_last_node_[after_node] = vehicle;
4614 first_node_on_route_[vehicle] = before_node;
4615 last_node_on_route_[vehicle] = after_node;
4627 const int v1 = vehicle_of_first_or_last_node_[before_node];
4628 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
4630 const int v2 = vehicle_of_first_or_last_node_[after_node];
4631 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
4633 if (before_node == last_node && after_node == first_node && v1 != v2 &&
4635 CHECK_EQ(
Value(before_node),
model()->End(v1));
4636 CHECK_EQ(
Value(
model()->Start(v2)), after_node);
4642 MergeRoutes(v1, v2, before_node, after_node);
4647 const int vehicle = vehicle_of_first_or_last_node_[before_node];
4648 const int64_t last_node =
4649 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
4651 if (before_node == last_node) {
4656 if (type != route_type) {
4664 SetNext(before_node, after_node, vehicle);
4667 if (first_node_on_route_[vehicle] != before_node) {
4669 DCHECK_NE(
Value(
model()->Start(vehicle)), before_node);
4670 vehicle_of_first_or_last_node_[before_node] = -1;
4672 vehicle_of_first_or_last_node_[after_node] = vehicle;
4673 last_node_on_route_[vehicle] = after_node;
4680 const int vehicle = vehicle_of_first_or_last_node_[after_node];
4681 const int64_t first_node =
4682 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
4684 if (after_node == first_node) {
4685 const int64_t start =
model()->
Start(vehicle);
4686 CHECK_EQ(
Value(start), after_node);
4689 if (type != route_type) {
4697 SetNext(before_node, after_node, vehicle);
4698 SetNext(start, before_node, vehicle);
4700 if (last_node_on_route_[vehicle] != after_node) {
4702 DCHECK_NE(
Value(after_node),
model()->End(vehicle));
4703 vehicle_of_first_or_last_node_[after_node] = -1;
4705 vehicle_of_first_or_last_node_[before_node] = vehicle;
4706 first_node_on_route_[vehicle] = before_node;
4715void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
4717 int64_t before_node,
4718 int64_t after_node) {
4720 const int64_t new_first_node = first_node_on_route_[first_vehicle];
4721 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
4722 CHECK_EQ(
Value(
model()->Start(first_vehicle)), new_first_node);
4723 const int64_t new_last_node = last_node_on_route_[second_vehicle];
4724 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
4725 CHECK_EQ(
Value(new_last_node),
model()->End(second_vehicle));
4728 int used_vehicle = first_vehicle;
4729 int unused_vehicle = second_vehicle;
4730 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
4731 model()->GetFixedCostOfVehicle(second_vehicle)) {
4732 used_vehicle = second_vehicle;
4733 unused_vehicle = first_vehicle;
4736 SetNext(before_node, after_node, used_vehicle);
4739 if (used_vehicle == first_vehicle) {
4740 SetNext(new_last_node,
model()->End(used_vehicle), used_vehicle);
4742 SetNext(
model()->Start(used_vehicle), new_first_node, used_vehicle);
4744 bool committed =
Evaluate(
true).has_value();
4746 model()->GetVehicleClassIndexOfVehicle(first_vehicle).value() !=
4747 model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) {
4749 std::swap(used_vehicle, unused_vehicle);
4750 SetNext(before_node, after_node, used_vehicle);
4753 if (used_vehicle == first_vehicle) {
4754 SetNext(new_last_node,
model()->End(used_vehicle), used_vehicle);
4756 SetNext(
model()->Start(used_vehicle), new_first_node, used_vehicle);
4758 committed =
Evaluate(
true).has_value();
4764 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).value(),
4765 model()->GetFixedCostOfVehicle(unused_vehicle));
4768 first_node_on_route_[unused_vehicle] = -1;
4769 last_node_on_route_[unused_vehicle] = -1;
4770 vehicle_of_first_or_last_node_[before_node] = -1;
4771 vehicle_of_first_or_last_node_[after_node] = -1;
4772 first_node_on_route_[used_vehicle] = new_first_node;
4773 last_node_on_route_[used_vehicle] = new_last_node;
4774 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
4775 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
4785 use_minimum_matching_(use_minimum_matching) {}
4795 std::vector<int> indices(1, 0);
4796 for (
int i = 1; i < size; ++i) {
4797 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
4798 indices.push_back(i);
4802 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
4803 std::vector<bool> class_covered(num_cost_classes,
false);
4804 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
4805 const int64_t cost_class =
4807 if (!class_covered[cost_class]) {
4808 class_covered[cost_class] =
true;
4809 const int64_t start =
model()->
Start(vehicle);
4811 auto cost = [
this, &indices, start,
end, cost_class](
int from,
int to) {
4812 DCHECK_LT(from, indices.size());
4813 DCHECK_LT(
to, indices.size());
4814 const int from_index = (from == 0) ? start : indices[from];
4815 const int to_index = (
to == 0) ?
end : indices[
to];
4816 const int64_t cost =
4822 return std::min(cost, std::numeric_limits<int64_t>::max() / 2);
4824 using Cost =
decltype(cost);
4826 indices.size(), cost);
4827 if (use_minimum_matching_) {
4830 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
4832 if (christofides_solver.
Solve().ok()) {
4833 path_per_cost_class[cost_class] =
4839 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
4840 const int64_t cost_class =
4842 const std::vector<int>& path = path_per_cost_class[cost_class];
4843 if (path.empty())
continue;
4844 DCHECK_EQ(0, path[0]);
4845 DCHECK_EQ(0, path.back());
4849 for (
int i = 1; i < path.size() - 1 && prev !=
end; ++i) {
4851 int next = indices[path[i]];
4870 SweepIndex(
const int64_t index,
const double angle,
const double distance)
4871 : index(index), angle(angle), distance(distance) {}
4872 ~SweepIndex() =
default;
4879struct SweepIndexSortAngle {
4880 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
4881 return (node1.angle < node2.angle);
4883} SweepIndexAngleComparator;
4885struct SweepIndexSortDistance {
4886 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
4887 return (node1.distance < node2.distance);
4889} SweepIndexDistanceComparator;
4893 absl::Span<
const std::pair<int64_t, int64_t>> points)
4894 : coordinates_(2 * points.size(), 0), sectors_(1) {
4895 for (int64_t i = 0; i < points.size(); ++i) {
4896 coordinates_[2 * i] = points[i].first;
4897 coordinates_[2 * i + 1] = points[i].second;
4904 const double pi_rad = 3.14159265;
4906 const int x0 = coordinates_[0];
4907 const int y0 = coordinates_[1];
4909 std::vector<SweepIndex> sweep_indices;
4910 for (int64_t index = 0; index < static_cast<int>(coordinates_.size()) / 2;
4912 const int x = coordinates_[2 * index];
4913 const int y = coordinates_[2 * index + 1];
4914 const double x_delta = x - x0;
4915 const double y_delta = y - y0;
4916 double square_distance = x_delta * x_delta + y_delta * y_delta;
4917 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
4918 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
4919 sweep_indices.push_back(SweepIndex(index, angle, square_distance));
4921 absl::c_sort(sweep_indices, SweepIndexDistanceComparator);
4923 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
4924 for (
int sector = 0; sector < sectors_; ++sector) {
4925 std::vector<SweepIndex> cluster;
4926 std::vector<SweepIndex>::iterator
begin =
4927 sweep_indices.begin() + sector * size;
4928 std::vector<SweepIndex>::iterator
end =
4929 sector == sectors_ - 1 ? sweep_indices.end()
4930 : sweep_indices.begin() + (sector + 1) * size;
4931 std::sort(
begin,
end, SweepIndexAngleComparator);
4933 for (
const SweepIndex& sweep_index : sweep_indices) {
4934 indices->push_back(sweep_index.index);
4941 Link(std::pair<int, int> link,
double value,
int vehicle_class,
4942 int64_t start_depot, int64_t end_depot)
4945 vehicle_class(vehicle_class),
4946 start_depot(start_depot),
4947 end_depot(end_depot) {}
4950 std::pair<int, int> link;
4953 int64_t start_depot;
4962class RouteConstructor {
4964 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
4965 bool check_assignment, int64_t num_indices,
4966 const std::vector<Link>& links_list)
4967 : assignment_(assignment),
4969 check_assignment_(check_assignment),
4970 solver_(model_->solver()),
4971 num_indices_(num_indices),
4972 links_list_(links_list),
4973 nexts_(model_->Nexts()),
4974 in_route_(num_indices_, -1),
4976 index_to_chain_index_(num_indices, -1),
4977 index_to_vehicle_class_index_(num_indices, -1) {
4979 const std::vector<std::string> dimension_names =
4980 model_->GetAllDimensionNames();
4981 dimensions_.assign(dimension_names.size(),
nullptr);
4982 for (
int i = 0;
i < dimension_names.size(); ++
i) {
4983 dimensions_[
i] = &model_->GetDimensionOrDie(dimension_names[i]);
4986 cumuls_.resize(dimensions_.size());
4987 for (std::vector<int64_t>& cumuls : cumuls_) {
4988 cumuls.resize(num_indices_);
4990 new_possible_cumuls_.resize(dimensions_.size());
4993 ~RouteConstructor() =
default;
4996 model_->solver()->TopPeriodicCheck();
4998 for (
int index = 0; index < num_indices_; ++index) {
4999 if (!model_->IsStart(index) && !model_->IsEnd(index)) {
5000 routes_.push_back({index});
5001 in_route_[index] = routes_.size() - 1;
5005 for (
const Link& link : links_list_) {
5006 model_->solver()->TopPeriodicCheck();
5007 const int index1 = link.link.first;
5008 const int index2 = link.link.second;
5009 const int vehicle_class = link.vehicle_class;
5010 const int64_t start_depot = link.start_depot;
5011 const int64_t end_depot = link.end_depot;
5014 if (index_to_vehicle_class_index_[index1] < 0) {
5015 for (
int dimension_index = 0; dimension_index < dimensions_.size();
5016 ++dimension_index) {
5017 cumuls_[dimension_index][index1] =
5018 std::max(dimensions_[dimension_index]->GetTransitValue(
5019 start_depot, index1, 0),
5020 dimensions_[dimension_index]->CumulVar(index1)->Min());
5023 if (index_to_vehicle_class_index_[index2] < 0) {
5024 for (
int dimension_index = 0; dimension_index < dimensions_.size();
5025 ++dimension_index) {
5026 cumuls_[dimension_index][index2] =
5027 std::max(dimensions_[dimension_index]->GetTransitValue(
5028 start_depot, index2, 0),
5029 dimensions_[dimension_index]->CumulVar(index2)->Min());
5033 const int route_index1 = in_route_[index1];
5034 const int route_index2 = in_route_[index2];
5036 route_index1 >= 0 && route_index2 >= 0 &&
5037 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
5038 index2, route_index1, route_index2, vehicle_class,
5039 start_depot, end_depot);
5040 if (Merge(merge, route_index1, route_index2)) {
5041 index_to_vehicle_class_index_[index1] = vehicle_class;
5042 index_to_vehicle_class_index_[index2] = vehicle_class;
5046 model_->solver()->TopPeriodicCheck();
5050 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
5051 if (!deleted_chains_.contains(chain_index)) {
5052 final_chains_.push_back(chains_[chain_index]);
5055 absl::c_sort(final_chains_, ChainComparator);
5056 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
5057 if (!deleted_routes_.contains(route_index)) {
5058 final_routes_.push_back(routes_[route_index]);
5061 absl::c_sort(final_routes_, RouteComparator);
5063 const int extra_vehicles = std::max(
5064 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
5066 int chain_index = 0;
5067 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
5069 if (chain_index - extra_vehicles >= model_->vehicles()) {
5072 const int start = final_chains_[chain_index].head;
5073 const int end = final_chains_[chain_index].tail;
5075 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
5076 assignment_->SetValue(
5077 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
5078 assignment_->Add(nexts_[
end]);
5079 assignment_->SetValue(nexts_[
end],
5080 model_->End(chain_index - extra_vehicles));
5084 for (
int route_index = 0; route_index < final_routes_.size();
5086 if (chain_index - extra_vehicles >= model_->vehicles()) {
5089 DCHECK_LT(route_index, final_routes_.size());
5090 const int head = final_routes_[route_index].front();
5091 const int tail = final_routes_[route_index].back();
5092 if (head == tail && head < model_->Size()) {
5094 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
5095 assignment_->SetValue(
5096 model_->NextVar(model_->Start(chain_index - extra_vehicles)), head);
5097 assignment_->Add(nexts_[tail]);
5098 assignment_->SetValue(nexts_[tail],
5099 model_->End(chain_index - extra_vehicles));
5105 for (
int index = 0; index < model_->Size(); ++index) {
5106 IntVar*
const next = nexts_[index];
5107 if (!assignment_->Contains(next)) {
5108 assignment_->Add(next);
5109 if (next->Contains(index)) {
5110 assignment_->SetValue(next, index);
5117 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
5120 bool operator()(absl::Span<const int> route1,
5121 absl::Span<const int> route2)
const {
5122 return (route1.size() < route2.size());
5133 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
5134 return (chain1.nodes < chain2.nodes);
5138 bool Head(
int node)
const {
5139 return (node == routes_[in_route_[node]].front());
5142 bool Tail(
int node)
const {
5143 return (node == routes_[in_route_[node]].back());
5146 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
5147 int dimension_index) {
5148 const RoutingDimension& dimension = *dimensions_[dimension_index];
5149 std::vector<int>::const_iterator it = route.begin();
5150 int64_t cumul = route_cumul;
5151 while (it != route.end()) {
5152 const int previous = *it;
5153 const int64_t cumul_previous = cumul;
5157 if (it == route.end()) {
5160 const int next = *it;
5161 int64_t available_from_previous =
5162 cumul_previous + dimension.GetTransitValue(previous, next, 0);
5163 int64_t available_cumul_next =
5164 std::max(cumuls_[dimension_index][next], available_from_previous);
5166 const int64_t slack = available_cumul_next - available_from_previous;
5167 if (slack > dimension.SlackVar(previous)->Max()) {
5168 available_cumul_next =
5169 available_from_previous + dimension.SlackVar(previous)->Max();
5172 if (available_cumul_next > dimension.CumulVar(next)->Max()) {
5175 if (available_cumul_next <= cumuls_[dimension_index][next]) {
5178 cumul = available_cumul_next;
5183 bool CheckRouteConnection(absl::Span<const int> route1,
5184 const std::vector<int>& route2,
int dimension_index,
5185 int64_t , int64_t end_depot) {
5186 const int tail1 = route1.back();
5187 const int head2 = route2.front();
5188 const int tail2 = route2.back();
5189 const RoutingDimension& dimension = *dimensions_[dimension_index];
5190 int non_depot_node = -1;
5191 for (
int node = 0; node < num_indices_; ++node) {
5192 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
5193 non_depot_node = node;
5197 CHECK_GE(non_depot_node, 0);
5198 const int64_t depot_threshold =
5199 std::max(dimension.SlackVar(non_depot_node)->Max(),
5200 dimension.CumulVar(non_depot_node)->Max());
5202 int64_t available_from_tail1 = cumuls_[dimension_index][tail1] +
5203 dimension.GetTransitValue(tail1, head2, 0);
5204 int64_t new_available_cumul_head2 =
5205 std::max(cumuls_[dimension_index][head2], available_from_tail1);
5207 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
5208 if (slack > dimension.SlackVar(tail1)->Max()) {
5209 new_available_cumul_head2 =
5210 available_from_tail1 + dimension.SlackVar(tail1)->Max();
5213 bool feasible_route =
true;
5214 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
5217 if (new_available_cumul_head2 <= cumuls_[dimension_index][head2]) {
5222 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
5223 const int64_t new_possible_cumul_tail2 =
5224 new_possible_cumuls_[dimension_index].contains(tail2)
5225 ? new_possible_cumuls_[dimension_index][tail2]
5226 : cumuls_[dimension_index][tail2];
5228 if (!feasible_route || (new_possible_cumul_tail2 +
5229 dimension.GetTransitValue(tail2, end_depot, 0) >
5236 bool FeasibleMerge(absl::Span<const int> route1,
5237 const std::vector<int>& route2,
int node1,
int node2,
5238 int route_index1,
int route_index2,
int vehicle_class,
5239 int64_t start_depot, int64_t end_depot) {
5240 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
5245 if (!((index_to_vehicle_class_index_[node1] == -1 &&
5246 index_to_vehicle_class_index_[node2] == -1) ||
5247 (index_to_vehicle_class_index_[node1] == vehicle_class &&
5248 index_to_vehicle_class_index_[node2] == -1) ||
5249 (index_to_vehicle_class_index_[node1] == -1 &&
5250 index_to_vehicle_class_index_[node2] == vehicle_class) ||
5251 (index_to_vehicle_class_index_[node1] == vehicle_class &&
5252 index_to_vehicle_class_index_[node2] == vehicle_class))) {
5258 for (
int dimension_index = 0; dimension_index < dimensions_.size();
5259 ++dimension_index) {
5260 new_possible_cumuls_[dimension_index].clear();
5261 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
5262 start_depot, end_depot);
5270 bool CheckTempAssignment(Assignment*
const temp_assignment,
5271 int new_chain_index,
int old_chain_index,
int head1,
5272 int tail1,
int head2,
int tail2) {
5275 if (new_chain_index >= model_->vehicles())
return false;
5276 const int start = head1;
5277 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
5278 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
5280 temp_assignment->Add(nexts_[tail1]);
5281 temp_assignment->SetValue(nexts_[tail1], head2);
5282 temp_assignment->Add(nexts_[tail2]);
5283 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
5284 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
5285 if ((chain_index != new_chain_index) &&
5286 (chain_index != old_chain_index) &&
5287 (!deleted_chains_.contains(chain_index))) {
5288 const int start = chains_[chain_index].head;
5289 const int end = chains_[chain_index].tail;
5290 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
5291 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
5293 temp_assignment->Add(nexts_[
end]);
5294 temp_assignment->SetValue(nexts_[
end], model_->End(chain_index));
5297 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
5300 bool UpdateAssignment(absl::Span<const int> route1,
5301 absl::Span<const int> route2) {
5302 bool feasible =
true;
5303 const int head1 = route1.front();
5304 const int tail1 = route1.back();
5305 const int head2 = route2.front();
5306 const int tail2 = route2.back();
5307 const int chain_index1 = index_to_chain_index_[head1];
5308 const int chain_index2 = index_to_chain_index_[head2];
5309 if (chain_index1 < 0 && chain_index2 < 0) {
5310 const int chain_index = chains_.size();
5311 if (check_assignment_) {
5312 Assignment*
const temp_assignment =
5313 solver_->MakeAssignment(assignment_);
5314 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
5315 tail1, head2, tail2);
5322 index_to_chain_index_[head1] = chain_index;
5323 index_to_chain_index_[tail2] = chain_index;
5324 chains_.push_back(chain);
5326 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
5327 if (check_assignment_) {
5328 Assignment*
const temp_assignment =
5329 solver_->MakeAssignment(assignment_);
5331 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
5332 head1, tail1, head2, tail2);
5335 index_to_chain_index_[tail2] = chain_index1;
5336 chains_[chain_index1].head = head1;
5337 chains_[chain_index1].tail = tail2;
5338 ++chains_[chain_index1].nodes;
5340 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
5341 if (check_assignment_) {
5342 Assignment*
const temp_assignment =
5343 solver_->MakeAssignment(assignment_);
5345 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
5346 head1, tail1, head2, tail2);
5349 index_to_chain_index_[head1] = chain_index2;
5350 chains_[chain_index2].head = head1;
5351 chains_[chain_index2].tail = tail2;
5352 ++chains_[chain_index2].nodes;
5355 if (check_assignment_) {
5356 Assignment*
const temp_assignment =
5357 solver_->MakeAssignment(assignment_);
5359 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
5360 head1, tail1, head2, tail2);
5363 index_to_chain_index_[tail2] = chain_index1;
5364 chains_[chain_index1].head = head1;
5365 chains_[chain_index1].tail = tail2;
5366 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
5367 deleted_chains_.insert(chain_index2);
5371 assignment_->Add(nexts_[tail1]);
5372 assignment_->SetValue(nexts_[tail1], head2);
5377 bool Merge(
bool merge,
int index1,
int index2) {
5379 if (UpdateAssignment(routes_[index1], routes_[index2])) {
5381 for (
const int node : routes_[index2]) {
5382 in_route_[node] = index1;
5383 routes_[index1].push_back(node);
5385 for (
int dimension_index = 0; dimension_index < dimensions_.size();
5386 ++dimension_index) {
5387 for (
const std::pair<int, int64_t> new_possible_cumul :
5388 new_possible_cumuls_[dimension_index]) {
5389 cumuls_[dimension_index][new_possible_cumul.first] =
5390 new_possible_cumul.second;
5393 deleted_routes_.insert(index2);
5400 Assignment*
const assignment_;
5401 RoutingModel*
const model_;
5402 const bool check_assignment_;
5403 Solver*
const solver_;
5404 const int64_t num_indices_;
5405 const std::vector<Link> links_list_;
5406 std::vector<IntVar*> nexts_;
5407 std::vector<const RoutingDimension*> dimensions_;
5408 std::vector<std::vector<int64_t>> cumuls_;
5409 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
5410 std::vector<std::vector<int>> routes_;
5411 std::vector<int> in_route_;
5412 absl::flat_hash_set<int> deleted_routes_;
5413 std::vector<std::vector<int>> final_routes_;
5414 std::vector<Chain> chains_;
5415 absl::flat_hash_set<int> deleted_chains_;
5416 std::vector<Chain> final_chains_;
5417 std::vector<int> index_to_chain_index_;
5418 std::vector<int> index_to_vehicle_class_index_;
5426 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
5427 : model_(model), check_assignment_(check_assignment) {}
5428 ~SweepBuilder()
override =
default;
5430 Decision*
Next(Solver*
const solver)
override {
5435 Assignment*
const assignment = solver->MakeAssignment();
5436 route_constructor_ = std::make_unique<RouteConstructor>(
5437 assignment, model_, check_assignment_, num_indices_, links_);
5439 route_constructor_->Construct();
5440 route_constructor_.reset(
nullptr);
5442 assignment->Restore();
5449 const int depot = model_->GetDepot();
5450 num_indices_ = model_->Size() + model_->vehicles();
5451 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
5452 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
5453 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
5455 std::vector<int64_t> indices;
5456 model_->sweep_arranger()->ArrangeIndices(&indices);
5457 for (
int i = 0;
i < indices.size() - 1; ++
i) {
5458 const int64_t first = indices[
i];
5459 const int64_t second = indices[
i + 1];
5460 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
5461 (model_->IsStart(second) || !model_->IsEnd(second))) {
5462 if (first != depot && second != depot) {
5464 Link(std::make_pair(first, second), 0, 0, depot, depot));
5470 RoutingModel*
const model_;
5471 std::unique_ptr<RouteConstructor> route_constructor_;
5472 const bool check_assignment_;
5473 int64_t num_indices_;
5474 std::vector<Link> links_;
5479 bool check_assignment) {
5480 return model->
solver()->
RevAlloc(
new SweepBuilder(model, check_assignment));
5489class AllUnperformed :
public DecisionBuilder {
5492 explicit AllUnperformed(RoutingModel*
const model) : model_(model) {}
5493 ~AllUnperformed()
override =
default;
5494 Decision*
Next(Solver*
const )
override {
5497 model_->CostVar()->FreezeQueue();
5498 for (
int i = 0;
i < model_->Size(); ++
i) {
5499 if (!model_->IsStart(i)) {
5500 model_->ActiveVar(i)->SetValue(0);
5503 model_->CostVar()->UnfreezeQueue();
5508 RoutingModel*
const model_;
5518class GuidedSlackFinalizer :
public DecisionBuilder {
5520 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel* model,
5521 std::function<int64_t(int64_t)> initializer);
5524 GuidedSlackFinalizer(
const GuidedSlackFinalizer&) =
delete;
5525 GuidedSlackFinalizer& operator=(
const GuidedSlackFinalizer&) =
delete;
5527 Decision*
Next(Solver* solver)
override;
5530 int64_t SelectValue(int64_t index);
5531 int64_t ChooseVariable();
5533 const RoutingDimension*
const dimension_;
5534 RoutingModel*
const model_;
5535 const std::function<int64_t(int64_t)> initializer_;
5536 RevArray<bool> is_initialized_;
5537 std::vector<int64_t> initial_values_;
5538 Rev<int64_t> current_index_;
5539 Rev<int64_t> current_route_;
5540 RevArray<int64_t> last_delta_used_;
5543GuidedSlackFinalizer::GuidedSlackFinalizer(
5545 std::function<int64_t(int64_t)> initializer)
5546 : dimension_(ABSL_DIE_IF_NULL(dimension)),
5547 model_(ABSL_DIE_IF_NULL(model)),
5548 initializer_(std::move(initializer)),
5549 is_initialized_(dimension->slacks().size(),
false),
5550 initial_values_(dimension->slacks().size(),
5551 std::numeric_limits<int64_t>::min()),
5552 current_index_(model_->Start(0)),
5554 last_delta_used_(dimension->slacks().size(), 0) {}
5556Decision* GuidedSlackFinalizer::Next(Solver* solver) {
5557 CHECK_EQ(solver, model_->solver());
5558 const int node_idx = ChooseVariable();
5559 CHECK(node_idx == -1 ||
5560 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
5561 if (node_idx != -1) {
5562 if (!is_initialized_[node_idx]) {
5563 initial_values_[node_idx] = initializer_(node_idx);
5564 is_initialized_.SetValue(solver, node_idx,
true);
5566 const int64_t value = SelectValue(node_idx);
5567 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
5568 return solver->MakeAssignVariableValue(slack_variable, value);
5573int64_t GuidedSlackFinalizer::SelectValue(int64_t index) {
5574 const IntVar*
const slack_variable = dimension_->SlackVar(index);
5575 const int64_t center = initial_values_[index];
5576 const int64_t max_delta =
5577 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
5579 int64_t delta = last_delta_used_[index];
5583 while (std::abs(delta) < max_delta &&
5584 !slack_variable->Contains(center + delta)) {
5591 last_delta_used_.SetValue(model_->solver(), index, delta);
5592 return center + delta;
5595int64_t GuidedSlackFinalizer::ChooseVariable() {
5596 int64_t int_current_node = current_index_.Value();
5597 int64_t int_current_route = current_route_.Value();
5599 while (int_current_route < model_->vehicles()) {
5600 while (!model_->IsEnd(int_current_node) &&
5601 dimension_->SlackVar(int_current_node)->Bound()) {
5602 int_current_node = model_->NextVar(int_current_node)->Value();
5604 if (!model_->IsEnd(int_current_node)) {
5607 int_current_route += 1;
5608 if (int_current_route < model_->vehicles()) {
5609 int_current_node = model_->Start(int_current_route);
5613 CHECK(int_current_route == model_->vehicles() ||
5614 !dimension_->SlackVar(int_current_node)->Bound());
5615 current_index_.SetValue(model_->solver(), int_current_node);
5616 current_route_.SetValue(model_->solver(), int_current_route);
5617 if (int_current_route < model_->vehicles()) {
5618 return int_current_node;
5627 std::function<int64_t(int64_t)> initializer) {
5628 return solver_->RevAlloc(
5629 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
5633 CHECK_EQ(base_dimension_,
this);
5634 CHECK(!model_->IsEnd(node));
5638 const int64_t next = model_->NextVar(node)->Value();
5639 if (model_->IsEnd(next)) {
5642 const int64_t next_next = model_->NextVar(next)->Value();
5643 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
5644 CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value());
5646 model_->StateDependentTransitCallback(
5647 state_dependent_class_evaluators_
5648 [state_dependent_vehicle_to_class_[serving_vehicle]])(next,
5651 const int64_t next_cumul_min =
CumulVar(next)->Min();
5652 const int64_t next_cumul_max =
CumulVar(next)->Max();
5653 const int64_t optimal_next_cumul =
5655 next_cumul_min, next_cumul_max + 1);
5657 DCHECK_LE(next_cumul_min, optimal_next_cumul);
5658 DCHECK_LE(optimal_next_cumul, next_cumul_max);
5663 const int64_t current_cumul =
CumulVar(node)->Value();
5664 const int64_t current_state_independent_transit = model_->TransitCallback(
5665 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node, next);
5666 const int64_t current_state_dependent_transit =
5668 ->StateDependentTransitCallback(
5669 state_dependent_class_evaluators_
5670 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
5672 .transit->Query(current_cumul);
5673 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
5674 current_state_independent_transit -
5675 current_state_dependent_transit;
5676 CHECK_LE(
SlackVar(node)->Min(), optimal_slack);
5677 CHECK_LE(optimal_slack,
SlackVar(node)->Max());
5678 return optimal_slack;
5684 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
5687 GreedyDescentLSOperator(
const GreedyDescentLSOperator&) =
delete;
5688 GreedyDescentLSOperator& operator=(
const GreedyDescentLSOperator&) =
delete;
5691 void Start(
const Assignment* assignment)
override;
5694 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
5696 const std::vector<IntVar*> variables_;
5698 int64_t current_step_;
5705 int64_t current_direction_;
5708GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
5709 : variables_(
std::move(variables)),
5712 current_direction_(0) {}
5714bool GreedyDescentLSOperator::MakeNextNeighbor(
Assignment* delta,
5716 static const int64_t sings[] = {1, -1};
5717 for (; 1 <= current_step_; current_step_ /= 2) {
5718 for (; current_direction_ < 2 * variables_.size();) {
5719 const int64_t variable_idx = current_direction_ / 2;
5720 IntVar*
const variable = variables_[variable_idx];
5721 const int64_t sign_index = current_direction_ % 2;
5722 const int64_t sign = sings[sign_index];
5723 const int64_t offset = sign * current_step_;
5724 const int64_t new_value = center_->
Value(variable) + offset;
5725 ++current_direction_;
5726 if (variable->Contains(new_value)) {
5727 delta->Add(variable);
5728 delta->SetValue(variable, new_value);
5732 current_direction_ = 0;
5737void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
5738 CHECK(assignment !=
nullptr);
5739 current_step_ = FindMaxDistanceToDomain(assignment);
5740 center_ = assignment;
5743int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
5744 const Assignment* assignment) {
5745 int64_t result = std::numeric_limits<int64_t>::min();
5746 for (
const IntVar*
const var : variables_) {
5747 result = std::max(result, std::abs(var->Max() - assignment->Value(var)));
5748 result = std::max(result, std::abs(var->Min() - assignment->Value(var)));
5755 std::vector<IntVar*> variables) {
5756 return std::unique_ptr<LocalSearchOperator>(
5757 new GreedyDescentLSOperator(std::move(variables)));
5762 CHECK(dimension !=
nullptr);
5769 solver_->MakeSolveOnce(guided_finalizer);
5770 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
5771 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
5772 start_cumuls[vehicle_idx] = dimension->
CumulVar(
Start(vehicle_idx));
5775 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
5777 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
5779 Assignment*
const first_solution = solver_->MakeAssignment();
5780 first_solution->
Add(start_cumuls);
5781 for (
IntVar*
const cumul : start_cumuls) {
5782 first_solution->
SetValue(cumul, cumul->Min());
5785 solver_->MakeLocalSearchPhase(first_solution, parameters);
bool Contains(const T *val) const
void NoteChangedPriority(T *val)
const E & Element(const V *const var) const
E * AddAtPosition(V *var, int position)
const E * ElementPtrOrNull(const V *const var) const
int64_t Value(const IntVar *var) const
void SetValue(const IntVar *var, int64_t value)
IntVarElement * Add(IntVar *var)
void Copy(const Assignment *assignment)
AssignmentContainer< IntVar, IntVarElement > IntContainer
const IntContainer & IntVarContainer() const
int64_t ObjectiveValue() const
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(absl::Span< const int > vehicles)
int64_t GetEvaluatorInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
bool HasHintedNext(int node) const
std::optional< int64_t > GetInsertionCostForPairAtPositions(int64_t pickup_to_insert, int64_t pickup_insert_after, int64_t delivery_to_insert, int64_t delivery_insert_after, int vehicle, int hint_weight=0)
std::vector< int > hint_prev_values_
bool IsHint(int node, int64_t next) const
std::vector< int > hint_next_values_
void InitializeSeedQueue(std::vector< std::vector< StartEndValue > > *start_end_distances_per_node, SeedQueue *sq)
std::vector< EvaluatorCache > evaluator_cache_
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.
int64_t GetUnperformedValue(int64_t node_to_insert) const
std::optional< int64_t > GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle, int hint_weight=0)
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor, int vehicle=-1)
bool HasHintedPrev(int node) const
std::function< int64_t(int64_t)> penalty_evaluator_
void AddSeedNodeToQueue(int node, std::vector< StartEndValue > *start_end_distances, SeedQueue *sq)
ChristofidesFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void SetMatchingAlgorithm(MatchingAlgorithm matching)
absl::StatusOr< const std::vector< NodeIndex > & > TravelingSalesmanPath()
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.
static constexpr Value PATH_MOST_CONSTRAINED_ARC
FirstSolutionStrategy_Value Value
static constexpr Value PATH_CHEAPEST_ARC
static constexpr Value PARALLEL_CHEAPEST_INSERTION
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, bool is_sequential)
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, absl::Span< const 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 * Next(Solver *solver) override
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
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)
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'.
void SynchronizeFilters()
Synchronizes filters with an assignment (the current solution).
std::optional< int64_t > Evaluate(bool commit, bool ignore_upper_bound=false, bool update_upper_bound=true)
bool IsSecondaryVar(int64_t index) const
Returns true if 'index' is a secondary variable index.
virtual int64_t Value() const =0
virtual uint64_t Size() const =0
This method returns the number of values in the domain of the variable.
void Initialize() override
Initialize the heuristic; called before starting to build a new solution.
LocalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, LocalCheapestInsertionParameters lci_params, LocalSearchFilterManager *filter_manager, bool use_first_solution_hint, BinCapacities *bin_capacities=nullptr, std::function< bool(const std::vector< RoutingModel::VariableValuePair > &, std::vector< RoutingModel::VariableValuePair > *)> optimize_on_insertion=nullptr)
Takes ownership of evaluator.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
static constexpr InsertionSortingProperty SORTING_PROPERTY_PENALTY
static constexpr PairInsertionStrategy AUTOMATIC
static constexpr PairInsertionStrategy BEST_PICKUP_DELIVERY_PAIR
LocalCheapestInsertionParameters_InsertionSortingProperty InsertionSortingProperty
static constexpr PairInsertionStrategy BEST_PICKUP_THEN_BEST_DELIVERY
bool Accept(LocalSearchMonitor *monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
The base class for all local search operators.
static IntOut SafeRound(FloatIn x)
virtual int64_t RangeMinArgument(int64_t from, int64_t to) const =0
const RoutingDimension * base_dimension() const
Returns the parent in the dependency tree if any or nullptr otherwise.
int64_t ShortestTransitionSlack(int64_t node) const
operations_research::IntVar * SlackVar(int64_t index) const
operations_research::IntVar * CumulVar(int64_t index) const
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)
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 AddUnassignedNodesToEmptyVehicles()
Adds all unassigned nodes to empty vehicles.
void MakePartiallyPerformedPairsUnperformed()
int64_t Size() const
Returns the number of next variables in the model.
CostClassIndex GetCostClassIndexOfVehicle(int64_t vehicle) const
Get the cost class index of the given vehicle.
void ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(int64_t index, int64_t max_cardinality, F f) const
std::optional< PickupDeliveryPosition > GetPickupPosition(int64_t node_index) const
Returns the pickup and delivery positions where the node is a pickup.
int64_t Start(int vehicle) const
int64_t GetArcCostForClass(int64_t from_index, int64_t to_index, int64_t cost_class_index) const
operations_research::IntVar * NextVar(int64_t index) const
int64_t End(int vehicle) const
Returns the variable index of the ending node of a vehicle route.
int GetCostClassesCount() const
Returns the number of different cost classes in the model.
const std::vector< PickupDeliveryPair > & GetPickupAndDeliveryPairs() const
Returns pickup and delivery pairs currently in the model.
VehicleClassIndex GetVehicleClassIndexOfVehicle(int64_t vehicle) const
friend class RoutingDimension
DecisionBuilder * MakeSelfDependentDimensionFinalizer(const RoutingDimension *dimension)
const NodeNeighborsByCostClass * GetOrCreateNodeNeighborsByCostClass(double neighbors_ratio, int64_t min_neighbors, double &neighbors_ratio_used, bool add_vehicle_starts_to_neighbors=true, bool add_vehicle_ends_to_neighbors=false, bool only_sort_neighbors_for_partial_neighborhoods=true)
const operations_research::Assignment * GetFirstSolutionHint() const
Returns the current hint assignment.
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
int vehicles() const
Returns the number of vehicle routes in the model.
std::optional< PickupDeliveryPosition > GetDeliveryPosition(int64_t node_index) const
Returns the pickup and delivery positions where the node is a delivery.
operations_research::IntVar * CostVar() const
Returns the global cost variable which is being minimized.
const operations_research::Assignment * SolveFromAssignmentWithParameters(const operations_research::Assignment *assignment, const RoutingSearchParameters &search_parameters, std::vector< const operations_research::Assignment * > *solutions=nullptr)
int64_t GetArcCostForVehicle(int64_t from_index, int64_t to_index, int64_t vehicle) const
static std::unique_ptr< LocalSearchOperator > MakeGreedyDescentLSOperator(std::vector< operations_research::IntVar * > variables)
int64_t GetFixedCostOfVehicle(int vehicle) const
operations_research::IntVar * VehicleVar(int64_t index) const
RoutingTransitCallback1 TransitCallback1
const std::vector< operations_research::IntVar * > & Nexts() const
DecisionBuilder * MakeGuidedSlackFinalizer(const RoutingDimension *dimension, std::function< int64_t(int64_t)> initializer)
The next few members are in the public section only for testing purposes.
::operations_research::LocalSearchMetaheuristic_Value local_search_metaheuristic() const
const ::google::protobuf::Duration & time_limit() const
void set_local_search_metaheuristic(::operations_research::LocalSearchMetaheuristic_Value value)
bool has_time_limit() const
const std::vector< Saving > & GetSortedSavingsForVehicleType(int type)
void InitializeContainer(int64_t size, int64_t saving_neighbors)
void Update(bool update_best_saving, int type=-1)
void AddNewSaving(const Saving &saving, int64_t total_cost, int64_t before_node, int64_t after_node, int vehicle_type)
SavingsContainer(const SavingsFilteredHeuristic *savings_db, int vehicle_types)
void UpdateWithType(int type)
void ReinjectSkippedSavingsEndingAt(int64_t node)
void ReinjectSkippedSavingsStartingAt(int64_t node)
virtual void BuildRoutesFromSavings()=0
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
SavingsFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, SavingsParameters parameters, LocalSearchFilterManager *filter_manager)
~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_
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
Assignment * MakeAssignment()
This method creates an empty assignment.
int64_t wall_time() const
std::function< bool(int64_t, int64_t, int64_t)> VariableValueComparator
SweepArranger(absl::Span< const 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)
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
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
std::vector< LocalCheapestInsertionParameters::InsertionSortingProperty > GetLocalCheapestInsertionSortingProperties(absl::Span< const int > lci_insertion_sorting_properties)
int64_t CapAdd(int64_t x, int64_t y)
void CapAddTo(int64_t x, int64_t *y)
FirstSolutionStrategy::Value AutomaticFirstSolutionStrategy(bool has_pickup_deliveries, bool has_node_precedences, bool has_single_vehicle_node)
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
std::vector< int64_t > ComputeVehicleEndChainStarts(const RoutingModel &model)
int64_t CapSub(int64_t x, int64_t y)
ClosedInterval::Iterator end(ClosedInterval interval)
LocalSearchMetaheuristic_Value
const Assignment * SolveFromAssignmentWithAlternativeSolvers(const Assignment *assignment, RoutingModel *primary_model, const std::vector< RoutingModel * > &alternative_models, const RoutingSearchParameters ¶meters, int max_non_improving_iterations)
const Assignment * SolveFromAssignmentWithAlternativeSolversAndParameters(const Assignment *assignment, RoutingModel *primary_model, const RoutingSearchParameters &primary_parameters, const std::vector< RoutingModel * > &alternative_models, const std::vector< RoutingSearchParameters > &alternative_parameters, int max_non_improving_iterations)
static const int kUnassigned
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
ClosedInterval::Iterator begin(ClosedInterval interval)
const Assignment * SolveWithAlternativeSolvers(RoutingModel *primary_model, const std::vector< RoutingModel * > &alternative_models, const RoutingSearchParameters ¶meters, int max_non_improving_iterations)
int64_t CapOpp(int64_t v)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
trees with all degrees equal to
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
ABSL_FLAG(bool, routing_shift_insertion_cost_by_penalty, true, "Shift insertion costs by the penalty of the inserted node(s).")
const bool prioritize_farthest_nodes
std::priority_queue< Seed, std::vector< Seed >, std::greater< Seed > > priority_queue
bool operator<(const Entry &other) const
RangeMinMaxIndexFunction * transit_plus_identity
f(x)
static const int64_t kint64max