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/strings/str_cat.h"
47#include "absl/strings/string_view.h"
48#include "absl/time/time.h"
49#include "absl/types/span.h"
60#include "ortools/constraint_solver/routing_enums.pb.h"
61#include "ortools/constraint_solver/routing_parameters.pb.h"
73ABSL_FLAG(
bool, routing_shift_insertion_cost_by_penalty,
true,
74 "Shift insertion costs by the penalty of the inserted node(s).");
77 "The number of sectors the space is divided into before it is sweeped"
83void ConvertAssignment(
const RoutingModel* src_model,
const Assignment* src,
84 const RoutingModel* dst_model, Assignment* dst) {
85 DCHECK_EQ(src_model->Nexts().size(), dst_model->Nexts().size());
86 DCHECK_NE(src,
nullptr);
87 DCHECK_EQ(src_model->solver(), src->solver());
88 DCHECK_EQ(dst_model->solver(), dst->solver());
89 for (
int i = 0; i < src_model->Nexts().size(); i++) {
90 IntVar*
const dst_next_var = dst_model->NextVar(i);
91 if (!dst->Contains(dst_next_var)) {
92 dst->Add(dst_next_var);
94 dst->SetValue(dst_next_var, src->Value(src_model->NextVar(i)));
98bool AreAssignmentsEquivalent(
const RoutingModel& model,
101 for (
IntVar* next_var : model.Nexts()) {
102 if (assignment1.Value(next_var) != assignment2.Value(next_var)) {
109absl::Duration GetTimeLimit(
const RoutingSearchParameters& search_parameters) {
110 return search_parameters.has_time_limit()
113 : absl::InfiniteDuration();
119bool UpdateTimeLimits(
Solver* solver, int64_t start_time_ms,
121 RoutingSearchParameters& search_parameters) {
122 if (!search_parameters.has_time_limit())
return true;
123 const absl::Duration elapsed_time =
124 absl::Milliseconds(solver->wall_time() - start_time_ms);
125 const absl::Duration time_left =
126 std::min(
time_limit - elapsed_time, GetTimeLimit(search_parameters));
127 if (time_left <= absl::ZeroDuration())
return false;
129 time_left, search_parameters.mutable_time_limit());
136 RoutingModel* primary_model,
137 const std::vector<RoutingModel*>& alternative_models,
138 const RoutingSearchParameters& parameters,
139 int max_non_improving_iterations) {
141 nullptr, primary_model, alternative_models, parameters,
142 max_non_improving_iterations);
146 const Assignment* assignment, RoutingModel* primary_model,
147 const std::vector<RoutingModel*>& alternative_models,
148 const RoutingSearchParameters& parameters,
149 int max_non_improving_iterations) {
151 assignment, primary_model, parameters, alternative_models, {},
152 max_non_improving_iterations);
156 const Assignment* assignment, RoutingModel* primary_model,
157 const RoutingSearchParameters& primary_parameters,
158 const std::vector<RoutingModel*>& alternative_models,
159 const std::vector<RoutingSearchParameters>& alternative_parameters,
160 int max_non_improving_iterations) {
162 if (max_non_improving_iterations < 0)
return nullptr;
164 if (max_non_improving_iterations == 0 || alternative_models.empty()) {
165 return primary_model->SolveFromAssignmentWithParameters(assignment,
168 RoutingSearchParameters mutable_search_parameters = primary_parameters;
172 const LocalSearchMetaheuristic_Value metaheuristic =
173 mutable_search_parameters.local_search_metaheuristic();
174 const bool run_metaheuristic_phase =
175 metaheuristic != LocalSearchMetaheuristic::GREEDY_DESCENT &&
176 metaheuristic != LocalSearchMetaheuristic::AUTOMATIC;
177 if (run_metaheuristic_phase) {
178 mutable_search_parameters.set_local_search_metaheuristic(
179 LocalSearchMetaheuristic::GREEDY_DESCENT);
181 std::vector<RoutingSearchParameters> mutable_alternative_parameters =
182 alternative_parameters;
183 DCHECK(mutable_alternative_parameters.empty() ||
184 mutable_alternative_parameters.size() == alternative_models.size());
185 for (RoutingSearchParameters& mutable_alternative_parameter :
186 mutable_alternative_parameters) {
187 if (!mutable_alternative_parameter.has_time_limit() &&
188 mutable_search_parameters.has_time_limit()) {
189 *mutable_alternative_parameter.mutable_time_limit() =
190 mutable_search_parameters.time_limit();
193 const absl::Duration
time_limit = GetTimeLimit(mutable_search_parameters);
195 std::vector<Assignment*> first_solutions;
196 first_solutions.reserve(alternative_models.size());
197 for (RoutingModel* model : alternative_models) {
198 first_solutions.push_back(model->solver()->MakeAssignment());
203 DCHECK(assignment ==
nullptr ||
204 assignment->
solver() == primary_model->solver());
206 assignment, mutable_search_parameters);
207 if (
solution ==
nullptr)
return nullptr;
210 while (iteration < max_non_improving_iterations) {
215 for (
int i = 0; i < alternative_models.size(); ++i) {
216 RoutingSearchParameters& mutable_alternative_parameter =
217 mutable_alternative_parameters.empty()
218 ? mutable_search_parameters
219 : mutable_alternative_parameters[i];
220 if (!UpdateTimeLimits(primary_model->solver(), start_time_ms,
time_limit,
221 mutable_alternative_parameter)) {
222 return best_assignment;
224 ConvertAssignment(primary_model,
solution, alternative_models[i],
226 solution = alternative_models[i]->SolveFromAssignmentWithParameters(
227 first_solutions[i], mutable_alternative_parameter);
231 if (!UpdateTimeLimits(primary_model->solver(), start_time_ms,
time_limit,
232 mutable_search_parameters)) {
233 return best_assignment;
235 ConvertAssignment(alternative_models[i],
solution, primary_model,
236 primary_first_solution);
237 solution = primary_model->SolveFromAssignmentWithParameters(
238 primary_first_solution, mutable_search_parameters);
239 if (
solution ==
nullptr)
return best_assignment;
242 if (AreAssignmentsEquivalent(*primary_model, *
solution,
243 *current_solution)) {
248 if (AreAssignmentsEquivalent(*primary_model, *
solution, *best_assignment)) {
255 if (run_metaheuristic_phase &&
256 UpdateTimeLimits(primary_model->solver(), start_time_ms,
time_limit,
257 mutable_search_parameters)) {
258 mutable_search_parameters.set_local_search_metaheuristic(metaheuristic);
259 return primary_model->SolveFromAssignmentWithParameters(
260 best_assignment, mutable_search_parameters);
262 return best_assignment;
268 const std::vector<std::set<VehicleClassEntry>>& all_vehicle_classes_per_type =
269 vehicle_type_container_->sorted_vehicle_classes_per_type;
270 sorted_vehicle_classes_per_type_.resize(all_vehicle_classes_per_type.size());
271 const std::vector<std::deque<int>>& all_vehicles_per_class =
272 vehicle_type_container_->vehicles_per_vehicle_class;
273 vehicles_per_vehicle_class_.resize(all_vehicles_per_class.size());
275 for (
int type = 0; type < all_vehicle_classes_per_type.size(); type++) {
276 std::set<VehicleClassEntry>& stored_class_entries =
277 sorted_vehicle_classes_per_type_[type];
278 stored_class_entries.clear();
279 for (VehicleClassEntry class_entry : all_vehicle_classes_per_type[type]) {
280 const int vehicle_class = class_entry.vehicle_class;
281 std::vector<int>& stored_vehicles =
282 vehicles_per_vehicle_class_[vehicle_class];
283 stored_vehicles.clear();
284 for (
int vehicle : all_vehicles_per_class[vehicle_class]) {
285 if (store_vehicle(vehicle)) {
286 stored_vehicles.push_back(vehicle);
289 if (!stored_vehicles.empty()) {
290 stored_class_entries.insert(class_entry);
297 const std::function<
bool(
int)>& remove_vehicle) {
298 for (std::set<VehicleClassEntry>& class_entries :
299 sorted_vehicle_classes_per_type_) {
300 auto class_entry_it = class_entries.begin();
301 while (class_entry_it != class_entries.end()) {
302 const int vehicle_class = class_entry_it->vehicle_class;
303 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
304 vehicles.erase(std::remove_if(vehicles.begin(), vehicles.end(),
305 [&remove_vehicle](
int vehicle) {
306 return remove_vehicle(vehicle);
309 if (vehicles.empty()) {
310 class_entry_it = class_entries.erase(class_entry_it);
319 int type,
const std::function<
bool(
int)>& vehicle_is_compatible)
const {
320 for (
const VehicleClassEntry& vehicle_class_entry :
321 sorted_vehicle_classes_per_type_[type]) {
323 vehicles_per_vehicle_class_[vehicle_class_entry.vehicle_class]) {
324 if (vehicle_is_compatible(vehicle))
return true;
331 int type,
const std::function<
bool(
int)>& vehicle_is_compatible,
332 const std::function<
bool(
int)>& stop_and_return_vehicle) {
333 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
334 sorted_vehicle_classes_per_type_[type];
335 auto vehicle_class_it = sorted_classes.begin();
337 while (vehicle_class_it != sorted_classes.end()) {
338 const int vehicle_class = vehicle_class_it->vehicle_class;
339 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
340 DCHECK(!vehicles.empty());
342 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
344 const int vehicle = *vehicle_it;
345 if (vehicle_is_compatible(vehicle)) {
346 vehicles.erase(vehicle_it);
347 if (vehicles.empty()) {
348 sorted_classes.erase(vehicle_class_it);
350 return {vehicle, -1};
352 if (stop_and_return_vehicle(vehicle)) {
353 return {-1, vehicle};
372 bool has_pickup_deliveries,
bool has_node_precedences,
373 bool has_single_vehicle_node) {
374 if (has_pickup_deliveries || has_node_precedences) {
375 return FirstSolutionStrategy::PARALLEL_CHEAPEST_INSERTION;
377 if (has_single_vehicle_node) {
378 return FirstSolutionStrategy::PATH_MOST_CONSTRAINED_ARC;
380 return FirstSolutionStrategy::PATH_CHEAPEST_ARC;
384 const int64_t size = model.Size();
385 const int num_vehicles = model.vehicles();
393 std::vector<int64_t> starts(size + num_vehicles, -1);
394 std::vector<int64_t> ends(size + num_vehicles, -1);
395 for (
int node = 0; node < size + num_vehicles; ++node) {
400 std::vector<bool> touched(size,
false);
401 for (
int node = 0; node < size; ++node) {
403 while (!model.IsEnd(current) && !touched[current]) {
404 touched[current] =
true;
405 IntVar*
const next_var = model.NextVar(current);
406 if (next_var->
Bound()) {
407 current = next_var->
Value();
412 starts[ends[current]] = starts[node];
413 ends[starts[node]] = ends[current];
417 std::vector<int64_t> end_chain_starts(num_vehicles);
418 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
419 end_chain_starts[vehicle] = starts[model.End(vehicle)];
421 return end_chain_starts;
429 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
430 : heuristic_(
std::move(heuristic)) {}
433 Assignment*
const assignment = heuristic_->BuildSolution();
434 if (assignment !=
nullptr) {
435 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
436 VLOG(2) <<
"Number of rejected decisions: "
437 << heuristic_->number_of_rejects();
446 return heuristic_->number_of_decisions();
450 return heuristic_->number_of_rejects();
454 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
455 heuristic_->DebugString(),
")");
463 Solver* solver,
const std::vector<IntVar*>& vars,
464 const std::vector<IntVar*>& secondary_vars,
469 base_vars_size_(vars.size()),
470 delta_(solver->MakeAssignment()),
471 empty_(solver->MakeAssignment()),
472 filter_manager_(filter_manager),
473 objective_upper_bound_(
std::numeric_limits<int64_t>::max()),
474 number_of_decisions_(0),
475 number_of_rejects_(0) {
476 if (!secondary_vars.empty()) {
477 vars_.insert(vars_.end(), secondary_vars.begin(), secondary_vars.end());
479 assignment_->MutableIntVarContainer()->Resize(vars_.size());
480 is_in_delta_.resize(vars_.size(),
false);
481 delta_indices_.reserve(vars_.size());
485 number_of_decisions_ = 0;
486 number_of_rejects_ = 0;
489 delta_->MutableIntVarContainer()->Clear();
491 assignment_->MutableIntVarContainer()->Resize(vars_.size());
508 const std::function<int64_t(int64_t)>& next_accessor) {
515 if (!InitializeSolution()) {
519 for (
int v = 0; v < model_->vehicles(); v++) {
520 int64_t node = model_->Start(v);
521 while (!model_->IsEnd(node)) {
522 const int64_t next = next_accessor(node);
523 DCHECK_NE(next, node);
540 bool commit,
bool ignore_upper_bound,
bool update_upper_bound) {
541 ++number_of_decisions_;
542 const bool accept = FilterAccept(ignore_upper_bound);
543 int64_t objective_upper_bound = objective_upper_bound_;
545 if (filter_manager_ !=
nullptr) {
553 DCHECK(ignore_upper_bound ||
554 filter_manager_->GetAcceptedObjectiveValue() <=
555 objective_upper_bound_);
556 objective_upper_bound = filter_manager_->GetAcceptedObjectiveValue();
557 if (update_upper_bound) {
558 objective_upper_bound_ = objective_upper_bound;
563 delta_->IntVarContainer();
564 const int delta_size = delta_container.
Size();
567 for (
int i = 0; i < delta_size; ++i) {
570 DCHECK_EQ(var, vars_[delta_indices_[i]]);
577 ++number_of_rejects_;
580 for (
const int delta_index : delta_indices_) {
581 is_in_delta_[delta_index] =
false;
584 delta_indices_.clear();
585 return accept ? std::optional<int64_t>{objective_upper_bound} : std::nullopt;
589 if (filter_manager_) filter_manager_->Synchronize(
assignment_, delta_);
591 objective_upper_bound_ = std::numeric_limits<int64_t>::max();
594bool IntVarFilteredHeuristic::FilterAccept(
bool ignore_upper_bound) {
595 if (!filter_manager_)
return true;
597 return filter_manager_->
Accept(
598 monitor, delta_, empty_, std::numeric_limits<int64_t>::min(),
599 ignore_upper_bound ? std::numeric_limits<int64_t>::max()
600 : objective_upper_bound_);
606 RoutingModel*
model, std::function<
bool()> stop_search,
609 model->CostsAreHomogeneousAcrossVehicles()
611 :
model->VehicleVars(),
614 stop_search_(
std::move(stop_search)) {}
616bool RoutingFilteredHeuristic::InitializeSolution() {
621 start_chain_ends_.resize(
model()->vehicles());
622 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
623 int64_t node =
model()->Start(vehicle);
624 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
625 const int64_t next =
Var(node)->
Min();
630 start_chain_ends_[vehicle] = node;
637 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
638 int64_t node = start_chain_ends_[vehicle];
639 if (!
model()->IsEnd(node)) {
640 int64_t next = end_chain_starts_[vehicle];
644 while (!
model()->IsEnd(node)) {
661 model()->ForEachNodeInDisjunctionWithMaxCardinalityFromIndex(
662 node, 1, [
this, node](
int alternate) {
663 if (node != alternate && !
Contains(alternate)) {
664 SetNext(alternate, alternate, -1);
672 absl::btree_set<std::pair<int64_t, int>> empty_vehicles;
673 for (
int vehicle = 0; vehicle < model_->vehicles(); ++vehicle) {
675 empty_vehicles.insert({
model()->GetFixedCostOfVehicle(vehicle), vehicle});
678 for (
int index = 0; index < model_->Size(); ++index) {
679 if (
StopSearch() || empty_vehicles.empty())
return;
682 for (
auto [cost, vehicle] : empty_vehicles) {
683 SetNext(model_->Start(vehicle), index, vehicle);
684 SetNext(index, model_->End(vehicle), vehicle);
686 empty_vehicles.erase({cost, vehicle});
696 for (
int index = 0; index < model_->Size(); ++index) {
706 const int num_nexts =
model()->Nexts().size();
707 std::vector<bool> to_make_unperformed(num_nexts,
false);
708 for (
const auto& [pickups, deliveries] :
709 model()->GetPickupAndDeliveryPairs()) {
710 int64_t performed_pickup = -1;
711 for (int64_t pickup : pickups) {
713 performed_pickup = pickup;
717 int64_t performed_delivery = -1;
718 for (int64_t delivery : deliveries) {
720 performed_delivery = delivery;
724 if ((performed_pickup == -1) != (performed_delivery == -1)) {
725 if (performed_pickup != -1) {
726 to_make_unperformed[performed_pickup] =
true;
728 if (performed_delivery != -1) {
729 to_make_unperformed[performed_delivery] =
true;
733 for (
int index = 0; index < num_nexts; ++index) {
734 if (to_make_unperformed[index] || !
Contains(index))
continue;
737 int64_t next =
Value(index);
738 while (next < num_nexts && to_make_unperformed[next]) {
739 const int64_t next_of_next =
Value(next);
740 SetNext(index, next_of_next, vehicle);
750 RoutingModel*
model, std::function<
bool()> stop_search,
751 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
752 std::function<int64_t(int64_t)> penalty_evaluator,
759void ProcessVehicleStartEndCosts(
760 const RoutingModel& model,
int node,
761 const std::function<
void(int64_t,
int)>& process_cost,
763 bool ignore_end =
false) {
764 const auto start_end_cost = [&model, ignore_start, ignore_end](int64_t node,
766 const int64_t start_cost =
767 ignore_start ? 0 : model.GetArcCostForVehicle(model.Start(v), node, v);
768 const int64_t end_cost =
769 ignore_end ? 0 : model.GetArcCostForVehicle(model.End(v), node, v);
770 return CapAdd(start_cost, end_cost);
776 const IntVar*
const vehicle_var = model.VehicleVar(node);
777 if (vehicle_var->Size() < vehicle_set.
size()) {
778 std::unique_ptr<IntVarIterator> it(vehicle_var->MakeDomainIterator(
false));
779 for (
const int v : InitAndGetValues(it.get())) {
780 if (v < 0 || !vehicle_set[v]) {
783 process_cost(start_end_cost(node, v), v);
786 for (
const int v : vehicle_set) {
787 if (!vehicle_var->Contains(v))
continue;
788 process_cost(start_end_cost(node, v), v);
794std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
796 absl::Span<const int> vehicles) {
799 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
803 for (
int v : vehicles) vehicle_set.
Set(v);
805 for (
int node = 0; node <
model.Size(); node++) {
807 std::vector<StartEndValue>& start_end_distances =
808 start_end_distances_per_node[node];
809 start_end_distances.reserve(
810 std::min(
model.VehicleVar(node)->Size(),
811 static_cast<uint64_t
>(vehicles.size())));
813 ProcessVehicleStartEndCosts(
815 [&start_end_distances](int64_t dist,
int v) {
816 start_end_distances.push_back({dist, v});
822 absl::c_sort(start_end_distances,
824 return second < first;
827 return start_end_distances_per_node;
831 int node, std::vector<StartEndValue>* start_end_distances,
SeedQueue* sq) {
832 if (start_end_distances->empty()) {
837 StartEndValue& start_end_value = start_end_distances->back();
841 const int64_t num_allowed_vehicles =
model()->VehicleVar(node)->Size();
842 const int64_t neg_penalty =
CapOpp(
model()->UnperformedPenalty(node));
843 sq->
priority_queue.push({.properties = {num_allowed_vehicles, neg_penalty,
845 .vehicle = start_end_value.
vehicle,
846 .is_node_index =
true,
848 start_end_distances->pop_back();
852 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
854 const int num_nodes =
model()->Size();
855 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
857 for (
int node = 0; node < num_nodes; node++) {
878 int64_t node_to_insert, int64_t start, int64_t next_after_start,
879 int vehicle,
bool ignore_cost,
880 std::vector<NodeInsertion>* node_insertions) {
881 DCHECK(node_insertions !=
nullptr);
882 int64_t insert_after = start;
883 if (!
model()->VehicleVar(node_to_insert)->
Contains(vehicle))
return;
884 while (!
model()->IsEnd(insert_after)) {
885 const int64_t insert_before =
886 (insert_after == start) ? next_after_start :
Value(insert_after);
887 const int hint_weight =
IsHint(insert_after, node_to_insert) +
888 IsHint(node_to_insert, insert_before) -
889 IsHint(insert_after, insert_before);
891 InsertBetween(node_to_insert, insert_after, insert_before, vehicle);
892 std::optional<int64_t> insertion_cost =
895 if (insertion_cost.has_value()) {
896 node_insertions->push_back(
897 {insert_after, vehicle, -hint_weight, *insertion_cost});
900 node_insertions->push_back(
901 {insert_after, vehicle, -hint_weight,
905 insert_before, vehicle)});
907 insert_after = insert_before;
912 int64_t node_to_insert, int64_t insert_after, int64_t insert_before,
916 evaluator_(node_to_insert, insert_before, vehicle)),
917 evaluator_(insert_after, insert_before, vehicle));
921 int64_t node_to_insert)
const {
925 return std::numeric_limits<int64_t>::max();
932 RoutingModel*
model, std::function<
bool()> stop_search,
933 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
934 std::function<int64_t(int64_t)> penalty_evaluator,
938 model,
std::move(stop_search),
std::move(evaluator),
939 std::move(penalty_evaluator), filter_manager),
940 gci_params_(parameters),
941 node_index_to_vehicle_(
model->Size(), -1),
942 node_index_to_neighbors_by_cost_class_(nullptr),
943 empty_vehicle_type_curator_(nullptr) {
944 CHECK_GT(gci_params_.neighbors_ratio, 0);
945 CHECK_LE(gci_params_.neighbors_ratio, 1);
946 CHECK_GE(gci_params_.min_neighbors, 1);
949bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
950 std::vector<bool> node_is_visited(
model()->Size(),
false);
951 for (
int v = 0; v <
model()->vehicles(); v++) {
952 for (
int node =
model()->Start(v); !
model()->IsEnd(node);
953 node =
Value(node)) {
954 if (node_index_to_vehicle_[node] != v) {
957 node_is_visited[node] =
true;
961 for (
int node = 0; node <
model()->Size(); node++) {
962 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
972 double neighbors_ratio_used = 1;
973 node_index_to_neighbors_by_cost_class_ =
974 model()->GetOrCreateNodeNeighborsByCostClass(gci_params_.neighbors_ratio,
975 gci_params_.min_neighbors,
976 neighbors_ratio_used);
977 if (neighbors_ratio_used == 1) {
978 gci_params_.use_neighbors_ratio_for_initialization =
false;
981 if (empty_vehicle_type_curator_ ==
nullptr) {
982 empty_vehicle_type_curator_ = std::make_unique<VehicleTypeCurator>(
983 model()->GetVehicleTypeContainer());
986 empty_vehicle_type_curator_->Reset(
989 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
990 model()->GetPickupAndDeliveryPairs();
991 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
992 absl::flat_hash_map<int, std::map<int64_t, std::vector<int>>>
993 vehicle_to_pair_nodes;
994 for (
int index = 0; index < pickup_delivery_pairs.size(); index++) {
996 pickup_delivery_pairs[index];
997 const auto& [pickups, deliveries] = pickup_delivery_pair;
998 int pickup_vehicle = -1;
999 for (int64_t pickup : pickups) {
1001 pickup_vehicle = node_index_to_vehicle_[pickup];
1005 int delivery_vehicle = -1;
1006 for (int64_t delivery : deliveries) {
1008 delivery_vehicle = node_index_to_vehicle_[delivery];
1012 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
1013 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pair)]
1016 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
1017 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle][1];
1018 for (int64_t delivery : deliveries) {
1019 pair_nodes.push_back(delivery);
1022 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
1023 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle][1];
1024 for (int64_t pickup : pickups) {
1025 pair_nodes.push_back(pickup);
1030 const auto unperform_unassigned_and_check = [
this]() {
1034 for (
const auto& [vehicle, nodes] : vehicle_to_pair_nodes) {
1035 if (!InsertNodesOnRoutes(nodes, {vehicle})) {
1036 return unperform_unassigned_and_check();
1040 if (!InsertPairsAndNodesByRequirementTopologicalOrder()) {
1041 return unperform_unassigned_and_check();
1046 if (!InsertPairs(pairs_to_insert_by_bucket)) {
1047 return unperform_unassigned_and_check();
1049 std::map<int64_t, std::vector<int>> nodes_by_bucket;
1050 for (
int node = 0; node <
model()->Size(); ++node) {
1052 !
model()->IsDelivery(node)) {
1053 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
1056 InsertFarthestNodesAsSeeds();
1057 if (gci_params_.is_sequential) {
1058 if (!SequentialInsertNodes(nodes_by_bucket)) {
1059 return unperform_unassigned_and_check();
1061 }
else if (!InsertNodesOnRoutes(nodes_by_bucket, {})) {
1062 return unperform_unassigned_and_check();
1064 DCHECK(CheckVehicleIndices());
1065 return unperform_unassigned_and_check();
1068bool GlobalCheapestInsertionFilteredHeuristic::
1069 InsertPairsAndNodesByRequirementTopologicalOrder() {
1070 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1071 model()->GetPickupAndDeliveryPairs();
1072 for (
const std::vector<int>& types :
1073 model()->GetTopologicallySortedVisitTypes()) {
1074 for (
int type : types) {
1075 std::map<int64_t, std::vector<int>> pairs_to_insert_by_bucket;
1076 for (
int index :
model()->GetPairIndicesOfType(type)) {
1077 pairs_to_insert_by_bucket[GetBucketOfPair(pickup_delivery_pairs[index])]
1080 if (!InsertPairs(pairs_to_insert_by_bucket))
return false;
1081 std::map<int64_t, std::vector<int>> nodes_by_bucket;
1082 for (
int node :
model()->GetSingleNodesOfType(type)) {
1083 nodes_by_bucket[GetBucketOfNode(node)].push_back(node);
1085 if (!InsertNodesOnRoutes(nodes_by_bucket, {}))
return false;
1091bool GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
1092 const std::map<int64_t, std::vector<int>>& pair_indices_by_bucket) {
1093 AdjustablePriorityQueue<PairEntry> priority_queue;
1094 std::vector<PairEntries> pickup_to_entries;
1095 std::vector<PairEntries> delivery_to_entries;
1096 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1097 model()->GetPickupAndDeliveryPairs();
1098 auto pair_is_performed = [
this, &pickup_delivery_pairs](
int pair_index) {
1099 const auto& [pickups, deliveries] = pickup_delivery_pairs[pair_index];
1100 for (int64_t pickup : pickups) {
1103 for (int64_t delivery : deliveries) {
1104 if (
Contains(delivery))
return true;
1108 absl::flat_hash_set<int> pair_indices_to_insert;
1109 for (
const auto& [bucket, pair_indices] : pair_indices_by_bucket) {
1110 for (
const int pair_index : pair_indices) {
1111 if (!pair_is_performed(pair_index)) {
1112 pair_indices_to_insert.insert(pair_index);
1115 if (!InitializePairPositions(pair_indices_to_insert, &priority_queue,
1116 &pickup_to_entries, &delivery_to_entries)) {
1119 while (!priority_queue.
IsEmpty()) {
1120 if (StopSearchAndCleanup(&priority_queue)) {
1123 PairEntry*
const entry = priority_queue.
Top();
1124 const int64_t pickup = entry->pickup_to_insert();
1125 const int64_t delivery = entry->delivery_to_insert();
1127 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1128 &delivery_to_entries);
1132 const int entry_vehicle = entry->vehicle();
1133 if (entry_vehicle == -1) {
1136 SetNext(delivery, delivery, -1);
1138 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1139 &delivery_to_entries);
1145 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle)) {
1146 if (!InsertPairEntryUsingEmptyVehicleTypeCurator(
1147 pair_indices_to_insert, entry, &priority_queue,
1148 &pickup_to_entries, &delivery_to_entries)) {
1156 const int64_t pickup_insert_after = entry->pickup_insert_after();
1157 const int64_t pickup_insert_before =
Value(pickup_insert_after);
1158 InsertBetween(pickup, pickup_insert_after, pickup_insert_before);
1160 const int64_t delivery_insert_after = entry->delivery_insert_after();
1161 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
1162 ? pickup_insert_before
1163 :
Value(delivery_insert_after);
1164 InsertBetween(delivery, delivery_insert_after, delivery_insert_before);
1166 if (!UpdateAfterPairInsertion(
1167 pair_indices_to_insert, entry_vehicle, pickup,
1168 pickup_insert_after, delivery, delivery_insert_after,
1169 &priority_queue, &pickup_to_entries, &delivery_to_entries)) {
1173 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
1174 &delivery_to_entries);
1179 for (
auto it = pair_indices_to_insert.begin(),
1180 last = pair_indices_to_insert.end();
1182 if (pair_is_performed(*it)) {
1183 pair_indices_to_insert.erase(it++);
1192bool GlobalCheapestInsertionFilteredHeuristic::
1193 InsertPairEntryUsingEmptyVehicleTypeCurator(
1194 const absl::flat_hash_set<int>& pair_indices,
1195 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
1196 AdjustablePriorityQueue<
1197 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1199 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1201 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1202 delivery_to_entries) {
1203 const int entry_vehicle = pair_entry->vehicle();
1204 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle));
1210 const int64_t pickup = pair_entry->pickup_to_insert();
1211 const int64_t delivery = pair_entry->delivery_to_insert();
1212 const int64_t entry_fixed_cost =
1213 model()->GetFixedCostOfVehicle(entry_vehicle);
1214 auto vehicle_is_compatible = [
this, entry_fixed_cost, pickup,
1215 delivery](
int vehicle) {
1216 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1221 const int64_t end =
model()->End(vehicle);
1230 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1231 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1233 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1234 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1235 empty_vehicle_type_curator_->Type(entry_vehicle),
1236 vehicle_is_compatible, stop_and_return_vehicle);
1237 if (compatible_vehicle >= 0) {
1239 const int64_t vehicle_start =
model()->Start(compatible_vehicle);
1240 const int num_previous_vehicle_entries =
1241 pickup_to_entries->at(vehicle_start).size() +
1242 delivery_to_entries->at(vehicle_start).size();
1243 if (!UpdateAfterPairInsertion(
1244 pair_indices, compatible_vehicle, pickup, vehicle_start, delivery,
1245 pickup, priority_queue, pickup_to_entries, delivery_to_entries)) {
1248 if (compatible_vehicle != entry_vehicle) {
1255 num_previous_vehicle_entries == 0 ||
1256 model()->GetVehicleClassIndexOfVehicle(compatible_vehicle).value() !=
1257 model()->GetVehicleClassIndexOfVehicle(entry_vehicle).value());
1263 const int new_empty_vehicle =
1264 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1265 empty_vehicle_type_curator_->Type(compatible_vehicle));
1267 if (new_empty_vehicle >= 0) {
1274 const int64_t new_empty_vehicle_start =
model()->Start(new_empty_vehicle);
1275 const std::vector<PairEntry*> to_remove(
1276 pickup_to_entries->at(new_empty_vehicle_start).begin(),
1277 pickup_to_entries->at(new_empty_vehicle_start).end());
1278 for (PairEntry* entry : to_remove) {
1279 DeletePairEntry(entry, priority_queue, pickup_to_entries,
1280 delivery_to_entries);
1282 if (!AddPairEntriesWithPickupAfter(
1283 pair_indices, new_empty_vehicle, new_empty_vehicle_start,
1285 pickup_to_entries, delivery_to_entries)) {
1289 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1296 pair_entry->set_vehicle(next_fixed_cost_empty_vehicle);
1297 pickup_to_entries->at(pair_entry->pickup_insert_after()).erase(pair_entry);
1298 pair_entry->set_pickup_insert_after(
1299 model()->Start(next_fixed_cost_empty_vehicle));
1300 pickup_to_entries->at(pair_entry->pickup_insert_after()).insert(pair_entry);
1301 DCHECK_EQ(pair_entry->delivery_insert_after(), pickup);
1302 UpdatePairEntry(pair_entry, priority_queue);
1304 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1305 delivery_to_entries);
1335 : entries_(num_nodes), touched_entries_(num_nodes) {}
1337 priority_queue_.Clear();
1338 for (Entries& entries : entries_) entries.Clear();
1339 touched_entries_.SparseClearAll();
1342 return priority_queue_.IsEmpty() &&
1343 touched_entries_.NumberOfSetCallsWithDifferentArguments() == 0;
1346 return insert_after >= entries_.size() ||
1347 entries_[insert_after].entries.empty();
1351 for (
int touched : touched_entries_.PositionsSetAtLeastOnce()) {
1352 SortInsertions(&entries_[touched]);
1354 touched_entries_.SparseClearAll();
1355 DCHECK(!priority_queue_.IsEmpty());
1356 Entries* entries = priority_queue_.Top();
1357 DCHECK(!entries->entries.empty());
1358 return entries->Top();
1362 CHECK_EQ(touched_entries_.NumberOfSetCallsWithDifferentArguments(), 0);
1363 Entries* top = priority_queue_.Top();
1364 if (top->IncrementTop()) {
1365 priority_queue_.NoteChangedPriority(top);
1367 priority_queue_.Remove(top);
1372 if (
IsEmpty(insert_after))
return;
1373 Entries& entries = entries_[insert_after];
1374 if (priority_queue_.Contains(&entries)) {
1375 priority_queue_.Remove(&entries);
1380 int bucket, int64_t value) {
1381 entries_[insert_after].entries.push_back(
1382 {value, node, insert_after, vehicle, bucket});
1383 touched_entries_.Set(insert_after);
1388 bool operator<(
const Entries& other)
const {
1389 DCHECK(!entries.empty());
1390 DCHECK(!other.entries.empty());
1391 return other.entries[other.top] < entries[top];
1398 void SetHeapIndex(
int index) { heap_index = index; }
1399 int GetHeapIndex()
const {
return heap_index; }
1400 bool IncrementTop() {
1402 return top < entries.size();
1404 Entry* Top() {
return &entries[top]; }
1406 std::vector<Entry> entries;
1408 int heap_index = -1;
1411 void SortInsertions(Entries* entries) {
1413 if (entries->entries.empty())
return;
1414 absl::c_sort(entries->entries);
1415 if (!priority_queue_.Contains(entries)) {
1416 priority_queue_.Add(entries);
1418 priority_queue_.NoteChangedPriority(entries);
1422 AdjustablePriorityQueue<Entries> priority_queue_;
1423 std::vector<Entries> entries_;
1424 SparseBitset<int> touched_entries_;
1427bool GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
1428 const std::map<int64_t, std::vector<int>>& nodes_by_bucket,
1429 const absl::flat_hash_set<int>& vehicles) {
1431 SparseBitset<int> nodes_to_insert(
model()->Size());
1432 for (
const auto& [bucket, nodes] : nodes_by_bucket) {
1433 for (
int node : nodes) {
1434 nodes_to_insert.Set(node);
1436 if (!InitializePositions(nodes_to_insert, vehicles, &queue)) {
1445 const bool all_vehicles =
1446 vehicles.empty() || vehicles.size() ==
model()->vehicles();
1448 while (!queue.IsEmpty()) {
1457 const int entry_vehicle = node_entry->vehicle;
1458 if (entry_vehicle == -1) {
1459 DCHECK(all_vehicles);
1461 SetNext(node_to_insert, node_to_insert, -1);
1469 if (UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles)) {
1470 DCHECK(all_vehicles);
1471 if (!InsertNodeEntryUsingEmptyVehicleTypeCurator(
1472 nodes_to_insert, all_vehicles, &queue)) {
1478 const int64_t insert_after = node_entry->insert_after;
1481 if (!UpdateAfterNodeInsertion(nodes_to_insert, entry_vehicle,
1482 node_to_insert, insert_after,
1483 all_vehicles, &queue)) {
1492 std::vector<int> non_inserted_nodes;
1493 non_inserted_nodes.reserve(
1494 nodes_to_insert.NumberOfSetCallsWithDifferentArguments());
1495 for (
int node : nodes_to_insert.PositionsSetAtLeastOnce()) {
1496 if (!
Contains(node)) non_inserted_nodes.push_back(node);
1498 nodes_to_insert.SparseClearAll();
1499 for (
int node : non_inserted_nodes) {
1500 nodes_to_insert.Set(node);
1506bool GlobalCheapestInsertionFilteredHeuristic::
1509 NodeEntryQueue* queue) {
1511 const int entry_vehicle = node_entry->
vehicle;
1512 DCHECK(UseEmptyVehicleTypeCuratorForVehicle(entry_vehicle, all_vehicles));
1519 const int64_t node_to_insert = node_entry->node_to_insert;
1520 const int bucket = node_entry->bucket;
1521 const int64_t entry_fixed_cost =
1522 model()->GetFixedCostOfVehicle(entry_vehicle);
1523 auto vehicle_is_compatible = [
this, entry_fixed_cost,
1524 node_to_insert](
int vehicle) {
1525 if (
model()->GetFixedCostOfVehicle(vehicle) != entry_fixed_cost) {
1531 model()->End(vehicle), vehicle);
1538 auto stop_and_return_vehicle = [
this, entry_fixed_cost](
int vehicle) {
1539 return model()->GetFixedCostOfVehicle(vehicle) > entry_fixed_cost;
1541 const auto [compatible_vehicle, next_fixed_cost_empty_vehicle] =
1542 empty_vehicle_type_curator_->GetCompatibleVehicleOfType(
1543 empty_vehicle_type_curator_->Type(entry_vehicle),
1544 vehicle_is_compatible, stop_and_return_vehicle);
1545 if (compatible_vehicle >= 0) {
1547 const int64_t compatible_start =
model()->Start(compatible_vehicle);
1548 const bool no_prior_entries_for_this_vehicle =
1549 queue->IsEmpty(compatible_start);
1550 if (!UpdateAfterNodeInsertion(nodes, compatible_vehicle, node_to_insert,
1551 compatible_start, all_vehicles, queue)) {
1554 if (compatible_vehicle != entry_vehicle) {
1561 no_prior_entries_for_this_vehicle ||
1562 model()->GetVehicleClassIndexOfVehicle(compatible_vehicle).value() !=
1563 model()->GetVehicleClassIndexOfVehicle(entry_vehicle).value());
1569 const int new_empty_vehicle =
1570 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1571 empty_vehicle_type_curator_->Type(compatible_vehicle));
1573 if (new_empty_vehicle >= 0) {
1580 const int64_t new_empty_vehicle_start =
model()->Start(new_empty_vehicle);
1581 queue->ClearInsertions(new_empty_vehicle_start);
1582 if (!AddNodeEntriesAfter(nodes, new_empty_vehicle,
1583 new_empty_vehicle_start, all_vehicles, queue)) {
1587 }
else if (next_fixed_cost_empty_vehicle >= 0) {
1595 const int64_t insert_after =
model()->Start(next_fixed_cost_empty_vehicle);
1597 node_to_insert, insert_after,
Value(insert_after),
1598 next_fixed_cost_empty_vehicle);
1599 const int64_t penalty_shift =
1600 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
1603 queue->PushInsertion(node_to_insert, insert_after,
1604 next_fixed_cost_empty_vehicle, bucket,
1605 CapSub(insertion_cost, penalty_shift));
1613bool GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
1614 const std::map<int64_t, std::vector<int>>& nodes_by_bucket) {
1615 std::vector<bool> is_vehicle_used;
1616 absl::flat_hash_set<int> used_vehicles;
1617 std::vector<int> unused_vehicles;
1619 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1620 if (!used_vehicles.empty() &&
1621 !InsertNodesOnRoutes(nodes_by_bucket, used_vehicles)) {
1625 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1630 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1633 while (vehicle >= 0) {
1634 if (!InsertNodesOnRoutes(nodes_by_bucket, {vehicle})) {
1637 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
1643void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
1644 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
1645 absl::flat_hash_set<int>* used_vehicles) {
1646 is_vehicle_used->clear();
1647 is_vehicle_used->resize(
model()->vehicles());
1649 used_vehicles->clear();
1650 used_vehicles->reserve(
model()->vehicles());
1652 unused_vehicles->clear();
1653 unused_vehicles->reserve(
model()->vehicles());
1655 for (
int vehicle = 0; vehicle <
model()->vehicles(); vehicle++) {
1657 (*is_vehicle_used)[vehicle] =
true;
1658 used_vehicles->insert(vehicle);
1660 (*is_vehicle_used)[vehicle] =
false;
1661 unused_vehicles->push_back(vehicle);
1666bool GlobalCheapestInsertionFilteredHeuristic::IsCheapestClassRepresentative(
1667 int vehicle)
const {
1671 const int curator_vehicle =
1672 empty_vehicle_type_curator_->GetLowestFixedCostVehicleOfType(
1673 empty_vehicle_type_curator_->Type(vehicle));
1674 if (curator_vehicle != vehicle &&
1675 model()->GetVehicleClassIndexOfVehicle(curator_vehicle).value() ==
1676 model()->GetVehicleClassIndexOfVehicle(vehicle).value()) {
1683void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
1685 if (gci_params_.farthest_seeds_ratio <= 0)
return;
1687 const int num_seeds =
static_cast<int>(
1688 std::ceil(gci_params_.farthest_seeds_ratio *
model()->vehicles()));
1690 std::vector<bool> is_vehicle_used;
1691 absl::flat_hash_set<int> used_vehicles;
1692 std::vector<int> unused_vehicles;
1693 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
1694 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
1702 int inserted_seeds = 0;
1703 while (inserted_seeds++ < num_seeds) {
1704 if (InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
1705 &is_vehicle_used) < 0) {
1714 DCHECK(empty_vehicle_type_curator_ !=
nullptr);
1715 empty_vehicle_type_curator_->Update(
1719int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
1720 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
1721 SeedQueue* sq, std::vector<bool>* is_vehicle_used) {
1722 auto& priority_queue = sq->priority_queue;
1723 while (!priority_queue.empty()) {
1725 const Seed& seed = priority_queue.top();
1726 const int seed_node = seed.index;
1727 DCHECK(seed.is_node_index);
1728 const int seed_vehicle = seed.vehicle;
1729 priority_queue.pop();
1731 std::vector<StartEndValue>& other_start_end_values =
1732 (*start_end_distances_per_node)[seed_node];
1737 other_start_end_values.clear();
1740 if (!(*is_vehicle_used)[seed_vehicle]) {
1742 const int64_t start =
model()->Start(seed_vehicle);
1743 const int64_t end =
model()->End(seed_vehicle);
1744 DCHECK_EQ(
Value(start), end);
1747 (*is_vehicle_used)[seed_vehicle] =
true;
1748 other_start_end_values.clear();
1749 SetVehicleIndex(seed_node, seed_vehicle);
1750 return seed_vehicle;
1762bool GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
1763 const absl::flat_hash_set<int>& pair_indices,
1764 AdjustablePriorityQueue<
1765 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1766 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1768 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1769 delivery_to_entries) {
1770 priority_queue->
Clear();
1771 pickup_to_entries->clear();
1772 pickup_to_entries->resize(
model()->Size());
1773 delivery_to_entries->clear();
1774 delivery_to_entries->resize(
model()->Size());
1775 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
1776 model()->GetPickupAndDeliveryPairs();
1777 for (
int index : pair_indices) {
1778 const auto& [pickups, deliveries] = pickup_delivery_pairs[index];
1779 for (int64_t pickup : pickups) {
1781 for (int64_t delivery : deliveries) {
1783 if (StopSearchAndCleanup(priority_queue))
return false;
1788 if (gci_params_.add_unperformed_entries && pickups.size() == 1 &&
1789 deliveries.size() == 1 &&
1791 std::numeric_limits<int64_t>::max() &&
1793 std::numeric_limits<int64_t>::max()) {
1794 AddPairEntry(pickup, -1, delivery, -1, -1, priority_queue,
nullptr,
1798 InitializeInsertionEntriesPerformingPair(
1799 pickup, delivery, priority_queue, pickup_to_entries,
1800 delivery_to_entries);
1807void GlobalCheapestInsertionFilteredHeuristic::
1808 InitializeInsertionEntriesPerformingPair(
1809 int64_t pickup, int64_t delivery,
1810 AdjustablePriorityQueue<
1811 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
1813 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1815 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1816 delivery_to_entries) {
1817 if (!gci_params_.use_neighbors_ratio_for_initialization) {
1818 struct PairInsertion {
1819 int64_t insert_pickup_after;
1820 int64_t insert_delivery_after;
1823 std::vector<PairInsertion> pair_insertions;
1824 std::vector<NodeInsertion> pickup_insertions;
1825 std::vector<NodeInsertion> delivery_insertions;
1826 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
1827 if (!IsCheapestClassRepresentative(vehicle))
continue;
1828 const int64_t start =
model()->Start(vehicle);
1829 pickup_insertions.clear();
1831 true, &pickup_insertions);
1832 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
1833 DCHECK(!
model()->IsEnd(pickup_insertion.insert_after));
1834 delivery_insertions.clear();
1836 delivery, pickup,
Value(pickup_insertion.insert_after), vehicle,
1837 true, &delivery_insertions);
1838 for (
const NodeInsertion& delivery_insertion : delivery_insertions) {
1839 pair_insertions.push_back({pickup_insertion.insert_after,
1840 delivery_insertion.insert_after, vehicle});
1844 for (
const auto& [insert_pickup_after, insert_delivery_after, vehicle] :
1846 DCHECK_NE(insert_pickup_after, insert_delivery_after);
1847 AddPairEntry(pickup, insert_pickup_after, delivery, insert_delivery_after,
1848 vehicle, priority_queue, pickup_to_entries,
1849 delivery_to_entries);
1856 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
1858 absl::flat_hash_set<std::pair<int64_t, int64_t>>
1859 existing_insertion_positions;
1861 for (
const int64_t pickup_insert_after :
1862 node_index_to_neighbors_by_cost_class_
1863 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, pickup)) {
1864 if (!
Contains(pickup_insert_after)) {
1867 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
1869 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1873 if (!IsCheapestClassRepresentative(vehicle))
continue;
1875 int64_t delivery_insert_after = pickup;
1876 while (!
model()->IsEnd(delivery_insert_after)) {
1877 const std::pair<int64_t, int64_t> insertion_position = {
1878 pickup_insert_after, delivery_insert_after};
1879 DCHECK(!existing_insertion_positions.contains(insertion_position));
1880 existing_insertion_positions.insert(insertion_position);
1882 AddPairEntry(pickup, pickup_insert_after, delivery,
1883 delivery_insert_after, vehicle, priority_queue,
1884 pickup_to_entries, delivery_to_entries);
1885 delivery_insert_after = (delivery_insert_after == pickup)
1886 ?
Value(pickup_insert_after)
1887 :
Value(delivery_insert_after);
1892 for (
const int64_t delivery_insert_after :
1893 node_index_to_neighbors_by_cost_class_
1894 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, delivery)) {
1895 if (!
Contains(delivery_insert_after)) {
1898 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
1900 model()->GetCostClassIndexOfVehicle(vehicle).value() != cost_class) {
1906 DCHECK_EQ(delivery_insert_after,
model()->Start(vehicle));
1909 int64_t pickup_insert_after =
model()->Start(vehicle);
1910 while (pickup_insert_after != delivery_insert_after) {
1911 if (!existing_insertion_positions.contains(
1912 std::make_pair(pickup_insert_after, delivery_insert_after))) {
1913 AddPairEntry(pickup, pickup_insert_after, delivery,
1914 delivery_insert_after, vehicle, priority_queue,
1915 pickup_to_entries, delivery_to_entries);
1917 pickup_insert_after =
Value(pickup_insert_after);
1923bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterPairInsertion(
1924 const absl::flat_hash_set<int>& pair_indices,
int vehicle, int64_t pickup,
1925 int64_t pickup_position, int64_t delivery, int64_t delivery_position,
1926 AdjustablePriorityQueue<PairEntry>* priority_queue,
1927 std::vector<PairEntries>* pickup_to_entries,
1928 std::vector<PairEntries>* delivery_to_entries) {
1931 const std::vector<PairEntry*> to_remove(
1932 delivery_to_entries->at(pickup).begin(),
1933 delivery_to_entries->at(pickup).end());
1934 for (PairEntry* pair_entry : to_remove) {
1935 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
1936 delivery_to_entries);
1938 DCHECK(pickup_to_entries->at(pickup).empty());
1939 DCHECK(pickup_to_entries->at(delivery).empty());
1940 DCHECK(delivery_to_entries->at(pickup).empty());
1941 DCHECK(delivery_to_entries->at(delivery).empty());
1944 if (!UpdateExistingPairEntriesOnChain(pickup_position,
Value(pickup_position),
1945 priority_queue, pickup_to_entries,
1946 delivery_to_entries) ||
1947 !UpdateExistingPairEntriesOnChain(
1948 delivery_position,
Value(delivery_position), priority_queue,
1949 pickup_to_entries, delivery_to_entries)) {
1955 if (!AddPairEntriesAfter(pair_indices, vehicle, pickup,
1957 priority_queue, pickup_to_entries,
1958 delivery_to_entries) ||
1959 !AddPairEntriesAfter(pair_indices, vehicle, delivery,
1961 priority_queue, pickup_to_entries,
1962 delivery_to_entries)) {
1965 SetVehicleIndex(pickup, vehicle);
1966 SetVehicleIndex(delivery, vehicle);
1970bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingPairEntriesOnChain(
1971 int64_t insert_after_start, int64_t insert_after_end,
1972 AdjustablePriorityQueue<
1973 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
1974 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1976 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
1977 delivery_to_entries) {
1978 int64_t insert_after = insert_after_start;
1979 while (insert_after != insert_after_end) {
1980 DCHECK(!
model()->IsEnd(insert_after));
1983 std::vector<PairEntry*> to_remove;
1984 for (
const PairEntries* pair_entries :
1985 {&pickup_to_entries->at(insert_after),
1986 &delivery_to_entries->at(insert_after)}) {
1987 if (StopSearchAndCleanup(priority_queue))
return false;
1988 for (PairEntry*
const pair_entry : *pair_entries) {
1989 DCHECK(priority_queue->
Contains(pair_entry));
1990 if (
Contains(pair_entry->pickup_to_insert()) ||
1991 Contains(pair_entry->delivery_to_insert())) {
1992 to_remove.push_back(pair_entry);
1994 DCHECK(pickup_to_entries->at(pair_entry->pickup_insert_after())
1995 .contains(pair_entry));
1996 DCHECK(delivery_to_entries->at(pair_entry->delivery_insert_after())
1997 .contains(pair_entry));
1998 UpdatePairEntry(pair_entry, priority_queue);
2002 for (PairEntry*
const pair_entry : to_remove) {
2003 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
2004 delivery_to_entries);
2006 insert_after =
Value(insert_after);
2011bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithPickupAfter(
2012 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
2013 int64_t insert_after, int64_t skip_entries_inserting_delivery_after,
2014 AdjustablePriorityQueue<
2015 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2016 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2018 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2019 delivery_to_entries) {
2020 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
2021 const int64_t pickup_insert_before =
Value(insert_after);
2022 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
2023 model()->GetPickupAndDeliveryPairs();
2024 DCHECK(pickup_to_entries->at(insert_after).empty());
2025 for (
const int64_t pickup :
2026 node_index_to_neighbors_by_cost_class_
2027 ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after)) {
2028 if (StopSearchAndCleanup(priority_queue))
return false;
2032 if (
const std::optional<RoutingModel::PickupDeliveryPosition> pickup_pos =
2033 model()->GetPickupPosition(pickup);
2034 pickup_pos.has_value()) {
2035 const int pair_index = pickup_pos->pd_pair_index;
2036 if (!pair_indices.contains(pair_index))
continue;
2037 for (
const int64_t delivery :
2038 pickup_delivery_pairs[pair_index].delivery_alternatives) {
2043 int64_t delivery_insert_after = pickup;
2044 while (!
model()->IsEnd(delivery_insert_after)) {
2045 if (delivery_insert_after != skip_entries_inserting_delivery_after) {
2046 AddPairEntry(pickup, insert_after, delivery, delivery_insert_after,
2047 vehicle, priority_queue, pickup_to_entries,
2048 delivery_to_entries);
2050 if (delivery_insert_after == pickup) {
2051 delivery_insert_after = pickup_insert_before;
2053 delivery_insert_after =
Value(delivery_insert_after);
2062bool GlobalCheapestInsertionFilteredHeuristic::AddPairEntriesWithDeliveryAfter(
2063 const absl::flat_hash_set<int>& pair_indices,
int vehicle,
2064 int64_t insert_after,
2065 AdjustablePriorityQueue<
2066 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2067 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2069 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2070 delivery_to_entries) {
2071 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
2072 const std::vector<PickupDeliveryPair>& pickup_delivery_pairs =
2073 model()->GetPickupAndDeliveryPairs();
2074 for (
const int64_t delivery :
2075 node_index_to_neighbors_by_cost_class_
2076 ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after)) {
2077 if (StopSearchAndCleanup(priority_queue))
return false;
2082 if (
const std::optional<RoutingModel::PickupDeliveryPosition> delivery_pos =
2083 model()->GetDeliveryPosition(delivery);
2084 delivery_pos.has_value()) {
2085 const int pair_index = delivery_pos->pd_pair_index;
2086 if (!pair_indices.contains(pair_index))
continue;
2087 for (
const int64_t pickup :
2088 pickup_delivery_pairs[pair_index].pickup_alternatives) {
2093 int64_t pickup_insert_after =
model()->Start(vehicle);
2094 while (pickup_insert_after != insert_after) {
2095 AddPairEntry(pickup, pickup_insert_after, delivery, insert_after,
2096 vehicle, priority_queue, pickup_to_entries,
2097 delivery_to_entries);
2098 pickup_insert_after =
Value(pickup_insert_after);
2106void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
2107 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
2108 AdjustablePriorityQueue<
2109 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2110 std::vector<PairEntries>* pickup_to_entries,
2111 std::vector<PairEntries>* delivery_to_entries) {
2112 priority_queue->
Remove(entry);
2113 if (entry->pickup_insert_after() != -1) {
2114 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
2116 if (entry->delivery_insert_after() != -1) {
2117 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
2119 pair_entry_allocator_.FreeEntry(entry);
2122void GlobalCheapestInsertionFilteredHeuristic::AddPairEntry(
2123 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
2124 int64_t delivery_insert_after,
int vehicle,
2125 AdjustablePriorityQueue<
2126 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
2127 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2129 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
2130 delivery_entries)
const {
2131 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
2132 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
2133 if (!pickup_vehicle_var->Contains(vehicle) ||
2134 !delivery_vehicle_var->Contains(vehicle)) {
2138 const auto vehicle_is_compatible = [pickup_vehicle_var,
2139 delivery_vehicle_var](
int vehicle) {
2140 return pickup_vehicle_var->Contains(vehicle) &&
2141 delivery_vehicle_var->Contains(vehicle);
2143 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2144 empty_vehicle_type_curator_->Type(vehicle),
2145 vehicle_is_compatible)) {
2149 const int num_allowed_vehicles =
2150 std::min(pickup_vehicle_var->Size(), delivery_vehicle_var->Size());
2151 if (pickup_insert_after == -1) {
2152 DCHECK_EQ(delivery_insert_after, -1);
2153 DCHECK_EQ(vehicle, -1);
2154 PairEntry* pair_entry = pair_entry_allocator_.NewEntry(
2155 pickup, -1, delivery, -1, -1, num_allowed_vehicles);
2156 pair_entry->set_value(
2157 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2161 priority_queue->
Add(pair_entry);
2165 PairEntry*
const pair_entry = pair_entry_allocator_.NewEntry(
2166 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle,
2167 num_allowed_vehicles);
2168 pair_entry->set_value(GetInsertionValueForPairAtPositions(
2169 pickup, pickup_insert_after, delivery, delivery_insert_after, vehicle));
2172 DCHECK(!priority_queue->
Contains(pair_entry));
2173 pickup_entries->at(pickup_insert_after).insert(pair_entry);
2174 delivery_entries->at(delivery_insert_after).insert(pair_entry);
2175 priority_queue->
Add(pair_entry);
2178void GlobalCheapestInsertionFilteredHeuristic::UpdatePairEntry(
2179 GlobalCheapestInsertionFilteredHeuristic::PairEntry*
const pair_entry,
2180 AdjustablePriorityQueue<
2181 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue)
2183 pair_entry->set_value(GetInsertionValueForPairAtPositions(
2184 pair_entry->pickup_to_insert(), pair_entry->pickup_insert_after(),
2185 pair_entry->delivery_to_insert(), pair_entry->delivery_insert_after(),
2186 pair_entry->vehicle()));
2189 DCHECK(priority_queue->
Contains(pair_entry));
2194GlobalCheapestInsertionFilteredHeuristic::GetInsertionValueForPairAtPositions(
2195 int64_t pickup, int64_t pickup_insert_after, int64_t delivery,
2196 int64_t delivery_insert_after,
int vehicle)
const {
2197 DCHECK_GE(pickup_insert_after, 0);
2198 const int64_t pickup_insert_before =
Value(pickup_insert_after);
2200 pickup, pickup_insert_after, pickup_insert_before, vehicle);
2202 DCHECK_GE(delivery_insert_after, 0);
2203 const int64_t delivery_insert_before = (delivery_insert_after == pickup)
2204 ? pickup_insert_before
2205 :
Value(delivery_insert_after);
2207 delivery, delivery_insert_after, delivery_insert_before, vehicle);
2209 const int64_t penalty_shift =
2210 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2213 return CapSub(
CapAdd(pickup_value, delivery_value), penalty_shift);
2216bool GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
2218 NodeEntryQueue* queue) {
2221 const int num_vehicles =
2222 vehicles.empty() ?
model()->vehicles() : vehicles.size();
2223 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
2225 for (
int node : nodes.PositionsSetAtLeastOnce()) {
2230 if (gci_params_.add_unperformed_entries &&
2232 AddNodeEntry(node, node, -1, all_vehicles, queue);
2235 InitializeInsertionEntriesPerformingNode(node, vehicles, queue);
2240void GlobalCheapestInsertionFilteredHeuristic::
2241 InitializeInsertionEntriesPerformingNode(
2242 int64_t node,
const absl::flat_hash_set<int>& vehicles,
2243 NodeEntryQueue* queue) {
2244 const int num_vehicles =
2245 vehicles.empty() ?
model()->vehicles() : vehicles.size();
2246 const bool all_vehicles = (num_vehicles ==
model()->vehicles());
2248 if (!gci_params_.use_neighbors_ratio_for_initialization) {
2249 auto vehicles_it = vehicles.begin();
2250 std::vector<NodeInsertion> insertions;
2256 for (
int v = 0; v < num_vehicles; v++) {
2257 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
2259 const int64_t start =
model()->Start(vehicle);
2260 if (all_vehicles && !IsCheapestClassRepresentative(vehicle))
continue;
2265 DCHECK_EQ(insertion.vehicle, vehicle);
2266 AddNodeEntry(node, insertion.insert_after, vehicle, all_vehicles,
2275 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
2276 int v,
int cost_class) {
2277 return (
model()->GetCostClassIndexOfVehicle(v).value() == cost_class) &&
2278 (all_vehicles || vehicles.contains(v));
2280 for (
int cost_class = 0; cost_class <
model()->GetCostClassesCount();
2282 for (
const int64_t insert_after :
2283 node_index_to_neighbors_by_cost_class_
2284 ->GetIncomingNeighborsOfNodeForCostClass(cost_class, node)) {
2288 const int vehicle = node_index_to_vehicle_[insert_after];
2289 if (vehicle == -1 ||
2290 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
2293 if (all_vehicles && !IsCheapestClassRepresentative(vehicle))
continue;
2294 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2299bool GlobalCheapestInsertionFilteredHeuristic::UpdateAfterNodeInsertion(
2301 int64_t insert_after,
bool all_vehicles, NodeEntryQueue* queue) {
2304 if (!UpdateExistingNodeEntriesOnChain(nodes, vehicle, insert_after,
2305 Value(insert_after), all_vehicles,
2317 if (!AddNodeEntriesAfter(nodes, vehicle, node, all_vehicles, queue)) {
2320 SetVehicleIndex(node, vehicle);
2324bool GlobalCheapestInsertionFilteredHeuristic::UpdateExistingNodeEntriesOnChain(
2326 int64_t insert_after_end,
bool all_vehicles, NodeEntryQueue* queue) {
2327 int64_t insert_after = insert_after_start;
2328 while (insert_after != insert_after_end) {
2329 DCHECK(!
model()->IsEnd(insert_after));
2330 AddNodeEntriesAfter(nodes, vehicle, insert_after, all_vehicles, queue);
2331 insert_after =
Value(insert_after);
2336bool GlobalCheapestInsertionFilteredHeuristic::AddNodeEntriesAfter(
2338 bool all_vehicles, NodeEntryQueue* queue) {
2339 const int cost_class =
model()->GetCostClassIndexOfVehicle(vehicle).value();
2342 queue->ClearInsertions(insert_after);
2343 const std::vector<int>& neighbors =
2344 node_index_to_neighbors_by_cost_class_
2345 ->GetOutgoingNeighborsOfNodeForCostClass(cost_class, insert_after);
2346 if (neighbors.size() < nodes.NumberOfSetCallsWithDifferentArguments()) {
2348 for (
int node : neighbors) {
2350 if (!
Contains(node) && nodes[node]) {
2351 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2356 for (
int node : nodes.PositionsSetAtLeastOnce()) {
2359 node_index_to_neighbors_by_cost_class_->IsNeighborhoodArcForCostClass(
2360 cost_class, insert_after, node)) {
2361 AddNodeEntry(node, insert_after, vehicle, all_vehicles, queue);
2368void GlobalCheapestInsertionFilteredHeuristic::AddNodeEntry(
2369 int64_t node, int64_t insert_after,
int vehicle,
bool all_vehicles,
2370 NodeEntryQueue* queue)
const {
2372 const int64_t penalty_shift =
2373 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
2376 const IntVar*
const vehicle_var =
model()->VehicleVar(node);
2377 if (!vehicle_var->Contains(vehicle)) {
2381 const auto vehicle_is_compatible = [vehicle_var](
int vehicle) {
2382 return vehicle_var->Contains(vehicle);
2384 if (!empty_vehicle_type_curator_->HasCompatibleVehicleOfType(
2385 empty_vehicle_type_curator_->Type(vehicle),
2386 vehicle_is_compatible)) {
2390 const int num_allowed_vehicles = vehicle_var->Size();
2391 if (vehicle == -1) {
2392 DCHECK_EQ(node, insert_after);
2393 if (!all_vehicles) {
2399 queue->PushInsertion(node, node, -1, num_allowed_vehicles,
2400 CapSub(node_penalty, penalty_shift));
2405 node, insert_after,
Value(insert_after), vehicle);
2406 if (!all_vehicles && insertion_cost > node_penalty) {
2413 queue->PushInsertion(node, insert_after, vehicle, num_allowed_vehicles,
2414 CapSub(insertion_cost, penalty_shift));
2418 int pickup,
int delivery,
int vehicle,
const std::vector<int>& path,
2419 const std::vector<bool>& path_node_is_pickup,
2420 const std::vector<bool>& path_node_is_delivery,
2422 const int num_nodes = path.size();
2423 DCHECK_GE(num_nodes, 2);
2424 const int kNoPrevIncrease = -1;
2425 const int kNoNextDecrease = num_nodes;
2427 prev_decrease_.resize(num_nodes - 1);
2428 prev_increase_.resize(num_nodes - 1);
2429 int prev_decrease = 0;
2430 int prev_increase = kNoPrevIncrease;
2431 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2432 if (path_node_is_delivery[pos]) prev_decrease = pos;
2433 prev_decrease_[pos] = prev_decrease;
2434 if (path_node_is_pickup[pos]) prev_increase = pos;
2435 prev_increase_[pos] = prev_increase;
2439 next_decrease_.resize(num_nodes - 1);
2440 next_increase_.resize(num_nodes - 1);
2441 int next_increase = num_nodes - 1;
2442 int next_decrease = kNoNextDecrease;
2443 for (
int pos = num_nodes - 2; pos >= 0; --pos) {
2444 next_decrease_[pos] = next_decrease;
2445 if (path_node_is_delivery[pos]) next_decrease = pos;
2446 next_increase_[pos] = next_increase;
2447 if (path_node_is_pickup[pos]) next_increase = pos;
2451 auto append = [pickup, delivery, vehicle, num_nodes, &path, &insertions](
2452 int pickup_pos,
int delivery_pos) {
2453 if (pickup_pos < 0 || num_nodes - 1 <= pickup_pos)
return;
2454 if (delivery_pos < 0 || num_nodes - 1 <= delivery_pos)
return;
2455 const int delivery_pred =
2456 pickup_pos == delivery_pos ? pickup : path[delivery_pos];
2458 vehicle, {{.pred = path[pickup_pos], .node = pickup},
2459 {.pred = delivery_pred, .node = delivery}});
2463 for (
int pos = 0; pos < num_nodes - 1; ++pos) {
2464 const bool is_after_decrease = prev_increase_[pos] < prev_decrease_[pos];
2465 const bool is_before_increase = next_increase_[pos] < next_decrease_[pos];
2466 if (is_after_decrease) {
2467 append(prev_increase_[pos], pos);
2468 if (is_before_increase) {
2469 append(pos, next_increase_[pos] - 1);
2470 append(pos, next_decrease_[pos] - 1);
2478 if (next_increase_[pos] - 1 != pos) {
2480 if (prev_decrease_[pos] != pos) append(prev_decrease_[pos], pos);
2484 append(pos, next_decrease_[pos] - 1);
2485 if (!is_before_increase && next_decrease_[pos] - 1 != pos) {
2494 if (prev_increase_[pos] != pos) append(prev_increase_[pos], pos);
2504 RoutingModel*
model, std::function<
bool()> stop_search,
2505 std::function<int64_t(int64_t, int64_t, int64_t)> evaluator,
2506 RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy,
2507 std::vector<RoutingSearchParameters::InsertionSortingProperty>
2508 insertion_sorting_properties,
2511 std::function<
bool(
const std::vector<RoutingModel::VariableValuePair>&,
2512 std::vector<RoutingModel::VariableValuePair>*)>
2513 optimize_on_insertion)
2515 std::move(evaluator), nullptr,
2517 pair_insertion_strategy_(pair_insertion_strategy),
2518 insertion_sorting_properties_(
std::move(insertion_sorting_properties)),
2519 use_first_solution_hint_(use_first_solution_hint),
2520 bin_capacities_(bin_capacities),
2521 optimize_on_insertion_(
std::move(optimize_on_insertion)) {
2522 DCHECK(!insertion_sorting_properties_.empty());
2528 synchronize_insertion_optimizer_ =
true;
2532 if (hint !=
nullptr && use_first_solution_hint_) {
2534 for (
int i = 0; i <
model()->Nexts().size(); ++i) {
2537 if (element !=
nullptr && element->
Bound()) {
2543 ComputeInsertionOrder();
2546bool LocalCheapestInsertionFilteredHeuristic::OptimizeOnInsertion(
2547 std::vector<int> delta_indices) {
2548 if (optimize_on_insertion_ ==
nullptr)
return false;
2549 std::vector<RoutingModel::VariableValuePair> in_state;
2550 if (synchronize_insertion_optimizer_) {
2551 for (
int i = 0; i <
model()->Nexts().size(); ++i) {
2553 in_state.push_back({i,
Value(i)});
2556 synchronize_insertion_optimizer_ =
false;
2559 in_state.push_back({index,
Value(index)});
2562 std::vector<RoutingModel::VariableValuePair> out_state;
2563 optimize_on_insertion_(in_state, &out_state);
2564 if (out_state.empty())
return false;
2565 for (
const auto& [var, value] : out_state) {
2575std::vector<std::vector<int64_t>> ComputeStartToPickupCosts(
2576 const RoutingModel& model, absl::Span<const int64_t> pickups,
2578 std::vector<std::vector<int64_t>> pickup_costs(model.Size());
2579 for (int64_t pickup : pickups) {
2580 std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2581 cost_from_start.resize(model.vehicles(), -1);
2583 ProcessVehicleStartEndCosts(
2585 [&cost_from_start](int64_t cost,
int v) { cost_from_start[v] = cost; },
2586 vehicle_set,
false,
true);
2588 return pickup_costs;
2592std::vector<std::vector<int64_t>> ComputeDeliveryToEndCosts(
2593 const RoutingModel& model, absl::Span<const int64_t> deliveries,
2595 std::vector<std::vector<int64_t>> delivery_costs(model.Size());
2596 for (int64_t delivery : deliveries) {
2597 std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2598 cost_to_end.resize(model.vehicles(), -1);
2600 ProcessVehicleStartEndCosts(
2602 [&cost_to_end](int64_t cost,
int v) { cost_to_end[v] = cost; },
2603 vehicle_set,
true,
false);
2605 return delivery_costs;
2612int64_t GetNegMaxDistanceFromVehicles(
const RoutingModel& model,
2614 const auto& [pickups, deliveries] =
2615 model.GetPickupAndDeliveryPairs()[pair_index];
2618 for (
int v = 0; v < model.vehicles(); ++v) vehicle_set.Set(v);
2621 const std::vector<std::vector<int64_t>> pickup_costs =
2622 ComputeStartToPickupCosts(model, pickups, vehicle_set);
2625 const std::vector<std::vector<int64_t>> delivery_costs =
2626 ComputeDeliveryToEndCosts(model, deliveries, vehicle_set);
2628 int64_t max_pair_distance = 0;
2629 for (int64_t pickup : pickups) {
2630 const std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2631 for (int64_t delivery : deliveries) {
2632 const std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2633 int64_t closest_vehicle_distance = std::numeric_limits<int64_t>::max();
2634 for (
int v = 0; v < model.vehicles(); v++) {
2635 if (cost_from_start[v] < 0 || cost_to_end[v] < 0) {
2639 closest_vehicle_distance =
2640 std::min(closest_vehicle_distance,
2641 CapAdd(cost_from_start[v], cost_to_end[v]));
2643 max_pair_distance = std::max(max_pair_distance, closest_vehicle_distance);
2646 return CapOpp(max_pair_distance);
2654std::optional<int64_t> GetAvgPickupDeliveryPairDistanceFromVehicles(
2655 const RoutingModel& model,
int pair_index,
2657 const auto& [pickups, deliveries] =
2658 model.GetPickupAndDeliveryPairs()[pair_index];
2661 const std::vector<std::vector<int64_t>> pickup_costs =
2662 ComputeStartToPickupCosts(model, pickups, vehicle_set);
2665 const std::vector<std::vector<int64_t>> delivery_costs =
2666 ComputeDeliveryToEndCosts(model, deliveries, vehicle_set);
2668 int64_t avg_pair_distance_sum = 0;
2669 int valid_pairs = 0;
2670 for (int64_t pickup : pickups) {
2671 const std::vector<int64_t>& cost_from_start = pickup_costs[pickup];
2672 for (int64_t delivery : deliveries) {
2673 const std::vector<int64_t>& cost_to_end = delivery_costs[delivery];
2674 int64_t distance_from_vehicle_sum = 0;
2675 int valid_vehicles = 0;
2676 for (
int v = 0; v < model.vehicles(); v++) {
2677 if (cost_from_start[v] < 0 || cost_to_end[v] < 0) {
2681 distance_from_vehicle_sum =
2682 CapAdd(distance_from_vehicle_sum,
2683 CapAdd(cost_from_start[v], cost_to_end[v]));
2686 if (valid_vehicles > 0) {
2687 avg_pair_distance_sum =
CapAdd(
2688 distance_from_vehicle_sum / valid_vehicles, avg_pair_distance_sum);
2693 return valid_pairs > 0
2694 ? std::make_optional<int64_t>(avg_pair_distance_sum / valid_pairs)
2700int64_t GetMaxFiniteDimensionCapacity(
2701 const RoutingModel& model,
2702 const std::vector<RoutingDimension*>& dimensions) {
2703 int64_t max_capacity = 0;
2704 for (
const RoutingDimension* dimension : dimensions) {
2705 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
2706 const int64_t capacity = dimension->vehicle_capacities()[vehicle];
2708 max_capacity = std::max(max_capacity, capacity);
2711 return max_capacity;
2716int64_t GetAvgNodeUnaryDimensionUsage(
2717 const RoutingModel& model,
const std::vector<RoutingDimension*>& dimensions,
2718 int64_t max_vehicle_capacity, int64_t node) {
2719 if (dimensions.empty() || max_vehicle_capacity == 0) {
2723 double dimension_usage_sum = 0;
2724 for (
const RoutingDimension* dimension : dimensions) {
2726 DCHECK(dimension->IsUnary());
2727 double dimension_usage_per_vehicle_sum = 0;
2728 int valid_vehicles = 0;
2731 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
2732 const int64_t capacity = dimension->vehicle_capacities()[vehicle];
2733 if (capacity == 0)
continue;
2734 DCHECK_GE(capacity, 0);
2739 const RoutingModel::TransitCallback1& transit_evaluator =
2740 dimension->GetUnaryTransitEvaluator(vehicle);
2741 dimension_usage_per_vehicle_sum +=
2742 1.0 * std::abs(transit_evaluator(node)) / capacity;
2745 if (valid_vehicles > 0) {
2748 dimension_usage_sum +=
2749 std::min(1.0, dimension_usage_per_vehicle_sum / valid_vehicles);
2756 dimension_usage_sum);
2761int64_t GetAvgPickupDeliveryPairUnaryDimensionUsage(
2762 const RoutingModel& model,
const std::vector<RoutingDimension*>& dimensions,
2763 int64_t max_vehicle_capacity,
int pair_index) {
2764 if (dimensions.empty()) {
2768 const auto& [pickups, deliveries] =
2769 model.GetPickupAndDeliveryPairs()[pair_index];
2770 if (pickups.empty() || deliveries.empty()) {
2774 double dimension_usage_sum = 0;
2775 for (
const RoutingDimension* dimension : dimensions) {
2776 double dimension_usage_per_vehicle_sum = 0;
2777 int valid_vehicles = 0;
2779 for (
int vehicle = 0; vehicle < model.vehicles(); ++vehicle) {
2780 const int64_t capacity = dimension->vehicle_capacities()[vehicle];
2781 if (capacity == 0)
continue;
2786 const RoutingModel::TransitCallback1& transit_evaluator =
2787 dimension->GetUnaryTransitEvaluator(vehicle);
2789 double pickup_sum = 0;
2790 for (int64_t pickup : pickups) {
2791 pickup_sum += 1.0 * std::abs(transit_evaluator(pickup)) / capacity;
2793 const double avg_pickup_usage = pickup_sum / pickups.size();
2794 double delivery_sum = 0;
2795 for (int64_t delivery : deliveries) {
2796 delivery_sum += 1.0 * std::abs(transit_evaluator(delivery)) / capacity;
2798 const double avg_delivery_usage = delivery_sum / deliveries.size();
2799 dimension_usage_per_vehicle_sum +=
2800 std::max(avg_pickup_usage, avg_delivery_usage);
2803 if (valid_vehicles > 0) {
2806 dimension_usage_sum +=
2807 std::min(1.0, dimension_usage_per_vehicle_sum / valid_vehicles);
2814 dimension_usage_sum);
2819void LocalCheapestInsertionFilteredHeuristic::ComputeInsertionOrder() {
2820 if (!insertion_order_.empty())
return;
2829 insertion_order_.reserve(
model.Size() +
2830 model.GetPickupAndDeliveryPairs().size());
2832 auto get_insertion_properties = [
this](int64_t penalty,
2833 int64_t num_allowed_vehicles,
2834 int64_t avg_distance_to_vehicle,
2835 int64_t neg_min_distance_to_vehicles,
2837 int reversed_hint_weight,
2838 int64_t avg_dimension_usage) {
2839 DCHECK_NE(0, num_allowed_vehicles);
2840 absl::InlinedVector<int64_t, 8> properties;
2841 properties.reserve(insertion_sorting_properties_.size());
2847 properties.push_back(-hint_weight);
2848 properties.push_back(-reversed_hint_weight);
2850 bool neg_min_distance_to_vehicles_appended =
false;
2851 for (
const int property : insertion_sorting_properties_) {
2853 case RoutingSearchParameters::SORTING_PROPERTY_ALLOWED_VEHICLES:
2854 properties.push_back(num_allowed_vehicles);
2856 case RoutingSearchParameters::SORTING_PROPERTY_PENALTY:
2857 properties.push_back(
CapOpp(penalty));
2859 case RoutingSearchParameters::
2860 SORTING_PROPERTY_PENALTY_OVER_ALLOWED_VEHICLES_RATIO:
2861 properties.push_back(
CapOpp(penalty / num_allowed_vehicles));
2863 case RoutingSearchParameters::
2864 SORTING_PROPERTY_HIGHEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS:
2865 properties.push_back(
CapOpp(avg_distance_to_vehicle));
2867 case RoutingSearchParameters::
2868 SORTING_PROPERTY_LOWEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS:
2869 properties.push_back(avg_distance_to_vehicle);
2871 case RoutingSearchParameters::
2872 SORTING_PROPERTY_LOWEST_MIN_ARC_COST_TO_VEHICLE_START_ENDS:
2873 properties.push_back(neg_min_distance_to_vehicles);
2874 neg_min_distance_to_vehicles_appended =
true;
2876 case RoutingSearchParameters::SORTING_PROPERTY_HIGHEST_DIMENSION_USAGE:
2877 properties.push_back(
CapOpp(avg_dimension_usage));
2881 <<
"Unknown RoutingSearchParameter::InsertionSortingProperty "
2891 if (!neg_min_distance_to_vehicles_appended) {
2892 properties.push_back(neg_min_distance_to_vehicles);
2900 bool compute_avg_pickup_delivery_pair_distance_from_vehicles =
false;
2901 bool compute_avg_dimension_usage =
false;
2902 for (
const RoutingSearchParameters::InsertionSortingProperty property :
2903 insertion_sorting_properties_) {
2905 RoutingSearchParameters::
2906 SORTING_PROPERTY_HIGHEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS ||
2908 RoutingSearchParameters::
2909 SORTING_PROPERTY_LOWEST_AVG_ARC_COST_TO_VEHICLE_START_ENDS) {
2910 compute_avg_pickup_delivery_pair_distance_from_vehicles =
true;
2913 RoutingSearchParameters::SORTING_PROPERTY_HIGHEST_DIMENSION_USAGE) {
2914 compute_avg_dimension_usage =
true;
2918 Bitset64<int> vehicle_set(
model.vehicles());
2919 for (
int v = 0; v <
model.vehicles(); ++v) vehicle_set.Set(v);
2921 const std::vector<RoutingDimension*> unary_dimensions =
2922 compute_avg_dimension_usage ?
model.GetUnaryDimensions()
2923 : std::vector<RoutingDimension*>();
2925 const int64_t max_dimension_capacity =
2926 compute_avg_dimension_usage
2927 ? GetMaxFiniteDimensionCapacity(
model, unary_dimensions)
2931 const std::vector<PickupDeliveryPair>& pairs =
2932 model.GetPickupAndDeliveryPairs();
2934 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
2935 const auto& [pickups, deliveries] = pairs[pair_index];
2936 int64_t num_allowed_vehicles = std::numeric_limits<int64_t>::max();
2937 int64_t pickup_penalty = 0;
2938 int hint_weight = 0;
2939 int reversed_hint_weight = 0;
2940 for (int64_t pickup : pickups) {
2941 num_allowed_vehicles =
2942 std::min(num_allowed_vehicles,
2943 static_cast<int64_t
>(
model.VehicleVar(pickup)->Size()));
2945 std::max(pickup_penalty,
model.UnperformedPenalty(pickup));
2949 int64_t delivery_penalty = 0;
2950 for (int64_t delivery : deliveries) {
2951 num_allowed_vehicles =
2952 std::min(num_allowed_vehicles,
2953 static_cast<int64_t
>(
model.VehicleVar(delivery)->Size()));
2955 std::max(delivery_penalty,
model.UnperformedPenalty(delivery));
2960 const std::optional<int64_t> maybe_avg_pair_to_vehicle_cost =
2961 compute_avg_pickup_delivery_pair_distance_from_vehicles
2962 ? GetAvgPickupDeliveryPairDistanceFromVehicles(
model, pair_index,
2964 : std::make_optional<int64_t>(0);
2966 if (!maybe_avg_pair_to_vehicle_cost.has_value()) {
2972 const int64_t avg_pair_dimension_usage =
2973 compute_avg_dimension_usage
2974 ? GetAvgPickupDeliveryPairUnaryDimensionUsage(
2975 model, unary_dimensions, max_dimension_capacity, pair_index)
2978 absl::InlinedVector<int64_t, 8> properties = get_insertion_properties(
2979 CapAdd(pickup_penalty, delivery_penalty), num_allowed_vehicles,
2980 maybe_avg_pair_to_vehicle_cost.value(),
2981 GetNegMaxDistanceFromVehicles(
model, pair_index), hint_weight,
2982 reversed_hint_weight, avg_pair_dimension_usage);
2984 insertion_order_.push_back({.properties = std::move(properties),
2986 .is_node_index =
false,
2987 .index = pair_index});
2990 for (
int node = 0; node <
model.Size(); ++node) {
2991 if (
model.IsStart(node) ||
model.IsEnd(node))
continue;
2993 int64_t min_distance = std::numeric_limits<int64_t>::max();
2994 int64_t sum_distance = 0;
2995 ProcessVehicleStartEndCosts(
2997 [&min_distance, &sum_distance](int64_t dist,
int) {
2998 min_distance = std::min(min_distance, dist);
2999 sum_distance =
CapAdd(dist, sum_distance);
3002 DCHECK_GT(vehicle_set.size(), 0);
3004 const int64_t avg_dimension_usage =
3005 compute_avg_dimension_usage
3006 ? GetAvgNodeUnaryDimensionUsage(
model, unary_dimensions,
3007 max_dimension_capacity, node)
3010 absl::InlinedVector<int64_t, 8> properties = get_insertion_properties(
3011 model.UnperformedPenalty(node),
model.VehicleVar(node)->Size(),
3012 sum_distance / vehicle_set.size(),
CapOpp(min_distance),
3014 insertion_order_.push_back({.properties = std::move(properties),
3016 .is_node_index =
true,
3020 absl::c_sort(insertion_order_, std::greater<Seed>());
3021 absl::c_reverse(insertion_order_);
3024bool LocalCheapestInsertionFilteredHeuristic::InsertPair(
3025 int64_t pickup, int64_t insert_pickup_after, int64_t delivery,
3026 int64_t insert_delivery_after,
int vehicle) {
3027 const int64_t insert_pickup_before =
Value(insert_pickup_after);
3028 InsertBetween(pickup, insert_pickup_after, insert_pickup_before, vehicle);
3029 DCHECK_NE(insert_delivery_after, insert_pickup_after);
3030 const int64_t insert_delivery_before = (insert_delivery_after == pickup)
3031 ? insert_pickup_before
3032 :
Value(insert_delivery_after);
3033 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
3037 if (
Evaluate(
true,
true).has_value()) {
3038 OptimizeOnInsertion(std::move(indices));
3044void LocalCheapestInsertionFilteredHeuristic::InsertBestPickupThenDelivery(
3046 for (
int pickup : index_pair.pickup_alternatives) {
3047 std::vector<NodeInsertion> pickup_insertions =
3048 ComputeEvaluatorSortedPositions(pickup);
3049 for (
int delivery : index_pair.delivery_alternatives) {
3051 for (
const NodeInsertion& pickup_insertion : pickup_insertions) {
3052 const int vehicle = pickup_insertion.vehicle;
3053 if (!
model()->VehicleVar(delivery)->
Contains(vehicle))
continue;
3054 if (MustUpdateBinCapacities() &&
3055 !bin_capacities_->CheckAdditionsFeasibility({pickup, delivery},
3060 ComputeEvaluatorSortedPositionsOnRouteAfter(
3061 delivery, pickup,
Value(pickup_insertion.insert_after),
3063 if (InsertPair(pickup, pickup_insertion.insert_after, delivery,
3064 delivery_insertion.insert_after, vehicle)) {
3065 if (MustUpdateBinCapacities()) {
3066 bin_capacities_->AddItemToBin(pickup, vehicle);
3067 bin_capacities_->AddItemToBin(delivery, vehicle);
3078void LocalCheapestInsertionFilteredHeuristic::InsertBestPair(
3080 for (
int pickup : pair.pickup_alternatives) {
3081 for (
int delivery : pair.delivery_alternatives) {
3083 std::vector<PickupDeliveryInsertion> sorted_pair_positions =
3084 ComputeEvaluatorSortedPairPositions(pickup, delivery);
3085 if (sorted_pair_positions.empty())
continue;
3086 for (
const auto [insert_pickup_after, insert_delivery_after,
3087 unused_hint_weight, unused_value, vehicle] :
3088 sorted_pair_positions) {
3089 if (InsertPair(pickup, insert_pickup_after, delivery,
3090 insert_delivery_after, vehicle)) {
3091 if (MustUpdateBinCapacities()) {
3092 bin_capacities_->AddItemToBin(pickup, vehicle);
3093 bin_capacities_->AddItemToBin(delivery, vehicle);
3103void LocalCheapestInsertionFilteredHeuristic::InsertBestPairMultitour(
3105 using InsertionSequence = InsertionSequenceContainer::InsertionSequence;
3106 using Insertion = InsertionSequenceContainer::Insertion;
3108 std::vector<int> path;
3109 std::vector<bool> path_node_is_pickup;
3110 std::vector<bool> path_node_is_delivery;
3112 auto fill_path = [&path, &path_node_is_pickup, &path_node_is_delivery,
3113 this](
int vehicle) {
3115 path_node_is_pickup.clear();
3116 path_node_is_delivery.clear();
3117 const int start =
model()->Start(vehicle);
3118 const int end =
model()->End(vehicle);
3119 for (
int node = start; node != end; node =
Value(node)) {
3120 path.push_back(node);
3121 path_node_is_pickup.push_back(
model()->IsPickup(node));
3122 path_node_is_delivery.push_back(
model()->IsDelivery(node));
3124 path.push_back(end);
3128 auto price_insertion_sequences_evaluator =
3129 [
this](BinCapacities* bin_capacities) {
3130 for (InsertionSequence sequence : insertion_container_) {
3131 int64_t sequence_cost = 0;
3132 int previous_node = -1;
3133 int previous_succ = -1;
3134 int hint_weight = 0;
3135 for (
const Insertion& insertion : sequence) {
3136 const int succ = previous_node == insertion.pred
3138 :
Value(insertion.pred);
3139 hint_weight +=
IsHint(insertion.pred, insertion.node);
3140 hint_weight +=
IsHint(insertion.node, succ);
3142 insertion.node, insertion.pred, succ, sequence.Vehicle());
3144 previous_node = insertion.node;
3145 previous_succ = succ;
3147 sequence.Cost() = sequence_cost;
3148 sequence.SetHintWeight(hint_weight);
3150 if (bin_capacities ==
nullptr)
return;
3151 for (InsertionSequence sequence : insertion_container_) {
3152 const int64_t old_cost = bin_capacities->
TotalCost();
3153 for (
const Insertion& insertion : sequence) {
3154 bin_capacities->
AddItemToBin(insertion.node, sequence.Vehicle());
3156 const int64_t new_cost = bin_capacities->
TotalCost();
3157 const int64_t delta_cost =
CapSub(new_cost, old_cost);
3158 CapAddTo(delta_cost, &sequence.Cost());
3159 for (
const Insertion& insertion : sequence) {
3161 sequence.Vehicle());
3166 auto price_insertion_sequences_no_evaluator = [
this]() {
3167 for (InsertionSequence sequence : insertion_container_) {
3168 int previous_node = -1;
3169 int previous_succ = -1;
3170 int hint_weight = 0;
3171 for (
const Insertion& insertion : sequence) {
3172 const int succ = previous_node == insertion.pred
3174 :
Value(insertion.pred);
3175 hint_weight +=
IsHint(insertion.pred, insertion.node);
3176 hint_weight +=
IsHint(insertion.node, succ);
3177 InsertBetween(insertion.node, insertion.pred, succ, sequence.Vehicle());
3178 previous_node = insertion.node;
3179 previous_succ = succ;
3184 sequence.SetHintWeight(hint_weight);
3188 for (
int pickup : pair.pickup_alternatives) {
3189 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
3191 for (
int delivery : pair.delivery_alternatives) {
3192 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
3193 insertion_container_.Clear();
3194 std::unique_ptr<IntVarIterator> pickup_vehicles(
3195 pickup_vehicle_var->MakeDomainIterator(
false));
3196 for (
const int vehicle : InitAndGetValues(pickup_vehicles.get())) {
3197 if (vehicle == -1)
continue;
3198 if (!delivery_vehicle_var->Contains(vehicle))
continue;
3199 if (MustUpdateBinCapacities() &&
3200 !bin_capacities_->CheckAdditionsFeasibility({pickup, delivery},
3205 insertion_generator_.AppendPickupDeliveryMultitourInsertions(
3206 pickup, delivery, vehicle, path, path_node_is_pickup,
3207 path_node_is_delivery, insertion_container_);
3211 price_insertion_sequences_no_evaluator();
3213 price_insertion_sequences_evaluator(bin_capacities_);
3216 insertion_container_.RemoveIf(
3217 [](
const InsertionSequence& sequence) ->
bool {
3220 insertion_container_.Sort();
3221 for (InsertionSequence sequence : insertion_container_) {
3223 int previous_node = -1;
3224 int previous_succ = -1;
3225 const int vehicle = sequence.Vehicle();
3226 for (
const Insertion& insertion : sequence) {
3227 const int succ = previous_node == insertion.pred
3229 :
Value(insertion.pred);
3230 InsertBetween(insertion.node, insertion.pred, succ, vehicle);
3231 previous_node = insertion.node;
3232 previous_succ = succ;
3237 if (MustUpdateBinCapacities()) {
3238 bin_capacities_->AddItemToBin(pickup, vehicle);
3239 bin_capacities_->AddItemToBin(delivery, vehicle);
3250 std::vector<bool>* data) {
3251 for (
const int64_t pickup : pair.pickup_alternatives) {
3252 data->at(pickup) =
false;
3254 for (
const int64_t delivery : pair.delivery_alternatives) {
3255 data->at(delivery) =
false;
3264 if (MustUpdateBinCapacities()) {
3265 bin_capacities_->ClearItems();
3266 for (
int vehicle = 0; vehicle <
model.vehicles(); ++vehicle) {
3267 const int start =
Value(
model.Start(vehicle));
3268 for (
int node = start; !
model.IsEnd(node); node =
Value(node)) {
3269 bin_capacities_->AddItemToBin(node, vehicle);
3274 const std::vector<PickupDeliveryPair>& pairs =
3275 model.GetPickupAndDeliveryPairs();
3276 std::vector<bool> ignore_pair_index(pairs.size(),
false);
3277 std::vector<bool> insert_as_single_node(
model.Size(),
true);
3278 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
3279 const auto& [pickups, deliveries] = pairs[pair_index];
3280 bool pickup_contained =
false;
3281 for (int64_t pickup : pickups) {
3283 pickup_contained =
true;
3287 bool delivery_contained =
false;
3288 for (int64_t delivery : deliveries) {
3290 delivery_contained =
true;
3294 ignore_pair_index[pair_index] = pickup_contained || delivery_contained;
3295 if (pickup_contained == delivery_contained) {
3300 SetFalseForAllAlternatives(pairs[pair_index], &insert_as_single_node);
3304 for (
const Seed& seed : insertion_order_) {
3305 const int index = seed.index;
3306 if (!seed.is_node_index) {
3307 if (ignore_pair_index[index])
continue;
3309 const auto& pair = pairs[index];
3310 switch (pair_insertion_strategy_) {
3311 case RoutingSearchParameters::AUTOMATIC:
3312 case RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR:
3313 InsertBestPair(pair);
3315 case RoutingSearchParameters::BEST_PICKUP_THEN_BEST_DELIVERY:
3316 InsertBestPickupThenDelivery(pair);
3318 case RoutingSearchParameters::BEST_PICKUP_DELIVERY_PAIR_MULTITOUR:
3319 InsertBestPairMultitour(pair);
3322 LOG(ERROR) <<
"Unknown pair insertion strategy value.";
3329 if (
Contains(index) || !insert_as_single_node[index]) {
3333 ComputeEvaluatorSortedPositions(index)) {
3338 Value(insertion.insert_after), insertion.vehicle);
3343 if (MustUpdateBinCapacities()) {
3344 bin_capacities_->AddItemToBin(index, insertion.vehicle);
3346 OptimizeOnInsertion(std::move(indices));
3355std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
3356LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
3359 const int size =
model()->Size();
3360 if (node >= size)
return {};
3361 std::vector<NodeInsertion> sorted_insertions;
3362 const IntVar* vehicle_var =
model()->VehicleVar(node);
3363 std::unique_ptr<IntVarIterator> node_vehicles(
3364 vehicle_var->MakeDomainIterator(
false));
3365 for (
const int vehicle : InitAndGetValues(node_vehicles.get())) {
3366 if (vehicle == -1)
continue;
3367 if (MustUpdateBinCapacities() &&
3371 const int64_t start =
model()->Start(vehicle);
3372 const size_t old_num_insertions = sorted_insertions.size();
3374 false, &sorted_insertions);
3375 if (MustUpdateBinCapacities() &&
evaluator_) {
3377 const int64_t old_cost = bin_capacities_->TotalCost();
3378 bin_capacities_->AddItemToBin(node, vehicle);
3379 const int64_t new_cost = bin_capacities_->TotalCost();
3380 bin_capacities_->RemoveItemFromBin(node, vehicle);
3381 const int64_t delta_cost =
CapSub(new_cost, old_cost);
3383 for (
size_t i = old_num_insertions;
i < sorted_insertions.size(); ++
i) {
3384 CapAddTo(delta_cost, &sorted_insertions[i].value);
3388 absl::c_sort(sorted_insertions);
3389 return sorted_insertions;
3392std::vector<LocalCheapestInsertionFilteredHeuristic::NodeInsertion>
3393LocalCheapestInsertionFilteredHeuristic::
3394 ComputeEvaluatorSortedPositionsOnRouteAfter(int64_t node, int64_t start,
3395 int64_t next_after_start,
3398 const int size =
model()->Size();
3399 if (node >= size)
return {};
3400 std::vector<NodeInsertion> sorted_insertions;
3402 false, &sorted_insertions);
3403 absl::c_sort(sorted_insertions);
3404 return sorted_insertions;
3407std::vector<PickupDeliveryInsertion>
3408LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPairPositions(
3409 int pickup,
int delivery) {
3410 std::vector<PickupDeliveryInsertion> sorted_pickup_delivery_insertions;
3411 const int size =
model()->Size();
3412 DCHECK_LT(pickup, size);
3413 DCHECK_LT(delivery, size);
3414 const IntVar* pickup_vehicle_var =
model()->VehicleVar(pickup);
3415 const IntVar* delivery_vehicle_var =
model()->VehicleVar(delivery);
3416 std::unique_ptr<IntVarIterator> pickup_vehicles(
3417 pickup_vehicle_var->MakeDomainIterator(
false));
3418 for (
const int vehicle : InitAndGetValues(pickup_vehicles.get())) {
3419 if (vehicle == -1)
continue;
3420 if (!delivery_vehicle_var->Contains(vehicle))
continue;
3421 if (MustUpdateBinCapacities() &&
3422 !bin_capacities_->CheckAdditionsFeasibility({pickup, delivery},
3426 int64_t insert_pickup_after =
model()->Start(vehicle);
3427 while (!
model()->IsEnd(insert_pickup_after)) {
3428 const int64_t insert_pickup_before =
Value(insert_pickup_after);
3429 int64_t insert_delivery_after = pickup;
3430 while (!
model()->IsEnd(insert_delivery_after)) {
3432 const int64_t insert_delivery_before =
3433 insert_delivery_after == pickup ? insert_pickup_before
3434 :
Value(insert_delivery_after);
3435 const int hint_weight =
IsHint(insert_pickup_after, pickup) +
3436 IsHint(insert_delivery_after, delivery) +
3437 IsHint(pickup, insert_pickup_before) +
3438 IsHint(delivery, insert_delivery_before);
3440 InsertBetween(pickup, insert_pickup_after, insert_pickup_before,
3442 InsertBetween(delivery, insert_delivery_after, insert_delivery_before,
3444 std::optional<int64_t> insertion_cost =
Evaluate(
3445 false, hint_weight > 0);
3446 if (insertion_cost.has_value()) {
3447 sorted_pickup_delivery_insertions.push_back(
3448 {insert_pickup_after, insert_delivery_after, -hint_weight,
3449 *insertion_cost, vehicle});
3453 pickup, insert_pickup_after, insert_pickup_before, vehicle);
3455 delivery, insert_delivery_after, insert_delivery_before, vehicle);
3456 int64_t total_cost =
CapAdd(pickup_cost, delivery_cost);
3457 if (MustUpdateBinCapacities()) {
3458 const int64_t old_cost = bin_capacities_->TotalCost();
3459 bin_capacities_->AddItemToBin(pickup, vehicle);
3460 bin_capacities_->AddItemToBin(delivery, vehicle);
3461 const int64_t new_cost = bin_capacities_->TotalCost();
3463 bin_capacities_->RemoveItemFromBin(pickup, vehicle);
3464 bin_capacities_->RemoveItemFromBin(delivery, vehicle);
3466 sorted_pickup_delivery_insertions.push_back(
3467 {insert_pickup_after, insert_delivery_after, -hint_weight,
3468 total_cost, vehicle});
3470 insert_delivery_after = insert_delivery_before;
3472 insert_pickup_after = insert_pickup_before;
3475 absl::c_sort(sorted_pickup_delivery_insertions);
3476 return sorted_pickup_delivery_insertions;
3482 RoutingModel*
model, std::function<
bool()> stop_search,
3488 const int num_nexts =
model()->Nexts().size();
3489 std::vector<std::vector<int64_t>> deliveries(num_nexts);
3490 std::vector<std::vector<int64_t>> pickups(num_nexts);
3491 for (
const auto& [pickup_alternatives, delivery_alternatives] :
3492 model()->GetPickupAndDeliveryPairs()) {
3493 for (
int pickup : pickup_alternatives) {
3494 for (
int delivery : delivery_alternatives) {
3495 deliveries[pickup].push_back(delivery);
3496 pickups[delivery].push_back(pickup);
3503 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
3504 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
3505 sorted_vehicles[vehicle] = vehicle;
3507 absl::c_sort(sorted_vehicles,
3508 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
3510 for (
const int vehicle : sorted_vehicles) {
3512 bool extend_route =
true;
3517 while (extend_route) {
3518 extend_route =
false;
3520 int64_t index = last_node;
3527 while (found && !
model()->IsEnd(index)) {
3529 std::vector<int64_t> neighbors;
3530 if (index <
model()->Nexts().size()) {
3531 std::unique_ptr<IntVarIterator> it(
3532 model()->Nexts()[index]->MakeDomainIterator(
false));
3534 neighbors = GetPossibleNextsFromIterator(index, next_values.begin(),
3537 for (
int i = 0; !found && i < neighbors.size(); ++i) {
3541 next = FindTopSuccessor(index, neighbors);
3544 SortSuccessors(index, &neighbors);
3545 ABSL_FALLTHROUGH_INTENDED;
3547 next = neighbors[i];
3549 if (
model()->IsEnd(next) && next != end) {
3553 if (!
model()->IsEnd(next) && !pickups[next].empty()) {
3554 bool contains_pickups =
false;
3555 for (int64_t pickup : pickups[next]) {
3557 contains_pickups =
true;
3561 if (!contains_pickups) {
3565 std::vector<int64_t> next_deliveries;
3566 if (next < deliveries.size()) {
3567 next_deliveries = GetPossibleNextsFromIterator(
3568 next, deliveries[next].begin(), deliveries[next].end());
3570 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
3571 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
3576 delivery = FindTopSuccessor(next, next_deliveries);
3579 SortSuccessors(next, &next_deliveries);
3580 ABSL_FALLTHROUGH_INTENDED;
3582 delivery = next_deliveries[j];
3586 SetNext(index, next, vehicle);
3587 if (!
model()->IsEnd(next)) {
3591 SetNext(next, delivery, vehicle);
3592 SetNext(delivery, end, vehicle);
3600 if (
model()->IsEnd(end) && last_node != delivery) {
3601 last_node = delivery;
3602 extend_route =
true;
3617bool CheapestAdditionFilteredHeuristic::
3618 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
3619 int vehicle2)
const {
3620 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
3621 builder_.GetStartChainEnd(vehicle1));
3622 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
3623 builder_.GetStartChainEnd(vehicle2));
3624 if (has_partial_route1 == has_partial_route2) {
3625 return vehicle2 < vehicle1;
3627 return has_partial_route2 < has_partial_route1;
3635 RoutingModel*
model, std::function<
bool()> stop_search,
3636 std::function<int64_t(int64_t, int64_t)> evaluator,
3640 evaluator_(
std::move(evaluator)) {}
3642int64_t EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
3643 int64_t node,
const std::vector<int64_t>& successors) {
3644 int64_t best_evaluation = std::numeric_limits<int64_t>::max();
3645 int64_t best_successor = -1;
3646 for (int64_t successor : successors) {
3647 const int64_t evaluation = (successor >= 0)
3648 ? evaluator_(node, successor)
3649 : std::numeric_limits<int64_t>::max();
3650 if (evaluation < best_evaluation ||
3651 (evaluation == best_evaluation && successor > best_successor)) {
3652 best_evaluation = evaluation;
3653 best_successor = successor;
3656 return best_successor;
3659void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
3660 int64_t node, std::vector<int64_t>* successors) {
3661 std::vector<std::pair<int64_t, int64_t>> values;
3662 values.reserve(successors->size());
3663 for (int64_t successor : *successors) {
3666 values.push_back({evaluator_(node, successor), successor});
3668 absl::c_sort(values, [](
const std::pair<int64_t, int64_t>& s1,
3669 const std::pair<int64_t, int64_t>& s2) {
3670 return s1.first < s2.first ||
3671 (s1.first == s2.first && s1.second > s2.second);
3673 successors->clear();
3674 for (
auto value : values) {
3675 successors->push_back(value.second);
3683 RoutingModel*
model, std::function<
bool()> stop_search,
3688 comparator_(
std::move(comparator)) {}
3690int64_t ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
3691 int64_t node,
const std::vector<int64_t>& successors) {
3692 return *absl::c_min_element(
3693 successors, [
this, node](
int successor1,
int successor2) {
3694 return comparator_(node, successor1, successor2);
3698void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
3699 int64_t node, std::vector<int64_t>* successors) {
3700 absl::c_sort(*successors, [
this, node](
int successor1,
int successor2) {
3701 return comparator_(node, successor1, successor2);
3753template <
typename Saving>
3758 : savings_db_(savings_db),
3759 index_in_sorted_savings_(0),
3760 vehicle_types_(vehicle_types),
3761 single_vehicle_type_(vehicle_types == 1),
3762 using_incoming_reinjected_saving_(false),
3767 sorted_savings_per_vehicle_type_.clear();
3768 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
3769 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
3770 savings.reserve(size * saving_neighbors);
3773 sorted_savings_.clear();
3774 costs_and_savings_per_arc_.clear();
3775 arc_indices_per_before_node_.clear();
3777 if (!single_vehicle_type_) {
3778 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
3779 arc_indices_per_before_node_.resize(size);
3780 for (
int before_node = 0; before_node < size; before_node++) {
3781 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
3784 skipped_savings_starting_at_.clear();
3785 skipped_savings_starting_at_.resize(size);
3786 skipped_savings_ending_at_.clear();
3787 skipped_savings_ending_at_.resize(size);
3788 incoming_reinjected_savings_ =
nullptr;
3789 outgoing_reinjected_savings_ =
nullptr;
3790 incoming_new_reinjected_savings_ =
nullptr;
3791 outgoing_new_reinjected_savings_ =
nullptr;
3795 int64_t before_node, int64_t after_node,
int vehicle_type) {
3796 CHECK(!sorted_savings_per_vehicle_type_.empty())
3797 <<
"Container not initialized!";
3798 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
3799 UpdateArcIndicesCostsAndSavings(before_node, after_node,
3800 {total_cost, saving});
3804 CHECK(!sorted_) <<
"Container already sorted!";
3806 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
3807 absl::c_sort(savings);
3810 if (single_vehicle_type_) {
3811 const auto& savings = sorted_savings_per_vehicle_type_[0];
3812 sorted_savings_.resize(savings.size());
3813 absl::c_transform(savings, sorted_savings_.begin(),
3814 [](
const Saving& saving) {
3815 return SavingAndArc({saving, -1});
3821 sorted_savings_.reserve(vehicle_types_ *
3822 costs_and_savings_per_arc_.size());
3824 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
3826 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
3827 costs_and_savings_per_arc_[arc_index];
3828 DCHECK(!costs_and_savings.empty());
3832 [](
const std::pair<int64_t, Saving>& cs1,
3833 const std::pair<int64_t, Saving>& cs2) {
return cs1 > cs2; });
3838 const int64_t cost = costs_and_savings.back().first;
3839 while (!costs_and_savings.empty() &&
3840 costs_and_savings.back().first == cost) {
3841 sorted_savings_.push_back(
3842 {costs_and_savings.back().second, arc_index});
3843 costs_and_savings.pop_back();
3846 absl::c_sort(sorted_savings_);
3847 next_saving_type_and_index_for_arc_.clear();
3848 next_saving_type_and_index_for_arc_.resize(
3849 costs_and_savings_per_arc_.size(), {-1, -1});
3852 index_in_sorted_savings_ = 0;
3857 return index_in_sorted_savings_ < sorted_savings_.size() ||
3858 HasReinjectedSavings();
3862 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
3864 <<
"Update() should be called between two calls to GetSaving() !";
3868 if (HasReinjectedSavings()) {
3869 if (incoming_reinjected_savings_ !=
nullptr &&
3870 outgoing_reinjected_savings_ !=
nullptr) {
3872 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
3873 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
3874 if (incoming_saving < outgoing_saving) {
3875 current_saving_ = incoming_saving;
3876 using_incoming_reinjected_saving_ =
true;
3878 current_saving_ = outgoing_saving;
3879 using_incoming_reinjected_saving_ =
false;
3882 if (incoming_reinjected_savings_ !=
nullptr) {
3883 current_saving_ = incoming_reinjected_savings_->front();
3884 using_incoming_reinjected_saving_ =
true;
3886 if (outgoing_reinjected_savings_ !=
nullptr) {
3887 current_saving_ = outgoing_reinjected_savings_->front();
3888 using_incoming_reinjected_saving_ =
false;
3892 current_saving_ = sorted_savings_[index_in_sorted_savings_];
3894 return current_saving_.
saving;
3897 void Update(
bool update_best_saving,
int type = -1) {
3898 CHECK(to_update_) <<
"Container already up to date!";
3899 if (update_best_saving) {
3900 const int64_t arc_index = current_saving_.arc_index;
3901 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
3903 if (!HasReinjectedSavings()) {
3904 index_in_sorted_savings_++;
3906 if (index_in_sorted_savings_ == sorted_savings_.size()) {
3907 sorted_savings_.swap(next_savings_);
3909 index_in_sorted_savings_ = 0;
3911 absl::c_sort(sorted_savings_);
3912 next_saving_type_and_index_for_arc_.clear();
3913 next_saving_type_and_index_for_arc_.resize(
3914 costs_and_savings_per_arc_.size(), {-1, -1});
3917 UpdateReinjectedSavings();
3922 CHECK(!single_vehicle_type_);
3927 CHECK(sorted_) <<
"Savings not sorted yet!";
3928 CHECK_LT(type, vehicle_types_);
3929 return sorted_savings_per_vehicle_type_[type];
3933 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
3934 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
3938 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
3939 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
3943 struct SavingAndArc {
3947 bool operator<(
const SavingAndArc& other)
const {
3948 return std::tie(saving, arc_index) <
3949 std::tie(other.saving, other.arc_index);
3955 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
3956 const Saving& saving = saving_and_arc.saving;
3957 const int64_t before_node = saving.before_node;
3958 const int64_t after_node = saving.after_node;
3959 if (!savings_db_->Contains(before_node)) {
3960 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
3962 if (!savings_db_->Contains(after_node)) {
3963 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
3977 void UpdateNextAndSkippedSavingsForArcWithType(int64_t arc_index,
int type) {
3978 if (single_vehicle_type_) {
3981 SkipSavingForArc(current_saving_);
3984 CHECK_GE(arc_index, 0);
3985 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
3986 const int previous_index = type_and_index.second;
3987 const int previous_type = type_and_index.first;
3988 bool next_saving_added =
false;
3991 if (previous_index >= 0) {
3993 DCHECK_GE(previous_type, 0);
3994 if (type == -1 || previous_type == type) {
3997 next_saving_added =
true;
3998 next_saving = next_savings_[previous_index].saving;
4002 if (!next_saving_added &&
4003 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
4004 type_and_index.first = next_saving.vehicle_type;
4005 if (previous_index >= 0) {
4007 next_savings_[previous_index] = {next_saving, arc_index};
4010 type_and_index.second = next_savings_.size();
4011 next_savings_.push_back({next_saving, arc_index});
4013 next_saving_added =
true;
4019 SkipSavingForArc(current_saving_);
4023 if (next_saving_added) {
4024 SkipSavingForArc({next_saving, arc_index});
4029 void UpdateReinjectedSavings() {
4030 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
4031 &incoming_reinjected_savings_,
4032 using_incoming_reinjected_saving_);
4033 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
4034 &outgoing_reinjected_savings_,
4035 !using_incoming_reinjected_saving_);
4036 incoming_new_reinjected_savings_ =
nullptr;
4037 outgoing_new_reinjected_savings_ =
nullptr;
4040 void UpdateGivenReinjectedSavings(
4041 std::deque<SavingAndArc>* new_reinjected_savings,
4042 std::deque<SavingAndArc>** reinjected_savings,
4043 bool using_reinjected_savings) {
4044 if (new_reinjected_savings ==
nullptr) {
4046 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
4047 CHECK(!(*reinjected_savings)->empty());
4048 (*reinjected_savings)->pop_front();
4049 if ((*reinjected_savings)->empty()) {
4050 *reinjected_savings =
nullptr;
4059 if (*reinjected_savings !=
nullptr) {
4060 (*reinjected_savings)->clear();
4062 *reinjected_savings =
nullptr;
4063 if (!new_reinjected_savings->empty()) {
4064 *reinjected_savings = new_reinjected_savings;
4068 bool HasReinjectedSavings() {
4069 return outgoing_reinjected_savings_ !=
nullptr ||
4070 incoming_reinjected_savings_ !=
nullptr;
4073 void UpdateArcIndicesCostsAndSavings(
4074 int64_t before_node, int64_t after_node,
4075 const std::pair<int64_t, Saving>& cost_and_saving) {
4076 if (single_vehicle_type_) {
4079 absl::flat_hash_map<int, int>& arc_indices =
4080 arc_indices_per_before_node_[before_node];
4081 const auto& arc_inserted = arc_indices.insert(
4082 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
4083 const int index = arc_inserted.first->second;
4084 if (arc_inserted.second) {
4085 costs_and_savings_per_arc_.push_back({cost_and_saving});
4087 DCHECK_LT(index, costs_and_savings_per_arc_.size());
4088 costs_and_savings_per_arc_[index].push_back(cost_and_saving);
4092 bool GetNextSavingForArcWithType(int64_t arc_index,
int type,
4093 Saving* next_saving) {
4094 std::vector<std::pair<int64_t, Saving>>& costs_and_savings =
4095 costs_and_savings_per_arc_[arc_index];
4097 bool found_saving =
false;
4098 while (!costs_and_savings.empty() && !found_saving) {
4099 const Saving& saving = costs_and_savings.back().second;
4100 if (type == -1 || saving.vehicle_type == type) {
4101 *next_saving = saving;
4102 found_saving =
true;
4104 costs_and_savings.pop_back();
4106 return found_saving;
4110 int64_t index_in_sorted_savings_;
4111 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
4112 std::vector<SavingAndArc> sorted_savings_;
4113 std::vector<SavingAndArc> next_savings_;
4114 std::vector<std::pair< int,
int>>
4115 next_saving_type_and_index_for_arc_;
4116 SavingAndArc current_saving_;
4117 std::vector<std::vector<std::pair< int64_t, Saving>>>
4118 costs_and_savings_per_arc_;
4119 std::vector<absl::flat_hash_map< int,
int>>
4120 arc_indices_per_before_node_;
4121 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
4122 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
4123 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
4124 std::deque<SavingAndArc>* incoming_reinjected_savings_;
4125 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
4126 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
4127 const int vehicle_types_;
4128 const bool single_vehicle_type_;
4129 bool using_incoming_reinjected_saving_;
4137 RoutingModel*
model, std::function<
bool()> stop_search,
4141 savings_params_(parameters) {
4142 DCHECK_GT(savings_params_.neighbors_ratio, 0);
4143 DCHECK_LE(savings_params_.neighbors_ratio, 1);
4144 DCHECK_GT(savings_params_.max_memory_usage_bytes, 0);
4145 DCHECK_GT(savings_params_.arc_coefficient, 0);
4153 model()->GetVehicleTypeContainer());
4158 if (!ComputeSavings())
return false;
4164 if (!
Evaluate(
true).has_value())
return false;
4170 int type, int64_t before_node, int64_t after_node) {
4171 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
4178 SetNext(
model()->Start(vehicle), before_node, vehicle);
4179 SetNext(before_node, after_node, vehicle);
4185 ->GetCompatibleVehicleOfType(
4186 type, vehicle_is_compatible,
4187 [](
int) {
return false; })
4191void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
4192 std::vector<std::vector<int64_t>>* adjacency_lists) {
4193 for (int64_t node = 0; node < adjacency_lists->size(); node++) {
4194 for (int64_t neighbor : (*adjacency_lists)[node]) {
4195 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
4198 (*adjacency_lists)[neighbor].push_back(node);
4201 absl::c_transform(*adjacency_lists, adjacency_lists->begin(),
4202 [](std::vector<int64_t> vec) {
4204 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
4220bool SavingsFilteredHeuristic::ComputeSavings() {
4222 const int size =
model()->Size();
4224 std::vector<int64_t> uncontained_non_start_end_nodes;
4225 uncontained_non_start_end_nodes.reserve(size);
4226 for (
int node = 0; node < size; node++) {
4228 uncontained_non_start_end_nodes.push_back(node);
4232 const int64_t saving_neighbors =
4233 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
4234 static_cast<int64_t
>(uncontained_non_start_end_nodes.size()));
4237 std::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
4240 std::vector<std::vector<int64_t>> adjacency_lists(size);
4242 for (
int type = 0; type < num_vehicle_types; ++type) {
4245 if (vehicle < 0)
continue;
4247 const int64_t cost_class =
4248 model()->GetCostClassIndexOfVehicle(vehicle).value();
4249 const int64_t start =
model()->Start(vehicle);
4250 const int64_t end =
model()->End(vehicle);
4251 const int64_t fixed_cost =
model()->GetFixedCostOfVehicle(vehicle);
4255 for (
int before_node : uncontained_non_start_end_nodes) {
4256 std::vector<std::pair< int64_t, int64_t>>
4258 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
4260 for (
int after_node : uncontained_non_start_end_nodes) {
4261 if (after_node != before_node) {
4262 costed_after_nodes.push_back(std::make_pair(
4263 model()->GetArcCostForClass(before_node, after_node, cost_class),
4267 if (saving_neighbors < costed_after_nodes.size()) {
4268 std::nth_element(costed_after_nodes.begin(),
4269 costed_after_nodes.begin() + saving_neighbors,
4270 costed_after_nodes.end());
4271 costed_after_nodes.resize(saving_neighbors);
4273 adjacency_lists[before_node].resize(costed_after_nodes.size());
4274 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
4275 adjacency_lists[before_node].begin(),
4276 [](std::pair<int64_t, int64_t> cost_and_node) {
4277 return cost_and_node.second;
4280 if (savings_params_.add_reverse_arcs) {
4281 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
4286 for (
int before_node : uncontained_non_start_end_nodes) {
4287 const int64_t before_to_end_cost =
4288 model()->GetArcCostForClass(before_node, end, cost_class);
4289 const int64_t start_to_before_cost =
4290 CapSub(
model()->GetArcCostForClass(start, before_node, cost_class),
4293 for (int64_t after_node : adjacency_lists[before_node]) {
4294 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
4295 before_node == after_node ||
Contains(after_node)) {
4298 const int64_t arc_cost =
4299 model()->GetArcCostForClass(before_node, after_node, cost_class);
4300 const int64_t start_to_after_cost =
4301 CapSub(
model()->GetArcCostForClass(start, after_node, cost_class),
4303 const int64_t after_to_end_cost =
4304 model()->GetArcCostForClass(after_node, end, cost_class);
4306 const double weighted_arc_cost_fp =
4307 savings_params_.arc_coefficient * arc_cost;
4308 const int64_t weighted_arc_cost =
4309 weighted_arc_cost_fp < std::numeric_limits<int64_t>::max()
4310 ?
static_cast<int64_t
>(weighted_arc_cost_fp)
4311 : std::numeric_limits<int64_t>::max();
4312 const int64_t saving_value =
CapSub(
4313 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
4316 BuildSaving(-saving_value, type, before_node, after_node);
4318 const int64_t total_cost =
4319 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
4330int64_t SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
4331 int num_vehicle_types)
const {
4332 const int64_t size =
model()->Size();
4334 const int64_t num_neighbors_with_ratio =
4335 std::max(1.0, size * savings_params_.neighbors_ratio);
4341 const double max_memory_usage_in_savings_unit =
4342 savings_params_.max_memory_usage_bytes / 16;
4359 if (num_vehicle_types > 1) {
4360 multiplicative_factor += 1.5;
4362 const double num_savings =
4363 max_memory_usage_in_savings_unit / multiplicative_factor;
4364 const int64_t num_neighbors_with_memory_restriction =
4365 std::max(1.0, num_savings / (num_vehicle_types * size));
4367 return std::min(num_neighbors_with_ratio,
4368 num_neighbors_with_memory_restriction);
4373void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
4375 DCHECK_GT(vehicle_types, 0);
4376 const int size =
model()->Size();
4379 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
4380 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
4381 for (
int type = 0; type < vehicle_types; type++) {
4382 const int vehicle_type_offset = type * size;
4383 const std::vector<Saving>& sorted_savings_for_type =
4385 for (
const Saving& saving : sorted_savings_for_type) {
4386 DCHECK_EQ(saving.vehicle_type, type);
4387 const int before_node = saving.before_node;
4388 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
4389 const int after_node = saving.after_node;
4390 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
4399 int before_node = saving.before_node;
4400 int after_node = saving.after_node;
4401 const bool nodes_contained =
Contains(before_node) ||
Contains(after_node);
4403 if (nodes_contained) {
4409 const int type = saving.vehicle_type;
4417 const int64_t start =
model()->Start(vehicle);
4418 const int64_t end =
model()->End(vehicle);
4422 const int saving_offset = type * size;
4424 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
4425 out_index < out_savings_ptr[saving_offset + before_node].size()) {
4428 int before_before_node = -1;
4429 int after_after_node = -1;
4430 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
4431 const Saving& in_saving =
4432 *(in_savings_ptr[saving_offset + after_node][in_index]);
4433 if (out_index < out_savings_ptr[saving_offset + before_node].size()) {
4434 const Saving& out_saving =
4435 *(out_savings_ptr[saving_offset + before_node][out_index]);
4436 if (in_saving.saving < out_saving.saving) {
4437 after_after_node = in_saving.after_node;
4439 before_before_node = out_saving.before_node;
4442 after_after_node = in_saving.after_node;
4445 before_before_node =
4446 out_savings_ptr[saving_offset + before_node][out_index]
4450 if (after_after_node != -1) {
4451 DCHECK_EQ(before_before_node, -1);
4455 SetNext(after_node, after_after_node, vehicle);
4456 SetNext(after_after_node, end, vehicle);
4459 after_node = after_after_node;
4464 CHECK_GE(before_before_node, 0);
4466 if (!
Contains(before_before_node)) {
4467 SetNext(start, before_before_node, vehicle);
4468 SetNext(before_before_node, before_node, vehicle);
4471 before_node = before_before_node;
4482void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
4485 const int64_t size =
model()->Size();
4486 const int vehicles =
model()->vehicles();
4488 first_node_on_route_.resize(vehicles, -1);
4489 last_node_on_route_.resize(vehicles, -1);
4490 vehicle_of_first_or_last_node_.resize(size, -1);
4492 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
4493 const int64_t start =
model()->Start(vehicle);
4494 const int64_t end =
model()->End(vehicle);
4498 int64_t node =
Value(start);
4500 vehicle_of_first_or_last_node_[node] = vehicle;
4501 first_node_on_route_[vehicle] = node;
4503 int64_t next =
Value(node);
4504 while (next != end) {
4508 vehicle_of_first_or_last_node_[node] = vehicle;
4509 last_node_on_route_[vehicle] = node;
4516 const int64_t before_node = saving.before_node;
4517 const int64_t after_node = saving.after_node;
4518 const int type = saving.vehicle_type;
4522 bool committed =
false;
4530 vehicle_of_first_or_last_node_[before_node] = vehicle;
4531 vehicle_of_first_or_last_node_[after_node] = vehicle;
4532 first_node_on_route_[vehicle] = before_node;
4533 last_node_on_route_[vehicle] = after_node;
4545 const int v1 = vehicle_of_first_or_last_node_[before_node];
4546 const int64_t last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
4548 const int v2 = vehicle_of_first_or_last_node_[after_node];
4549 const int64_t first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
4551 if (before_node == last_node && after_node == first_node && v1 != v2 &&
4553 CHECK_EQ(
Value(before_node),
model()->End(v1));
4554 CHECK_EQ(
Value(
model()->Start(v2)), after_node);
4560 MergeRoutes(v1, v2, before_node, after_node);
4565 const int vehicle = vehicle_of_first_or_last_node_[before_node];
4566 const int64_t last_node =
4567 vehicle == -1 ? -1 : last_node_on_route_[vehicle];
4569 if (before_node == last_node) {
4570 const int64_t end =
model()->End(vehicle);
4571 CHECK_EQ(
Value(before_node), end);
4574 if (type != route_type) {
4582 SetNext(before_node, after_node, vehicle);
4583 SetNext(after_node, end, vehicle);
4585 if (first_node_on_route_[vehicle] != before_node) {
4587 DCHECK_NE(
Value(
model()->Start(vehicle)), before_node);
4588 vehicle_of_first_or_last_node_[before_node] = -1;
4590 vehicle_of_first_or_last_node_[after_node] = vehicle;
4591 last_node_on_route_[vehicle] = after_node;
4598 const int vehicle = vehicle_of_first_or_last_node_[after_node];
4599 const int64_t first_node =
4600 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
4602 if (after_node == first_node) {
4603 const int64_t start =
model()->Start(vehicle);
4604 CHECK_EQ(
Value(start), after_node);
4607 if (type != route_type) {
4615 SetNext(before_node, after_node, vehicle);
4616 SetNext(start, before_node, vehicle);
4618 if (last_node_on_route_[vehicle] != after_node) {
4620 DCHECK_NE(
Value(after_node),
model()->End(vehicle));
4621 vehicle_of_first_or_last_node_[after_node] = -1;
4623 vehicle_of_first_or_last_node_[before_node] = vehicle;
4624 first_node_on_route_[vehicle] = before_node;
4633void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
4635 int64_t before_node,
4636 int64_t after_node) {
4638 const int64_t new_first_node = first_node_on_route_[first_vehicle];
4639 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
4640 CHECK_EQ(
Value(
model()->Start(first_vehicle)), new_first_node);
4641 const int64_t new_last_node = last_node_on_route_[second_vehicle];
4642 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
4643 CHECK_EQ(
Value(new_last_node),
model()->End(second_vehicle));
4646 int used_vehicle = first_vehicle;
4647 int unused_vehicle = second_vehicle;
4648 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
4649 model()->GetFixedCostOfVehicle(second_vehicle)) {
4650 used_vehicle = second_vehicle;
4651 unused_vehicle = first_vehicle;
4654 SetNext(before_node, after_node, used_vehicle);
4657 if (used_vehicle == first_vehicle) {
4658 SetNext(new_last_node,
model()->End(used_vehicle), used_vehicle);
4660 SetNext(
model()->Start(used_vehicle), new_first_node, used_vehicle);
4662 bool committed =
Evaluate(
true).has_value();
4664 model()->GetVehicleClassIndexOfVehicle(first_vehicle).value() !=
4665 model()->GetVehicleClassIndexOfVehicle(second_vehicle).value()) {
4667 std::swap(used_vehicle, unused_vehicle);
4668 SetNext(before_node, after_node, used_vehicle);
4671 if (used_vehicle == first_vehicle) {
4672 SetNext(new_last_node,
model()->End(used_vehicle), used_vehicle);
4674 SetNext(
model()->Start(used_vehicle), new_first_node, used_vehicle);
4676 committed =
Evaluate(
true).has_value();
4682 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).value(),
4683 model()->GetFixedCostOfVehicle(unused_vehicle));
4686 first_node_on_route_[unused_vehicle] = -1;
4687 last_node_on_route_[unused_vehicle] = -1;
4688 vehicle_of_first_or_last_node_[before_node] = -1;
4689 vehicle_of_first_or_last_node_[after_node] = -1;
4690 first_node_on_route_[used_vehicle] = new_first_node;
4691 last_node_on_route_[used_vehicle] = new_last_node;
4692 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
4693 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
4700 RoutingModel*
model, std::function<
bool()> stop_search,
4703 use_minimum_matching_(use_minimum_matching) {}
4707 const int size =
model()->Size() -
model()->vehicles() + 1;
4713 std::vector<int> indices(1, 0);
4714 for (
int i = 1; i < size; ++i) {
4715 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
4716 indices.push_back(i);
4719 const int num_cost_classes =
model()->GetCostClassesCount();
4720 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
4721 std::vector<bool> class_covered(num_cost_classes,
false);
4722 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
4723 const int64_t cost_class =
4724 model()->GetCostClassIndexOfVehicle(vehicle).value();
4725 if (!class_covered[cost_class]) {
4726 class_covered[cost_class] =
true;
4727 const int64_t start =
model()->Start(vehicle);
4728 const int64_t end =
model()->End(vehicle);
4729 auto cost = [
this, &indices, start, end, cost_class](
int from,
int to) {
4730 DCHECK_LT(from, indices.size());
4731 DCHECK_LT(
to, indices.size());
4732 const int from_index = (from == 0) ? start : indices[from];
4733 const int to_index = (
to == 0) ? end : indices[
to];
4734 const int64_t cost =
4735 model()->GetArcCostForClass(from_index, to_index, cost_class);
4740 return std::min(cost, std::numeric_limits<int64_t>::max() / 2);
4742 using Cost =
decltype(cost);
4744 indices.size(), cost);
4745 if (use_minimum_matching_) {
4748 MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING);
4750 if (christofides_solver.
Solve()) {
4751 path_per_cost_class[cost_class] =
4757 for (
int vehicle = 0; vehicle <
model()->vehicles(); ++vehicle) {
4758 const int64_t cost_class =
4759 model()->GetCostClassIndexOfVehicle(vehicle).value();
4760 const std::vector<int>& path = path_per_cost_class[cost_class];
4761 if (path.empty())
continue;
4762 DCHECK_EQ(0, path[0]);
4763 DCHECK_EQ(0, path.back());
4766 const int end =
model()->End(vehicle);
4767 for (
int i = 1; i < path.size() - 1 && prev != end; ++i) {
4769 int next = indices[path[i]];
4788 SweepIndex(
const int64_t index,
const double angle,
const double distance)
4789 : index(index), angle(angle), distance(distance) {}
4790 ~SweepIndex() =
default;
4797struct SweepIndexSortAngle {
4798 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
4799 return (node1.angle < node2.angle);
4801} SweepIndexAngleComparator;
4803struct SweepIndexSortDistance {
4804 bool operator()(
const SweepIndex& node1,
const SweepIndex& node2)
const {
4805 return (node1.distance < node2.distance);
4807} SweepIndexDistanceComparator;
4811 absl::Span<
const std::pair<int64_t, int64_t>> points)
4812 : coordinates_(2 * points.size(), 0), sectors_(1) {
4813 for (int64_t i = 0; i < points.size(); ++i) {
4814 coordinates_[2 * i] = points[i].first;
4815 coordinates_[2 * i + 1] = points[i].second;
4822 const double pi_rad = 3.14159265;
4824 const int x0 = coordinates_[0];
4825 const int y0 = coordinates_[1];
4827 std::vector<SweepIndex> sweep_indices;
4828 for (int64_t index = 0; index < static_cast<int>(coordinates_.size()) / 2;
4830 const int x = coordinates_[2 * index];
4831 const int y = coordinates_[2 * index + 1];
4832 const double x_delta = x - x0;
4833 const double y_delta = y - y0;
4834 double square_distance = x_delta * x_delta + y_delta * y_delta;
4835 double angle = square_distance == 0 ? 0 : std::atan2(y_delta, x_delta);
4836 angle = angle >= 0 ? angle : 2 * pi_rad + angle;
4837 sweep_indices.push_back(SweepIndex(index, angle, square_distance));
4839 absl::c_sort(sweep_indices, SweepIndexDistanceComparator);
4841 const int size =
static_cast<int>(sweep_indices.size()) / sectors_;
4842 for (
int sector = 0; sector < sectors_; ++sector) {
4843 std::vector<SweepIndex> cluster;
4844 std::vector<SweepIndex>::iterator begin =
4845 sweep_indices.begin() + sector * size;
4846 std::vector<SweepIndex>::iterator end =
4847 sector == sectors_ - 1 ? sweep_indices.end()
4848 : sweep_indices.begin() + (sector + 1) * size;
4849 std::sort(begin, end, SweepIndexAngleComparator);
4851 for (
const SweepIndex& sweep_index : sweep_indices) {
4852 indices->push_back(sweep_index.index);
4859 Link(std::pair<int, int> link,
double value,
int vehicle_class,
4860 int64_t start_depot, int64_t end_depot)
4863 vehicle_class(vehicle_class),
4864 start_depot(start_depot),
4865 end_depot(end_depot) {}
4868 std::pair<int, int> link;
4871 int64_t start_depot;
4880class RouteConstructor {
4882 RouteConstructor(Assignment*
const assignment, RoutingModel*
const model,
4883 bool check_assignment, int64_t num_indices,
4884 const std::vector<Link>& links_list)
4885 : assignment_(assignment),
4887 check_assignment_(check_assignment),
4888 solver_(model_->solver()),
4889 num_indices_(num_indices),
4890 links_list_(links_list),
4891 nexts_(model_->Nexts()),
4892 in_route_(num_indices_, -1),
4894 index_to_chain_index_(num_indices, -1),
4895 index_to_vehicle_class_index_(num_indices, -1) {
4897 const std::vector<std::string> dimension_names =
4898 model_->GetAllDimensionNames();
4899 dimensions_.assign(dimension_names.size(),
nullptr);
4900 for (
int i = 0;
i < dimension_names.size(); ++
i) {
4901 dimensions_[
i] = &model_->GetDimensionOrDie(dimension_names[i]);
4904 cumuls_.resize(dimensions_.size());
4905 for (std::vector<int64_t>& cumuls : cumuls_) {
4906 cumuls.resize(num_indices_);
4908 new_possible_cumuls_.resize(dimensions_.size());
4911 ~RouteConstructor() =
default;
4914 model_->solver()->TopPeriodicCheck();
4916 for (
int index = 0; index < num_indices_; ++index) {
4917 if (!model_->IsStart(index) && !model_->IsEnd(index)) {
4918 routes_.push_back({index});
4919 in_route_[index] = routes_.size() - 1;
4923 for (
const Link& link : links_list_) {
4924 model_->solver()->TopPeriodicCheck();
4925 const int index1 = link.link.first;
4926 const int index2 = link.link.second;
4927 const int vehicle_class = link.vehicle_class;
4928 const int64_t start_depot = link.start_depot;
4929 const int64_t end_depot = link.end_depot;
4932 if (index_to_vehicle_class_index_[index1] < 0) {
4933 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4934 ++dimension_index) {
4935 cumuls_[dimension_index][index1] =
4936 std::max(dimensions_[dimension_index]->GetTransitValue(
4937 start_depot, index1, 0),
4938 dimensions_[dimension_index]->CumulVar(index1)->Min());
4941 if (index_to_vehicle_class_index_[index2] < 0) {
4942 for (
int dimension_index = 0; dimension_index < dimensions_.size();
4943 ++dimension_index) {
4944 cumuls_[dimension_index][index2] =
4945 std::max(dimensions_[dimension_index]->GetTransitValue(
4946 start_depot, index2, 0),
4947 dimensions_[dimension_index]->CumulVar(index2)->Min());
4951 const int route_index1 = in_route_[index1];
4952 const int route_index2 = in_route_[index2];
4954 route_index1 >= 0 && route_index2 >= 0 &&
4955 FeasibleMerge(routes_[route_index1], routes_[route_index2], index1,
4956 index2, route_index1, route_index2, vehicle_class,
4957 start_depot, end_depot);
4958 if (Merge(merge, route_index1, route_index2)) {
4959 index_to_vehicle_class_index_[index1] = vehicle_class;
4960 index_to_vehicle_class_index_[index2] = vehicle_class;
4964 model_->solver()->TopPeriodicCheck();
4968 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
4969 if (!deleted_chains_.contains(chain_index)) {
4970 final_chains_.push_back(chains_[chain_index]);
4973 absl::c_sort(final_chains_, ChainComparator);
4974 for (
int route_index = 0; route_index < routes_.size(); ++route_index) {
4975 if (!deleted_routes_.contains(route_index)) {
4976 final_routes_.push_back(routes_[route_index]);
4979 absl::c_sort(final_routes_, RouteComparator);
4981 const int extra_vehicles = std::max(
4982 0,
static_cast<int>(final_chains_.size()) - model_->vehicles());
4984 int chain_index = 0;
4985 for (chain_index = extra_vehicles; chain_index < final_chains_.size();
4987 if (chain_index - extra_vehicles >= model_->vehicles()) {
4990 const int start = final_chains_[chain_index].head;
4991 const int end = final_chains_[chain_index].tail;
4993 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
4994 assignment_->SetValue(
4995 model_->NextVar(model_->Start(chain_index - extra_vehicles)), start);
4996 assignment_->Add(nexts_[end]);
4997 assignment_->SetValue(nexts_[end],
4998 model_->End(chain_index - extra_vehicles));
5002 for (
int route_index = 0; route_index < final_routes_.size();
5004 if (chain_index - extra_vehicles >= model_->vehicles()) {
5007 DCHECK_LT(route_index, final_routes_.size());
5008 const int head = final_routes_[route_index].front();
5009 const int tail = final_routes_[route_index].back();
5010 if (head == tail && head < model_->Size()) {
5012 model_->NextVar(model_->Start(chain_index - extra_vehicles)));
5013 assignment_->SetValue(
5014 model_->NextVar(model_->Start(chain_index - extra_vehicles)), head);
5015 assignment_->Add(nexts_[tail]);
5016 assignment_->SetValue(nexts_[tail],
5017 model_->End(chain_index - extra_vehicles));
5023 for (
int index = 0; index < model_->Size(); ++index) {
5024 IntVar*
const next = nexts_[index];
5025 if (!assignment_->Contains(next)) {
5026 assignment_->Add(next);
5027 if (next->Contains(index)) {
5028 assignment_->SetValue(next, index);
5035 enum MergeStatus { FIRST_SECOND, SECOND_FIRST, NO_MERGE };
5038 bool operator()(absl::Span<const int> route1,
5039 absl::Span<const int> route2)
const {
5040 return (route1.size() < route2.size());
5051 bool operator()(
const Chain& chain1,
const Chain& chain2)
const {
5052 return (chain1.nodes < chain2.nodes);
5056 bool Head(
int node)
const {
5057 return (node == routes_[in_route_[node]].front());
5060 bool Tail(
int node)
const {
5061 return (node == routes_[in_route_[node]].back());
5064 bool FeasibleRoute(
const std::vector<int>& route, int64_t route_cumul,
5065 int dimension_index) {
5066 const RoutingDimension& dimension = *dimensions_[dimension_index];
5067 std::vector<int>::const_iterator it = route.begin();
5068 int64_t cumul = route_cumul;
5069 while (it != route.end()) {
5070 const int previous = *it;
5071 const int64_t cumul_previous = cumul;
5075 if (it == route.end()) {
5078 const int next = *it;
5079 int64_t available_from_previous =
5080 cumul_previous + dimension.GetTransitValue(previous, next, 0);
5081 int64_t available_cumul_next =
5082 std::max(cumuls_[dimension_index][next], available_from_previous);
5084 const int64_t slack = available_cumul_next - available_from_previous;
5085 if (slack > dimension.SlackVar(previous)->Max()) {
5086 available_cumul_next =
5087 available_from_previous + dimension.SlackVar(previous)->Max();
5090 if (available_cumul_next > dimension.CumulVar(next)->Max()) {
5093 if (available_cumul_next <= cumuls_[dimension_index][next]) {
5096 cumul = available_cumul_next;
5101 bool CheckRouteConnection(absl::Span<const int> route1,
5102 const std::vector<int>& route2,
int dimension_index,
5103 int64_t , int64_t end_depot) {
5104 const int tail1 = route1.back();
5105 const int head2 = route2.front();
5106 const int tail2 = route2.back();
5107 const RoutingDimension& dimension = *dimensions_[dimension_index];
5108 int non_depot_node = -1;
5109 for (
int node = 0; node < num_indices_; ++node) {
5110 if (!model_->IsStart(node) && !model_->IsEnd(node)) {
5111 non_depot_node = node;
5115 CHECK_GE(non_depot_node, 0);
5116 const int64_t depot_threshold =
5117 std::max(dimension.SlackVar(non_depot_node)->Max(),
5118 dimension.CumulVar(non_depot_node)->Max());
5120 int64_t available_from_tail1 = cumuls_[dimension_index][tail1] +
5121 dimension.GetTransitValue(tail1, head2, 0);
5122 int64_t new_available_cumul_head2 =
5123 std::max(cumuls_[dimension_index][head2], available_from_tail1);
5125 const int64_t slack = new_available_cumul_head2 - available_from_tail1;
5126 if (slack > dimension.SlackVar(tail1)->Max()) {
5127 new_available_cumul_head2 =
5128 available_from_tail1 + dimension.SlackVar(tail1)->Max();
5131 bool feasible_route =
true;
5132 if (new_available_cumul_head2 > dimension.CumulVar(head2)->Max()) {
5135 if (new_available_cumul_head2 <= cumuls_[dimension_index][head2]) {
5140 FeasibleRoute(route2, new_available_cumul_head2, dimension_index);
5141 const int64_t new_possible_cumul_tail2 =
5142 new_possible_cumuls_[dimension_index].contains(tail2)
5143 ? new_possible_cumuls_[dimension_index][tail2]
5144 : cumuls_[dimension_index][tail2];
5146 if (!feasible_route || (new_possible_cumul_tail2 +
5147 dimension.GetTransitValue(tail2, end_depot, 0) >
5154 bool FeasibleMerge(
const std::vector<int>& route1,
5155 const std::vector<int>& route2,
int node1,
int node2,
5156 int route_index1,
int route_index2,
int vehicle_class,
5157 int64_t start_depot, int64_t end_depot) {
5158 if ((route_index1 == route_index2) || !(Tail(node1) && Head(node2))) {
5163 if (!((index_to_vehicle_class_index_[node1] == -1 &&
5164 index_to_vehicle_class_index_[node2] == -1) ||
5165 (index_to_vehicle_class_index_[node1] == vehicle_class &&
5166 index_to_vehicle_class_index_[node2] == -1) ||
5167 (index_to_vehicle_class_index_[node1] == -1 &&
5168 index_to_vehicle_class_index_[node2] == vehicle_class) ||
5169 (index_to_vehicle_class_index_[node1] == vehicle_class &&
5170 index_to_vehicle_class_index_[node2] == vehicle_class))) {
5176 for (
int dimension_index = 0; dimension_index < dimensions_.size();
5177 ++dimension_index) {
5178 new_possible_cumuls_[dimension_index].clear();
5179 merge = merge && CheckRouteConnection(route1, route2, dimension_index,
5180 start_depot, end_depot);
5188 bool CheckTempAssignment(Assignment*
const temp_assignment,
5189 int new_chain_index,
int old_chain_index,
int head1,
5190 int tail1,
int head2,
int tail2) {
5193 if (new_chain_index >= model_->vehicles())
return false;
5194 const int start = head1;
5195 temp_assignment->Add(model_->NextVar(model_->Start(new_chain_index)));
5196 temp_assignment->SetValue(model_->NextVar(model_->Start(new_chain_index)),
5198 temp_assignment->Add(nexts_[tail1]);
5199 temp_assignment->SetValue(nexts_[tail1], head2);
5200 temp_assignment->Add(nexts_[tail2]);
5201 temp_assignment->SetValue(nexts_[tail2], model_->End(new_chain_index));
5202 for (
int chain_index = 0; chain_index < chains_.size(); ++chain_index) {
5203 if ((chain_index != new_chain_index) &&
5204 (chain_index != old_chain_index) &&
5205 (!deleted_chains_.contains(chain_index))) {
5206 const int start = chains_[chain_index].head;
5207 const int end = chains_[chain_index].tail;
5208 temp_assignment->Add(model_->NextVar(model_->Start(chain_index)));
5209 temp_assignment->SetValue(model_->NextVar(model_->Start(chain_index)),
5211 temp_assignment->Add(nexts_[end]);
5212 temp_assignment->SetValue(nexts_[end], model_->End(chain_index));
5215 return solver_->Solve(solver_->MakeRestoreAssignment(temp_assignment));
5218 bool UpdateAssignment(absl::Span<const int> route1,
5219 absl::Span<const int> route2) {
5220 bool feasible =
true;
5221 const int head1 = route1.front();
5222 const int tail1 = route1.back();
5223 const int head2 = route2.front();
5224 const int tail2 = route2.back();
5225 const int chain_index1 = index_to_chain_index_[head1];
5226 const int chain_index2 = index_to_chain_index_[head2];
5227 if (chain_index1 < 0 && chain_index2 < 0) {
5228 const int chain_index = chains_.size();
5229 if (check_assignment_) {
5230 Assignment*
const temp_assignment =
5231 solver_->MakeAssignment(assignment_);
5232 feasible = CheckTempAssignment(temp_assignment, chain_index, -1, head1,
5233 tail1, head2, tail2);
5240 index_to_chain_index_[head1] = chain_index;
5241 index_to_chain_index_[tail2] = chain_index;
5242 chains_.push_back(chain);
5244 }
else if (chain_index1 >= 0 && chain_index2 < 0) {
5245 if (check_assignment_) {
5246 Assignment*
const temp_assignment =
5247 solver_->MakeAssignment(assignment_);
5249 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
5250 head1, tail1, head2, tail2);
5253 index_to_chain_index_[tail2] = chain_index1;
5254 chains_[chain_index1].head = head1;
5255 chains_[chain_index1].tail = tail2;
5256 ++chains_[chain_index1].nodes;
5258 }
else if (chain_index1 < 0 && chain_index2 >= 0) {
5259 if (check_assignment_) {
5260 Assignment*
const temp_assignment =
5261 solver_->MakeAssignment(assignment_);
5263 CheckTempAssignment(temp_assignment, chain_index2, chain_index1,
5264 head1, tail1, head2, tail2);
5267 index_to_chain_index_[head1] = chain_index2;
5268 chains_[chain_index2].head = head1;
5269 chains_[chain_index2].tail = tail2;
5270 ++chains_[chain_index2].nodes;
5273 if (check_assignment_) {
5274 Assignment*
const temp_assignment =
5275 solver_->MakeAssignment(assignment_);
5277 CheckTempAssignment(temp_assignment, chain_index1, chain_index2,
5278 head1, tail1, head2, tail2);
5281 index_to_chain_index_[tail2] = chain_index1;
5282 chains_[chain_index1].head = head1;
5283 chains_[chain_index1].tail = tail2;
5284 chains_[chain_index1].nodes += chains_[chain_index2].nodes;
5285 deleted_chains_.insert(chain_index2);
5289 assignment_->Add(nexts_[tail1]);
5290 assignment_->SetValue(nexts_[tail1], head2);
5295 bool Merge(
bool merge,
int index1,
int index2) {
5297 if (UpdateAssignment(routes_[index1], routes_[index2])) {
5299 for (
const int node : routes_[index2]) {
5300 in_route_[node] = index1;
5301 routes_[index1].push_back(node);
5303 for (
int dimension_index = 0; dimension_index < dimensions_.size();
5304 ++dimension_index) {
5305 for (
const std::pair<int, int64_t> new_possible_cumul :
5306 new_possible_cumuls_[dimension_index]) {
5307 cumuls_[dimension_index][new_possible_cumul.first] =
5308 new_possible_cumul.second;
5311 deleted_routes_.insert(index2);
5318 Assignment*
const assignment_;
5319 RoutingModel*
const model_;
5320 const bool check_assignment_;
5321 Solver*
const solver_;
5322 const int64_t num_indices_;
5323 const std::vector<Link> links_list_;
5324 std::vector<IntVar*> nexts_;
5325 std::vector<const RoutingDimension*> dimensions_;
5326 std::vector<std::vector<int64_t>> cumuls_;
5327 std::vector<absl::flat_hash_map<int, int64_t>> new_possible_cumuls_;
5328 std::vector<std::vector<int>> routes_;
5329 std::vector<int> in_route_;
5330 absl::flat_hash_set<int> deleted_routes_;
5331 std::vector<std::vector<int>> final_routes_;
5332 std::vector<Chain> chains_;
5333 absl::flat_hash_set<int> deleted_chains_;
5334 std::vector<Chain> final_chains_;
5335 std::vector<int> index_to_chain_index_;
5336 std::vector<int> index_to_vehicle_class_index_;
5344 SweepBuilder(RoutingModel*
const model,
bool check_assignment)
5345 : model_(model), check_assignment_(check_assignment) {}
5346 ~SweepBuilder()
override =
default;
5348 Decision*
Next(Solver*
const solver)
override {
5353 Assignment*
const assignment = solver->MakeAssignment();
5354 route_constructor_ = std::make_unique<RouteConstructor>(
5355 assignment, model_, check_assignment_, num_indices_, links_);
5357 route_constructor_->Construct();
5358 route_constructor_.reset(
nullptr);
5360 assignment->Restore();
5367 const int depot = model_->GetDepot();
5368 num_indices_ = model_->Size() + model_->vehicles();
5369 if (absl::GetFlag(FLAGS_sweep_sectors) > 0 &&
5370 absl::GetFlag(FLAGS_sweep_sectors) < num_indices_) {
5371 model_->sweep_arranger()->SetSectors(absl::GetFlag(FLAGS_sweep_sectors));
5373 std::vector<int64_t> indices;
5374 model_->sweep_arranger()->ArrangeIndices(&indices);
5375 for (
int i = 0;
i < indices.size() - 1; ++
i) {
5376 const int64_t first = indices[
i];
5377 const int64_t second = indices[
i + 1];
5378 if ((model_->IsStart(first) || !model_->IsEnd(first)) &&
5379 (model_->IsStart(second) || !model_->IsEnd(second))) {
5380 if (first != depot && second != depot) {
5382 Link(std::make_pair(first, second), 0, 0, depot, depot));
5388 RoutingModel*
const model_;
5389 std::unique_ptr<RouteConstructor> route_constructor_;
5390 const bool check_assignment_;
5391 int64_t num_indices_;
5392 std::vector<Link> links_;
5397 bool check_assignment) {
5398 return model->solver()->RevAlloc(
new SweepBuilder(model, check_assignment));
5407class AllUnperformed :
public DecisionBuilder {
5410 explicit AllUnperformed(RoutingModel*
const model) : model_(model) {}
5411 ~AllUnperformed()
override =
default;
5412 Decision*
Next(Solver*
const )
override {
5415 model_->CostVar()->FreezeQueue();
5416 for (
int i = 0;
i < model_->Size(); ++
i) {
5417 if (!model_->IsStart(i)) {
5418 model_->ActiveVar(i)->SetValue(0);
5421 model_->CostVar()->UnfreezeQueue();
5426 RoutingModel*
const model_;
5431 return model->solver()->RevAlloc(
new AllUnperformed(model));
5436class GuidedSlackFinalizer :
public DecisionBuilder {
5438 GuidedSlackFinalizer(
const RoutingDimension* dimension, RoutingModel* model,
5439 std::function<int64_t(int64_t)> initializer);
5442 GuidedSlackFinalizer(
const GuidedSlackFinalizer&) =
delete;
5443 GuidedSlackFinalizer& operator=(
const GuidedSlackFinalizer&) =
delete;
5445 Decision*
Next(Solver* solver)
override;
5448 int64_t SelectValue(int64_t index);
5449 int64_t ChooseVariable();
5451 const RoutingDimension*
const dimension_;
5452 RoutingModel*
const model_;
5453 const std::function<int64_t(int64_t)> initializer_;
5454 RevArray<bool> is_initialized_;
5455 std::vector<int64_t> initial_values_;
5456 Rev<int64_t> current_index_;
5457 Rev<int64_t> current_route_;
5458 RevArray<int64_t> last_delta_used_;
5461GuidedSlackFinalizer::GuidedSlackFinalizer(
5462 const RoutingDimension* dimension, RoutingModel* model,
5463 std::function<int64_t(int64_t)> initializer)
5464 : dimension_(ABSL_DIE_IF_NULL(dimension)),
5465 model_(ABSL_DIE_IF_NULL(model)),
5466 initializer_(std::move(initializer)),
5467 is_initialized_(dimension->slacks().size(),
false),
5468 initial_values_(dimension->slacks().size(),
5469 std::numeric_limits<int64_t>::min()),
5470 current_index_(model_->Start(0)),
5472 last_delta_used_(dimension->slacks().size(), 0) {}
5474Decision* GuidedSlackFinalizer::Next(Solver* solver) {
5475 CHECK_EQ(solver, model_->solver());
5476 const int node_idx = ChooseVariable();
5477 CHECK(node_idx == -1 ||
5478 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
5479 if (node_idx != -1) {
5480 if (!is_initialized_[node_idx]) {
5481 initial_values_[node_idx] = initializer_(node_idx);
5482 is_initialized_.SetValue(solver, node_idx,
true);
5484 const int64_t value = SelectValue(node_idx);
5485 IntVar*
const slack_variable = dimension_->SlackVar(node_idx);
5486 return solver->MakeAssignVariableValue(slack_variable, value);
5491int64_t GuidedSlackFinalizer::SelectValue(int64_t index) {
5492 const IntVar*
const slack_variable = dimension_->SlackVar(index);
5493 const int64_t center = initial_values_[index];
5494 const int64_t max_delta =
5495 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
5497 int64_t delta = last_delta_used_[index];
5501 while (std::abs(delta) < max_delta &&
5502 !slack_variable->Contains(center + delta)) {
5509 last_delta_used_.SetValue(model_->solver(), index, delta);
5510 return center + delta;
5513int64_t GuidedSlackFinalizer::ChooseVariable() {
5514 int64_t int_current_node = current_index_.Value();
5515 int64_t int_current_route = current_route_.Value();
5517 while (int_current_route < model_->vehicles()) {
5518 while (!model_->IsEnd(int_current_node) &&
5519 dimension_->SlackVar(int_current_node)->Bound()) {
5520 int_current_node = model_->NextVar(int_current_node)->Value();
5522 if (!model_->IsEnd(int_current_node)) {
5525 int_current_route += 1;
5526 if (int_current_route < model_->vehicles()) {
5527 int_current_node = model_->Start(int_current_route);
5531 CHECK(int_current_route == model_->vehicles() ||
5532 !dimension_->SlackVar(int_current_node)->Bound());
5533 current_index_.SetValue(model_->solver(), int_current_node);
5534 current_route_.SetValue(model_->solver(), int_current_route);
5535 if (int_current_route < model_->vehicles()) {
5536 return int_current_node;
5543DecisionBuilder* RoutingModel::MakeGuidedSlackFinalizer(
5544 const RoutingDimension* dimension,
5545 std::function<int64_t(int64_t)> initializer) {
5546 return solver_->RevAlloc(
5547 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
5550int64_t RoutingDimension::ShortestTransitionSlack(int64_t node)
const {
5551 CHECK_EQ(base_dimension_,
this);
5552 CHECK(!model_->IsEnd(node));
5556 const int64_t next = model_->NextVar(node)->Value();
5557 if (model_->IsEnd(next)) {
5558 return SlackVar(node)->Min();
5560 const int64_t next_next = model_->NextVar(next)->Value();
5561 const int64_t serving_vehicle = model_->VehicleVar(node)->Value();
5562 CHECK_EQ(serving_vehicle, model_->VehicleVar(next)->Value());
5563 const RoutingModel::StateDependentTransit transit_from_next =
5564 model_->StateDependentTransitCallback(
5565 state_dependent_class_evaluators_
5566 [state_dependent_vehicle_to_class_[serving_vehicle]])(next,
5569 const int64_t next_cumul_min = CumulVar(next)->Min();
5570 const int64_t next_cumul_max = CumulVar(next)->Max();
5571 const int64_t optimal_next_cumul =
5572 transit_from_next.transit_plus_identity->RangeMinArgument(
5573 next_cumul_min, next_cumul_max + 1);
5575 DCHECK_LE(next_cumul_min, optimal_next_cumul);
5576 DCHECK_LE(optimal_next_cumul, next_cumul_max);
5581 const int64_t current_cumul = CumulVar(node)->Value();
5582 const int64_t current_state_independent_transit = model_->TransitCallback(
5583 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node, next);
5584 const int64_t current_state_dependent_transit =
5586 ->StateDependentTransitCallback(
5587 state_dependent_class_evaluators_
5588 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
5590 .transit->Query(current_cumul);
5591 const int64_t optimal_slack = optimal_next_cumul - current_cumul -
5592 current_state_independent_transit -
5593 current_state_dependent_transit;
5594 CHECK_LE(SlackVar(node)->Min(), optimal_slack);
5595 CHECK_LE(optimal_slack, SlackVar(node)->Max());
5596 return optimal_slack;
5600class GreedyDescentLSOperator :
public LocalSearchOperator {
5602 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
5605 GreedyDescentLSOperator(
const GreedyDescentLSOperator&) =
delete;
5606 GreedyDescentLSOperator& operator=(
const GreedyDescentLSOperator&) =
delete;
5608 bool MakeNextNeighbor(Assignment* delta, Assignment* deltadelta)
override;
5609 void Start(
const Assignment* assignment)
override;
5612 int64_t FindMaxDistanceToDomain(
const Assignment* assignment);
5614 const std::vector<IntVar*> variables_;
5615 const Assignment* center_;
5616 int64_t current_step_;
5623 int64_t current_direction_;
5626GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
5627 : variables_(
std::move(variables)),
5630 current_direction_(0) {}
5632bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment* delta,
5634 static const int64_t sings[] = {1, -1};
5635 for (; 1 <= current_step_; current_step_ /= 2) {
5636 for (; current_direction_ < 2 * variables_.size();) {
5637 const int64_t variable_idx = current_direction_ / 2;
5638 IntVar*
const variable = variables_[variable_idx];
5639 const int64_t sign_index = current_direction_ % 2;
5640 const int64_t sign = sings[sign_index];
5641 const int64_t offset = sign * current_step_;
5642 const int64_t new_value = center_->Value(variable) + offset;
5643 ++current_direction_;
5644 if (variable->Contains(new_value)) {
5645 delta->Add(variable);
5646 delta->SetValue(variable, new_value);
5650 current_direction_ = 0;
5655void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
5656 CHECK(assignment !=
nullptr);
5657 current_step_ = FindMaxDistanceToDomain(assignment);
5658 center_ = assignment;
5661int64_t GreedyDescentLSOperator::FindMaxDistanceToDomain(
5662 const Assignment* assignment) {
5663 int64_t result = std::numeric_limits<int64_t>::min();
5664 for (
const IntVar*
const var : variables_) {
5665 result = std::max(result, std::abs(var->Max() - assignment->Value(var)));
5666 result = std::max(result, std::abs(var->Min() - assignment->Value(var)));
5672std::unique_ptr<LocalSearchOperator> RoutingModel::MakeGreedyDescentLSOperator(
5673 std::vector<IntVar*> variables) {
5674 return std::unique_ptr<LocalSearchOperator>(
5675 new GreedyDescentLSOperator(std::move(variables)));
5678DecisionBuilder* RoutingModel::MakeSelfDependentDimensionFinalizer(
5679 const RoutingDimension* dimension) {
5680 CHECK(dimension !=
nullptr);
5681 CHECK(dimension->base_dimension() == dimension);
5682 DecisionBuilder*
const guided_finalizer =
5683 MakeGuidedSlackFinalizer(dimension, [dimension](int64_t index) {
5684 return dimension->ShortestTransitionSlack(index);
5686 DecisionBuilder*
const slacks_finalizer =
5687 solver_->MakeSolveOnce(guided_finalizer);
5688 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
5689 for (int64_t vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
5690 start_cumuls[vehicle_idx] = dimension->CumulVar(Start(vehicle_idx));
5692 LocalSearchOperator*
const hill_climber =
5693 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
5694 LocalSearchPhaseParameters*
const parameters =
5695 solver_->MakeLocalSearchPhaseParameters(CostVar(), hill_climber,
5697 Assignment*
const first_solution = solver_->MakeAssignment();
5698 first_solution->Add(start_cumuls);
5699 for (IntVar*
const cumul : start_cumuls) {
5700 first_solution->SetValue(cumul, cumul->Min());
5702 DecisionBuilder*
const finalizer =
5703 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
void Copy(const Assignment *assignment)
AssignmentContainer< IntVar, IntVarElement > IntContainer
const IntContainer & IntVarContainer() const
int64_t ObjectiveValue() const
bool RemoveItemFromBin(int item, int bin)
Removes item from bin, return whether the bin is feasible.
bool CheckAdditionFeasibility(int item, int bin) const
Checks whether adding item(s) is feasible w.r.t. dimensions.
int64_t TotalCost() const
Returns the total cost incurred by violating soft costs.
bool AddItemToBin(int item, int bin)
IndexType size() const
Returns how many bits this Bitset64 can hold.
void Set(IndexType i)
Sets the bit at position i to 1.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
CheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
CheapestAdditionFilteredHeuristic.
int64_t GetInsertionCostForNodeAtPosition(int64_t node_to_insert, int64_t insert_after, int64_t insert_before, int vehicle) const
std::function< int64_t(int64_t, int64_t, int64_t)> evaluator_
std::vector< std::vector< StartEndValue > > ComputeStartEndDistanceForVehicles(absl::Span< const int > vehicles)
bool HasHintedNext(int node) const
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)
CheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
void AppendInsertionPositionsAfter(int64_t node_to_insert, int64_t start, int64_t next_after_start, int vehicle, bool ignore_cost, std::vector< NodeInsertion > *node_insertions)
int64_t GetUnperformedValue(int64_t node_to_insert) const
void InsertBetween(int64_t node, int64_t predecessor, int64_t successor, int vehicle=-1)
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)
clang-format on
ChristofidesFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager, bool use_minimum_matching)
ChristofidesFilteredHeuristic.
bool BuildSolutionInternal() override
void SetMatchingAlgorithm(MatchingAlgorithm matching)
std::vector< NodeIndex > TravelingSalesmanPath()
Returns the approximate TSP tour.
ComparatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, Solver::VariableValueComparator comparator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
EvaluatorCheapestAdditionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t)> evaluator, LocalSearchFilterManager *filter_manager)
Takes ownership of evaluator.
NodeEntryQueue(int num_nodes)
void PushInsertion(int64_t node, int64_t insert_after, int vehicle, int bucket, int64_t value)
bool IsEmpty(int64_t insert_after) const
void ClearInsertions(int64_t insert_after)
GlobalCheapestInsertionFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, std::function< int64_t(int64_t, int64_t, int64_t)> evaluator, std::function< int64_t(int64_t)> penalty_evaluator, LocalSearchFilterManager *filter_manager, GlobalCheapestInsertionParameters parameters)
Takes ownership of evaluators.
bool BuildSolutionInternal() override
Virtual method to redefine how to build a solution.
void AddInsertionSequence(int vehicle, std::initializer_list< Insertion > insertion_sequence)
void AppendPickupDeliveryMultitourInsertions(int pickup, int delivery, int vehicle, const std::vector< int > &path, const std::vector< bool > &path_node_is_pickup, const std::vector< bool > &path_node_is_delivery, InsertionSequenceContainer &insertions)
virtual bool Bound() const
Returns true if the min and the max of the expression are equal.
virtual int64_t Min() const =0
int64_t number_of_rejects() const
int64_t number_of_decisions() const
Returns statistics from its underlying heuristic.
std::string DebugString() const override
-------— Decision Builder -------—
Decision * Next(Solver *solver) override
IntVarFilteredDecisionBuilder(std::unique_ptr< IntVarFilteredHeuristic > heuristic)
— First solution decision builder —
bool HasSecondaryVars() const
Returns true if there are secondary variables.
Assignment *const assignment_
void ResetSolution()
Resets the data members for a new solution.
IntVarFilteredHeuristic(Solver *solver, const std::vector< IntVar * > &vars, const std::vector< IntVar * > &secondary_vars, LocalSearchFilterManager *filter_manager)
— First solution heuristics —
virtual bool BuildSolutionInternal()=0
Virtual method to redefine how to build a solution.
bool Contains(int64_t index) const
Returns true if the variable of index 'index' is in the current solution.
virtual void Initialize()
Initialize the heuristic; called before starting to build a new solution.
Assignment * BuildSolution()
int64_t SecondaryVarIndex(int64_t index) const
Returns the index of a secondary var.
int64_t Value(int64_t index) const
void SetValue(int64_t index, int64_t value)
virtual bool InitializeSolution()
Virtual method to initialize the solution.
const std::vector< int > & delta_indices() const
Returns the indices of the nodes currently in the insertion delta.
IntVar * Var(int64_t index) const
Returns the variable of index 'index'.
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
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, RoutingSearchParameters::PairInsertionStrategy pair_insertion_strategy, std::vector< RoutingSearchParameters::InsertionSortingProperty > insertion_sorting_properties, 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.
bool Accept(LocalSearchMonitor *monitor, const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max)
-------— Local Search Phase Parameters -------—
static IntOut SafeRound(FloatIn x)
int GetEndChainStart(int vehicle) const
Returns the start of the end chain of vehicle,.
virtual void ResetVehicleIndices()
virtual void SetVehicleIndex(int64_t, int)
Assignment * BuildSolutionFromRoutes(const std::function< int64_t(int64_t)> &next_accessor)
Builds a solution starting from the routes formed by the next accessor.
void SetNext(int64_t node, int64_t next, int vehicle)
bool VehicleIsEmpty(int vehicle) const
void MakeDisjunctionNodesUnperformed(int64_t node)
bool MakeUnassignedNodesUnperformed()
Make all unassigned nodes unperformed, always returns true.
RoutingFilteredHeuristic(RoutingModel *model, std::function< bool()> stop_search, LocalSearchFilterManager *filter_manager)
RoutingFilteredHeuristic.
int GetStartChainEnd(int vehicle) const
Returns the end of the start chain of vehicle,.
RoutingModel * model() const
bool StopSearch() override
Returns true if the search must be stopped.
void AddUnassignedNodesToEmptyVehicles()
Adds all unassigned nodes to empty vehicles.
void MakePartiallyPerformedPairsUnperformed()
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.
~SavingsFilteredHeuristic() override
virtual double ExtraSavingsMemoryMultiplicativeFactor() const =0
int StartNewRouteWithBestVehicleOfType(int type, int64_t before_node, int64_t after_node)
std::unique_ptr< SavingsContainer< Saving > > savings_container_
clang-format off
std::unique_ptr< VehicleTypeCurator > vehicle_type_curator_
clang-format on
For the time being, Solver is neither MT_SAFE nor MT_HOT.
void Fail()
Abandon the current branch in the search tree. A backtrack will follow.
LocalSearchMonitor * GetLocalSearchMonitor() const
Returns the local search monitor.
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)
— VehicleTypeCurator —
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
void STLClearObject(T *obj)
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
In SWIG mode, we don't want anything besides these top-level includes.
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)
Same as above but taking alternative parameters for each alternative model.
DecisionBuilder * MakeAllUnperformed(RoutingModel *model)
Returns a DecisionBuilder making all nodes unperformed.
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)
const Assignment * SolveFromAssignmentWithAlternativeSolvers(const Assignment *assignment, RoutingModel *primary_model, const std::vector< RoutingModel * > &alternative_models, const RoutingSearchParameters ¶meters, int max_non_improving_iterations)
Same as above, but taking an initial solution.
static const int kUnassigned
--— Routing model --—
DecisionBuilder * MakeSweepDecisionBuilder(RoutingModel *model, bool check_assignment)
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
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)
Note(user): -kint64min != kint64max, but kint64max == ~kint64min.
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
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
static const int64_t kint64max