26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
43 "Run stronger checks in debug; these stronger tests might change "
44 "the complexity of the code in particular.");
45 ABSL_FLAG(
bool, routing_shift_insertion_cost_by_penalty,
true,
46 "Shift insertion costs by the penalty of the inserted node(s).");
54 class MaxActiveVehiclesFilter :
public IntVarLocalSearchFilter {
56 explicit MaxActiveVehiclesFilter(
const RoutingModel& routing_model)
57 : IntVarLocalSearchFilter(routing_model.Nexts()),
58 routing_model_(routing_model),
59 is_active_(routing_model.vehicles(), false),
60 active_vehicles_(0) {}
61 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
62 int64 objective_min,
int64 objective_max)
override {
65 const int delta_size = container.Size();
66 int current_active_vehicles = active_vehicles_;
67 for (
int i = 0; i < delta_size; ++i) {
68 const IntVarElement& new_element = container.Element(i);
69 IntVar*
const var = new_element.Var();
71 if (FindIndex(
var, &
index) && routing_model_.IsStart(
index)) {
72 if (new_element.Min() != new_element.Max()) {
76 const int vehicle = routing_model_.VehicleIndex(
index);
77 const bool is_active =
78 (new_element.Min() != routing_model_.End(vehicle));
79 if (is_active && !is_active_[vehicle]) {
80 ++current_active_vehicles;
81 }
else if (!is_active && is_active_[vehicle]) {
82 --current_active_vehicles;
86 return current_active_vehicles <=
87 routing_model_.GetMaximumNumberOfActiveVehicles();
91 void OnSynchronize(
const Assignment*
delta)
override {
93 for (
int i = 0; i < routing_model_.vehicles(); ++i) {
94 const int index = routing_model_.Start(i);
99 is_active_[i] =
false;
104 const RoutingModel& routing_model_;
105 std::vector<bool> is_active_;
106 int active_vehicles_;
113 new MaxActiveVehiclesFilter(routing_model));
120 class NodeDisjunctionFilter :
public IntVarLocalSearchFilter {
122 explicit NodeDisjunctionFilter(
const RoutingModel& routing_model)
123 : IntVarLocalSearchFilter(routing_model.Nexts()),
124 routing_model_(routing_model),
125 active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
126 inactive_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
127 synchronized_objective_value_(
kint64min),
130 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
131 int64 objective_min,
int64 objective_max)
override {
134 const int delta_size = container.Size();
136 disjunction_active_deltas;
138 disjunction_inactive_deltas;
139 bool lns_detected =
false;
141 for (
int i = 0; i < delta_size; ++i) {
142 const IntVarElement& new_element = container.Element(i);
143 IntVar*
const var = new_element.Var();
146 const bool is_inactive =
147 (new_element.Min() <=
index && new_element.Max() >=
index);
148 if (new_element.Min() != new_element.Max()) {
152 routing_model_.GetDisjunctionIndices(
index)) {
153 const bool active_state_changed =
155 if (active_state_changed) {
158 disjunction_index, 0);
159 if (IsVarSynced(
index)) {
161 disjunction_index, 0);
165 disjunction_index, 0);
166 if (IsVarSynced(
index)) {
168 disjunction_index, 0);
176 for (
const std::pair<RoutingModel::DisjunctionIndex, int>
177 disjunction_active_delta : disjunction_active_deltas) {
178 const int current_active_nodes =
179 active_per_disjunction_[disjunction_active_delta.first];
180 const int active_nodes =
181 current_active_nodes + disjunction_active_delta.second;
182 const int max_cardinality = routing_model_.GetDisjunctionMaxCardinality(
183 disjunction_active_delta.first);
185 if (active_nodes > max_cardinality) {
190 accepted_objective_value_ = synchronized_objective_value_;
191 for (
const std::pair<RoutingModel::DisjunctionIndex, int>
192 disjunction_inactive_delta : disjunction_inactive_deltas) {
193 const int64 penalty = routing_model_.GetDisjunctionPenalty(
194 disjunction_inactive_delta.first);
195 if (penalty != 0 && !lns_detected) {
197 disjunction_inactive_delta.first;
198 const int current_inactive_nodes =
199 inactive_per_disjunction_[disjunction_index];
200 const int inactive_nodes =
201 current_inactive_nodes + disjunction_inactive_delta.second;
202 const int max_inactive_cardinality =
203 routing_model_.GetDisjunctionIndices(disjunction_index).size() -
204 routing_model_.GetDisjunctionMaxCardinality(disjunction_index);
206 if (inactive_nodes > max_inactive_cardinality) {
211 }
else if (current_inactive_nodes <= max_inactive_cardinality) {
214 accepted_objective_value_ =
215 CapAdd(accepted_objective_value_, penalty);
217 }
else if (current_inactive_nodes > max_inactive_cardinality) {
220 accepted_objective_value_ =
221 CapSub(accepted_objective_value_, penalty);
226 accepted_objective_value_ = 0;
230 return accepted_objective_value_ <= objective_max;
233 std::string DebugString()
const override {
return "NodeDisjunctionFilter"; }
234 int64 GetSynchronizedObjectiveValue()
const override {
235 return synchronized_objective_value_;
237 int64 GetAcceptedObjectiveValue()
const override {
238 return accepted_objective_value_;
242 void OnSynchronize(
const Assignment*
delta)
override {
243 synchronized_objective_value_ = 0;
245 i < active_per_disjunction_.size(); ++i) {
246 active_per_disjunction_[i] = 0;
247 inactive_per_disjunction_[i] = 0;
248 const std::vector<int64>& disjunction_indices =
249 routing_model_.GetDisjunctionIndices(i);
250 for (
const int64 index : disjunction_indices) {
251 const bool index_synced = IsVarSynced(
index);
254 ++active_per_disjunction_[i];
256 ++inactive_per_disjunction_[i];
260 const int64 penalty = routing_model_.GetDisjunctionPenalty(i);
261 const int max_cardinality =
262 routing_model_.GetDisjunctionMaxCardinality(i);
263 if (inactive_per_disjunction_[i] >
264 disjunction_indices.size() - max_cardinality &&
266 synchronized_objective_value_ =
267 CapAdd(synchronized_objective_value_, penalty);
272 const RoutingModel& routing_model_;
275 active_per_disjunction_;
277 inactive_per_disjunction_;
278 int64 synchronized_objective_value_;
279 int64 accepted_objective_value_;
286 new NodeDisjunctionFilter(routing_model));
292 int next_domain_size)
295 paths_(nexts.size(), -1),
296 new_synchronized_unperformed_nodes_(nexts.size()),
298 touched_paths_(nexts.size()),
300 ranks_(next_domain_size, -1),
301 status_(BasePathFilter::UNKNOWN) {}
305 int64 objective_max) {
307 for (
const int touched : delta_touched_) {
310 delta_touched_.clear();
312 const int delta_size = container.
Size();
313 delta_touched_.reserve(delta_size);
325 const auto update_touched_path_chain_start_end = [
this](
int64 index) {
328 touched_paths_.
Set(start);
330 int64& chain_start = touched_path_chain_start_ends_[start].first;
335 int64& chain_end = touched_path_chain_start_ends_[start].second;
341 for (
int i = 0; i < delta_size; ++i) {
346 if (!new_element.
Bound()) {
351 delta_touched_.push_back(
index);
352 update_touched_path_chain_start_end(
index);
353 update_touched_path_chain_start_end(new_nexts_[
index]);
357 InitializeAcceptPath();
360 const std::pair<int64, int64> start_end =
361 touched_path_chain_start_ends_[touched_start];
362 if (!AcceptPath(touched_start, start_end.first, start_end.second)) {
368 return accept && FinalizeAcceptPath(
delta, objective_min, objective_max);
371 void BasePathFilter::ComputePathStarts(std::vector<int64>* path_starts,
372 std::vector<int>* index_to_path) {
373 path_starts->clear();
374 const int nexts_size =
Size();
377 for (
int i = 0; i < nexts_size; ++i) {
382 if (
next < nexts_size) {
387 for (
int i = 0; i < nexts_size; ++i) {
389 (*index_to_path)[i] = path_starts->size();
390 path_starts->push_back(i);
395 bool BasePathFilter::HavePathsChanged() {
396 std::vector<int64> path_starts;
398 ComputePathStarts(&path_starts, &index_to_path);
399 if (path_starts.size() != starts_.size()) {
402 for (
int i = 0; i < path_starts.size(); ++i) {
403 if (path_starts[i] != starts_[i]) {
407 for (
int i = 0; i <
Size(); ++i) {
408 if (index_to_path[i] != paths_[i]) {
415 void BasePathFilter::SynchronizeFullAssignment() {
419 ComputePathStarts(&starts_, &paths_);
424 new_synchronized_unperformed_nodes_.
Set(
index);
428 node_path_starts_.assign(node_path_starts_.size(),
kUnassigned);
430 const int nexts_size =
Size();
431 for (
const int64 start : starts_) {
433 node_path_starts_[node] = start;
436 while (
next < nexts_size) {
438 node_path_starts_[node] = start;
442 node_path_starts_[
next] = start;
444 OnBeforeSynchronizePaths();
446 OnAfterSynchronizePaths();
450 if (status_ == BasePathFilter::UNKNOWN) {
452 DisableFiltering() ? BasePathFilter::DISABLED : BasePathFilter::ENABLED;
455 new_synchronized_unperformed_nodes_.
ClearAll();
456 if (
delta ==
nullptr ||
delta->Empty() || starts_.empty()) {
457 SynchronizeFullAssignment();
464 DCHECK(!absl::GetFlag(FLAGS_routing_strong_debug_checks) ||
465 !HavePathsChanged());
468 for (
int i = 0; i < container.
Size(); ++i) {
474 touched_paths_.
Set(start);
478 new_synchronized_unperformed_nodes_.
Set(
index);
484 OnBeforeSynchronizePaths();
486 int64 node = touched_start;
487 while (node <
Size()) {
488 node_path_starts_[node] = touched_start;
491 node_path_starts_[node] = touched_start;
492 UpdatePathRanksFromStart(touched_start);
493 OnSynchronizePathFromStart(touched_start);
495 OnAfterSynchronizePaths();
498 void BasePathFilter::UpdateAllRanks() {
499 for (
int i = 0; i < ranks_.size(); ++i) {
502 for (
int r = 0; r <
NumPaths(); ++r) {
503 UpdatePathRanksFromStart(
Start(r));
504 OnSynchronizePathFromStart(
Start(r));
508 void BasePathFilter::UpdatePathRanksFromStart(
int start) {
511 while (node <
Size()) {
521 class VehicleAmortizedCostFilter :
public BasePathFilter {
523 explicit VehicleAmortizedCostFilter(
const RoutingModel& routing_model);
524 ~VehicleAmortizedCostFilter()
override {}
525 std::string DebugString()
const override {
526 return "VehicleAmortizedCostFilter";
528 int64 GetSynchronizedObjectiveValue()
const override {
529 return current_vehicle_cost_;
531 int64 GetAcceptedObjectiveValue()
const override {
532 return delta_vehicle_cost_;
536 void OnSynchronizePathFromStart(
int64 start)
override;
537 void OnAfterSynchronizePaths()
override;
538 void InitializeAcceptPath()
override;
539 bool AcceptPath(
int64 path_start,
int64 chain_start,
540 int64 chain_end)
override;
541 bool FinalizeAcceptPath(
const Assignment*
delta,
int64 objective_min,
542 int64 objective_max)
override;
544 int64 current_vehicle_cost_;
545 int64 delta_vehicle_cost_;
546 std::vector<int> current_route_lengths_;
547 std::vector<int64> start_to_end_;
548 std::vector<int> start_to_vehicle_;
549 std::vector<int64> vehicle_to_start_;
550 const std::vector<int64>& linear_cost_factor_of_vehicle_;
551 const std::vector<int64>& quadratic_cost_factor_of_vehicle_;
554 VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
555 const RoutingModel& routing_model)
556 : BasePathFilter(routing_model.Nexts(),
557 routing_model.Size() + routing_model.vehicles()),
558 current_vehicle_cost_(0),
559 delta_vehicle_cost_(0),
560 current_route_lengths_(Size(), -1),
561 linear_cost_factor_of_vehicle_(
562 routing_model.GetAmortizedLinearCostFactorOfVehicles()),
563 quadratic_cost_factor_of_vehicle_(
564 routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) {
565 start_to_end_.resize(
Size(), -1);
566 start_to_vehicle_.resize(
Size(), -1);
567 vehicle_to_start_.resize(routing_model.vehicles());
568 for (
int v = 0; v < routing_model.vehicles(); v++) {
569 const int64 start = routing_model.Start(v);
570 start_to_vehicle_[start] = v;
571 start_to_end_[start] = routing_model.End(v);
572 vehicle_to_start_[v] = start;
576 void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(
int64 start) {
577 const int64 end = start_to_end_[start];
579 const int route_length = Rank(end) - 1;
581 current_route_lengths_[start] = route_length;
584 void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
585 current_vehicle_cost_ = 0;
586 for (
int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
587 const int64 start = vehicle_to_start_[vehicle];
588 DCHECK_EQ(vehicle, start_to_vehicle_[start]);
590 const int route_length = current_route_lengths_[start];
593 if (route_length == 0) {
598 const int64 linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle];
599 const int64 route_length_cost =
600 CapProd(quadratic_cost_factor_of_vehicle_[vehicle],
601 route_length * route_length);
603 current_vehicle_cost_ =
CapAdd(
604 current_vehicle_cost_,
CapSub(linear_cost_factor, route_length_cost));
608 void VehicleAmortizedCostFilter::InitializeAcceptPath() {
609 delta_vehicle_cost_ = current_vehicle_cost_;
612 bool VehicleAmortizedCostFilter::AcceptPath(
int64 path_start,
int64 chain_start,
615 const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start);
617 int new_chain_nodes = 0;
618 int64 node = GetNext(chain_start);
619 while (node != chain_end) {
621 node = GetNext(node);
624 const int previous_route_length = current_route_lengths_[path_start];
626 const int new_route_length =
627 previous_route_length - previous_chain_nodes + new_chain_nodes;
629 const int vehicle = start_to_vehicle_[path_start];
631 DCHECK_EQ(path_start, vehicle_to_start_[vehicle]);
635 if (previous_route_length == 0) {
638 delta_vehicle_cost_ =
639 CapAdd(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
640 }
else if (new_route_length == 0) {
642 delta_vehicle_cost_ =
643 CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
647 const int64 quadratic_cost_factor =
648 quadratic_cost_factor_of_vehicle_[vehicle];
649 delta_vehicle_cost_ =
650 CapAdd(delta_vehicle_cost_,
652 previous_route_length * previous_route_length));
653 delta_vehicle_cost_ =
CapSub(
655 CapProd(quadratic_cost_factor, new_route_length * new_route_length));
660 bool VehicleAmortizedCostFilter::FinalizeAcceptPath(
const Assignment*
delta,
662 int64 objective_max) {
663 return delta_vehicle_cost_ <= objective_max;
671 new VehicleAmortizedCostFilter(routing_model));
676 class TypeRegulationsFilter :
public BasePathFilter {
678 explicit TypeRegulationsFilter(
const RoutingModel&
model);
679 ~TypeRegulationsFilter()
override {}
680 std::string DebugString()
const override {
return "TypeRegulationsFilter"; }
683 void OnSynchronizePathFromStart(
int64 start)
override;
684 bool AcceptPath(
int64 path_start,
int64 chain_start,
685 int64 chain_end)
override;
687 bool HardIncompatibilitiesRespected(
int vehicle,
int64 chain_start,
690 const RoutingModel& routing_model_;
691 std::vector<int> start_to_vehicle_;
694 std::vector<std::vector<int>> hard_incompatibility_type_counts_per_vehicle_;
696 TypeIncompatibilityChecker temporal_incompatibility_checker_;
697 TypeRequirementChecker requirement_checker_;
700 TypeRegulationsFilter::TypeRegulationsFilter(
const RoutingModel&
model)
702 routing_model_(
model),
703 start_to_vehicle_(
model.Size(), -1),
704 temporal_incompatibility_checker_(
model,
706 requirement_checker_(
model) {
707 const int num_vehicles =
model.vehicles();
708 const bool has_hard_type_incompatibilities =
709 model.HasHardTypeIncompatibilities();
710 if (has_hard_type_incompatibilities) {
711 hard_incompatibility_type_counts_per_vehicle_.resize(num_vehicles);
713 const int num_visit_types =
model.GetNumberOfVisitTypes();
714 for (
int vehicle = 0; vehicle < num_vehicles; vehicle++) {
716 start_to_vehicle_[start] = vehicle;
717 if (has_hard_type_incompatibilities) {
718 hard_incompatibility_type_counts_per_vehicle_[vehicle].resize(
724 void TypeRegulationsFilter::OnSynchronizePathFromStart(
int64 start) {
725 if (!routing_model_.HasHardTypeIncompatibilities())
return;
727 const int vehicle = start_to_vehicle_[start];
729 std::vector<int>& type_counts =
730 hard_incompatibility_type_counts_per_vehicle_[vehicle];
731 std::fill(type_counts.begin(), type_counts.end(), 0);
732 const int num_types = type_counts.size();
735 while (node < Size()) {
736 DCHECK(IsVarSynced(node));
737 const int type = routing_model_.GetVisitType(node);
738 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
739 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
747 bool TypeRegulationsFilter::HardIncompatibilitiesRespected(
int vehicle,
750 if (!routing_model_.HasHardTypeIncompatibilities())
return true;
752 const std::vector<int>& previous_type_counts =
753 hard_incompatibility_type_counts_per_vehicle_[vehicle];
755 absl::flat_hash_map< int,
int> new_type_counts;
756 absl::flat_hash_set<int> types_to_check;
759 int64 node = GetNext(chain_start);
760 while (node != chain_end) {
761 const int type = routing_model_.GetVisitType(node);
762 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
763 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
764 DCHECK_LT(type, previous_type_counts.size());
766 previous_type_counts[type]);
767 if (type_count++ == 0) {
769 types_to_check.insert(type);
772 node = GetNext(node);
777 node =
Value(chain_start);
778 while (node != chain_end) {
779 const int type = routing_model_.GetVisitType(node);
780 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
781 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
782 DCHECK_LT(type, previous_type_counts.size());
784 previous_type_counts[type]);
792 for (
int type : types_to_check) {
793 for (
int incompatible_type :
794 routing_model_.GetHardTypeIncompatibilitiesOfType(type)) {
796 previous_type_counts[incompatible_type]) > 0) {
804 bool TypeRegulationsFilter::AcceptPath(
int64 path_start,
int64 chain_start,
806 const int vehicle = start_to_vehicle_[path_start];
808 const auto next_accessor = [
this](
int64 node) {
return GetNext(node); };
809 return HardIncompatibilitiesRespected(vehicle, chain_start, chain_end) &&
810 temporal_incompatibility_checker_.CheckVehicle(vehicle,
812 requirement_checker_.CheckVehicle(vehicle, next_accessor);
820 new TypeRegulationsFilter(routing_model));
830 class ChainCumulFilter :
public BasePathFilter {
832 ChainCumulFilter(
const RoutingModel& routing_model,
833 const RoutingDimension& dimension);
834 ~ChainCumulFilter()
override {}
835 std::string DebugString()
const override {
836 return "ChainCumulFilter(" + name_ +
")";
840 void OnSynchronizePathFromStart(
int64 start)
override;
841 bool AcceptPath(
int64 path_start,
int64 chain_start,
842 int64 chain_end)
override;
844 const std::vector<IntVar*>
cumuls_;
845 std::vector<int64> start_to_vehicle_;
846 std::vector<int64> start_to_end_;
847 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
848 const std::vector<int64> vehicle_capacities_;
849 std::vector<int64> current_path_cumul_mins_;
850 std::vector<int64> current_max_of_path_end_cumul_mins_;
851 std::vector<int64> old_nexts_;
852 std::vector<int> old_vehicles_;
853 std::vector<int64> current_transits_;
854 const std::string name_;
857 ChainCumulFilter::ChainCumulFilter(
const RoutingModel& routing_model,
858 const RoutingDimension& dimension)
859 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
861 evaluators_(routing_model.vehicles(), nullptr),
862 vehicle_capacities_(dimension.vehicle_capacities()),
863 current_path_cumul_mins_(dimension.cumuls().size(), 0),
864 current_max_of_path_end_cumul_mins_(dimension.cumuls().size(), 0),
867 current_transits_(routing_model.Size(), 0),
868 name_(dimension.
name()) {
869 start_to_vehicle_.resize(Size(), -1);
870 start_to_end_.resize(Size(), -1);
871 for (
int i = 0; i < routing_model.vehicles(); ++i) {
872 start_to_vehicle_[routing_model.Start(i)] = i;
873 start_to_end_[routing_model.Start(i)] = routing_model.End(i);
874 evaluators_[i] = &dimension.transit_evaluator(i);
881 void ChainCumulFilter::OnSynchronizePathFromStart(
int64 start) {
882 const int vehicle = start_to_vehicle_[start];
883 std::vector<int64> path_nodes;
886 while (node < Size()) {
887 path_nodes.push_back(node);
888 current_path_cumul_mins_[node] = cumul;
890 if (
next != old_nexts_[node] || vehicle != old_vehicles_[node]) {
891 old_nexts_[node] =
next;
892 old_vehicles_[node] = vehicle;
893 current_transits_[node] = (*evaluators_[vehicle])(node,
next);
895 cumul =
CapAdd(cumul, current_transits_[node]);
899 path_nodes.push_back(node);
900 current_path_cumul_mins_[node] = cumul;
901 int64 max_cumuls = cumul;
902 for (
int i = path_nodes.size() - 1; i >= 0; --i) {
903 const int64 node = path_nodes[i];
904 max_cumuls =
std::max(max_cumuls, current_path_cumul_mins_[node]);
905 current_max_of_path_end_cumul_mins_[node] = max_cumuls;
910 bool ChainCumulFilter::AcceptPath(
int64 path_start,
int64 chain_start,
912 const int vehicle = start_to_vehicle_[path_start];
914 int64 node = chain_start;
915 int64 cumul = current_path_cumul_mins_[node];
916 while (node != chain_end) {
918 if (IsVarSynced(node) &&
next ==
Value(node) &&
919 vehicle == old_vehicles_[node]) {
920 cumul =
CapAdd(cumul, current_transits_[node]);
922 cumul =
CapAdd(cumul, (*evaluators_[vehicle])(node,
next));
928 const int64 end = start_to_end_[path_start];
929 const int64 end_cumul_delta =
930 CapSub(current_path_cumul_mins_[end], current_path_cumul_mins_[node]);
931 const int64 after_chain_cumul_delta =
932 CapSub(current_max_of_path_end_cumul_mins_[node],
933 current_path_cumul_mins_[node]);
940 class PathCumulFilter :
public BasePathFilter {
942 PathCumulFilter(
const RoutingModel& routing_model,
943 const RoutingDimension& dimension,
945 bool propagate_own_objective_value,
946 bool filter_objective_cost,
bool can_use_lp);
947 ~PathCumulFilter()
override {}
948 std::string DebugString()
const override {
949 return "PathCumulFilter(" + name_ +
")";
951 int64 GetSynchronizedObjectiveValue()
const override {
952 return propagate_own_objective_value_ ? synchronized_objective_value_ : 0;
954 int64 GetAcceptedObjectiveValue()
const override {
955 return propagate_own_objective_value_ ? accepted_objective_value_ : 0;
962 struct SupportedPathCumul {
983 void ClearPath(
int path) {
984 paths_[path].clear();
985 transits_[path].clear();
987 int AddPaths(
int num_paths) {
988 const int first_path = paths_.size();
989 paths_.resize(first_path + num_paths);
990 transits_.resize(first_path + num_paths);
993 void ReserveTransits(
int path,
int number_of_route_arcs) {
994 transits_[path].reserve(number_of_route_arcs);
995 paths_[path].reserve(number_of_route_arcs + 1);
999 void PushTransit(
int path,
int node,
int next,
int64 transit) {
1000 transits_[path].push_back(transit);
1001 if (paths_[path].empty()) {
1002 paths_[path].push_back(node);
1005 paths_[path].push_back(
next);
1007 int NumPaths()
const {
return paths_.size(); }
1008 int PathSize(
int path)
const {
return paths_[path].size(); }
1009 int Node(
int path,
int position)
const {
return paths_[path][position]; }
1010 int64 Transit(
int path,
int position)
const {
1011 return transits_[path][position];
1016 std::vector<std::vector<int64>> paths_;
1019 std::vector<std::vector<int64>> transits_;
1022 void InitializeAcceptPath()
override {
1023 cumul_cost_delta_ = total_current_cumul_cost_value_;
1024 node_with_precedence_to_delta_min_max_cumuls_.clear();
1027 delta_paths_.clear();
1028 delta_path_transits_.Clear();
1029 lns_detected_ =
false;
1030 delta_nodes_with_precedences_and_changed_cumul_.
ClearAll();
1032 bool AcceptPath(
int64 path_start,
int64 chain_start,
1033 int64 chain_end)
override;
1034 bool FinalizeAcceptPath(
const Assignment*
delta,
int64 objective_min,
1035 int64 objective_max)
override;
1036 void OnBeforeSynchronizePaths()
override;
1038 bool FilterSpanCost()
const {
return global_span_cost_coefficient_ != 0; }
1040 bool FilterSlackCost()
const {
1041 return has_nonzero_vehicle_span_cost_coefficients_ ||
1042 has_vehicle_span_upper_bounds_;
1045 bool FilterBreakCost(
int vehicle)
const {
1046 return dimension_.HasBreakConstraints() &&
1047 !dimension_.GetBreakIntervalsOfVehicle(vehicle).empty();
1050 bool FilterCumulSoftBounds()
const {
return !cumul_soft_bounds_.empty(); }
1054 bool FilterCumulPiecewiseLinearCosts()
const {
1055 return !cumul_piecewise_linear_costs_.empty();
1058 bool FilterWithDimensionCumulOptimizerForVehicle(
int vehicle)
const {
1059 if (!can_use_lp_ || FilterCumulPiecewiseLinearCosts()) {
1063 int num_linear_constraints = 0;
1064 if (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0)
1065 ++num_linear_constraints;
1066 if (FilterSoftSpanCost(vehicle)) ++num_linear_constraints;
1067 if (FilterCumulSoftLowerBounds()) ++num_linear_constraints;
1068 if (FilterCumulSoftBounds()) ++num_linear_constraints;
1069 if (vehicle_span_upper_bounds_[vehicle] <
kint64max)
1070 ++num_linear_constraints;
1071 const bool has_breaks = FilterBreakCost(vehicle);
1072 if (has_breaks) ++num_linear_constraints;
1082 return num_linear_constraints >= 2 &&
1083 (has_breaks || filter_objective_cost_);
1086 bool FilterDimensionForbiddenIntervals()
const {
1087 for (
const SortedDisjointIntervalList& intervals :
1088 dimension_.forbidden_intervals()) {
1091 if (intervals.NumIntervals() > 0) {
1100 bool FilterCumulSoftLowerBounds()
const {
1101 return !cumul_soft_lower_bounds_.empty();
1104 bool FilterPrecedences()
const {
return !node_index_to_precedences_.empty(); }
1106 bool FilterSoftSpanCost()
const {
1107 return dimension_.HasSoftSpanUpperBounds();
1109 bool FilterSoftSpanCost(
int vehicle)
const {
1110 return dimension_.HasSoftSpanUpperBounds() &&
1111 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle).cost > 0;
1113 bool FilterSoftSpanQuadraticCost()
const {
1114 return dimension_.HasQuadraticCostSoftSpanUpperBounds();
1116 bool FilterSoftSpanQuadraticCost(
int vehicle)
const {
1117 return dimension_.HasQuadraticCostSoftSpanUpperBounds() &&
1118 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle)
1124 int64 GetPathCumulSoftLowerBoundCost(
const PathTransits& path_transits,
1127 void InitializeSupportedPathCumul(SupportedPathCumul* supported_cumul,
1128 int64 default_value);
1138 bool PickupToDeliveryLimitsRespected(
1139 const PathTransits& path_transits,
int path,
1140 const std::vector<int64>& min_path_cumuls)
const;
1150 void StoreMinMaxCumulOfNodesOnPath(
int path,
1151 const std::vector<int64>& min_path_cumuls,
1161 int64 ComputePathMaxStartFromEndCumul(
const PathTransits& path_transits,
1162 int path,
int64 path_start,
1163 int64 min_end_cumul)
const;
1165 const RoutingModel& routing_model_;
1166 const RoutingDimension& dimension_;
1167 const std::vector<IntVar*>
cumuls_;
1168 const std::vector<IntVar*> slacks_;
1169 std::vector<int64> start_to_vehicle_;
1170 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1171 std::vector<int64> vehicle_span_upper_bounds_;
1172 bool has_vehicle_span_upper_bounds_;
1173 int64 total_current_cumul_cost_value_;
1174 int64 synchronized_objective_value_;
1175 int64 accepted_objective_value_;
1178 absl::flat_hash_map<int64, int64> current_cumul_cost_values_;
1179 int64 cumul_cost_delta_;
1181 std::vector<int64> delta_path_cumul_cost_values_;
1182 const int64 global_span_cost_coefficient_;
1183 std::vector<SoftBound> cumul_soft_bounds_;
1184 std::vector<SoftBound> cumul_soft_lower_bounds_;
1185 std::vector<const PiecewiseLinearFunction*> cumul_piecewise_linear_costs_;
1186 std::vector<int64> vehicle_span_cost_coefficients_;
1187 bool has_nonzero_vehicle_span_cost_coefficients_;
1188 const std::vector<int64> vehicle_capacities_;
1192 std::vector<std::vector<RoutingDimension::NodePrecedence>>
1193 node_index_to_precedences_;
1196 SupportedPathCumul current_min_start_;
1197 SupportedPathCumul current_max_end_;
1198 PathTransits current_path_transits_;
1200 std::vector<std::pair<int64, int64>> current_min_max_node_cumuls_;
1203 PathTransits delta_path_transits_;
1204 int64 delta_max_end_cumul_;
1205 SparseBitset<int64> delta_nodes_with_precedences_and_changed_cumul_;
1206 absl::flat_hash_map<int64, std::pair<int64, int64>>
1207 node_with_precedence_to_delta_min_max_cumuls_;
1210 const std::string name_;
1212 LocalDimensionCumulOptimizer* optimizer_;
1213 std::unique_ptr<LocalDimensionCumulOptimizer> internal_optimizer_;
1214 LocalDimensionCumulOptimizer* mp_optimizer_;
1215 std::unique_ptr<LocalDimensionCumulOptimizer> internal_mp_optimizer_;
1216 const bool filter_objective_cost_;
1219 const bool can_use_lp_;
1220 const bool propagate_own_objective_value_;
1223 DisjunctivePropagator disjunctive_propagator_;
1224 DisjunctivePropagator::Tasks tasks_;
1225 TravelBounds travel_bounds_;
1226 std::vector<int64> current_path_;
1231 PathCumulFilter::PathCumulFilter(
const RoutingModel& routing_model,
1232 const RoutingDimension& dimension,
1234 bool propagate_own_objective_value,
1235 bool filter_objective_cost,
bool can_use_lp)
1236 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
1237 routing_model_(routing_model),
1238 dimension_(dimension),
1240 slacks_(dimension.slacks()),
1241 evaluators_(routing_model.vehicles(), nullptr),
1242 vehicle_span_upper_bounds_(dimension.vehicle_span_upper_bounds()),
1243 has_vehicle_span_upper_bounds_(false),
1244 total_current_cumul_cost_value_(0),
1245 synchronized_objective_value_(0),
1246 accepted_objective_value_(0),
1247 current_cumul_cost_values_(),
1248 cumul_cost_delta_(0),
1249 delta_path_cumul_cost_values_(routing_model.vehicles(),
kint64min),
1250 global_span_cost_coefficient_(dimension.global_span_cost_coefficient()),
1251 vehicle_span_cost_coefficients_(
1252 dimension.vehicle_span_cost_coefficients()),
1253 has_nonzero_vehicle_span_cost_coefficients_(false),
1254 vehicle_capacities_(dimension.vehicle_capacities()),
1255 delta_max_end_cumul_(0),
1256 delta_nodes_with_precedences_and_changed_cumul_(routing_model.Size()),
1257 name_(dimension.
name()),
1258 optimizer_(routing_model.GetMutableLocalCumulOptimizer(dimension)),
1259 mp_optimizer_(routing_model.GetMutableLocalCumulMPOptimizer(dimension)),
1260 filter_objective_cost_(filter_objective_cost),
1261 can_use_lp_(can_use_lp),
1262 propagate_own_objective_value_(propagate_own_objective_value),
1263 lns_detected_(false) {
1264 for (
const int64 upper_bound : vehicle_span_upper_bounds_) {
1266 has_vehicle_span_upper_bounds_ =
true;
1272 has_nonzero_vehicle_span_cost_coefficients_ =
true;
1276 cumul_soft_bounds_.resize(
cumuls_.size());
1277 cumul_soft_lower_bounds_.resize(
cumuls_.size());
1278 cumul_piecewise_linear_costs_.resize(
cumuls_.size());
1279 bool has_cumul_soft_bounds =
false;
1280 bool has_cumul_soft_lower_bounds =
false;
1281 bool has_cumul_piecewise_linear_costs =
false;
1282 bool has_cumul_hard_bounds =
false;
1283 for (
const IntVar*
const slack : slacks_) {
1284 if (slack->Min() > 0) {
1285 has_cumul_hard_bounds =
true;
1289 for (
int i = 0; i <
cumuls_.size(); ++i) {
1290 if (dimension.HasCumulVarSoftUpperBound(i)) {
1291 has_cumul_soft_bounds =
true;
1292 cumul_soft_bounds_[i].bound = dimension.GetCumulVarSoftUpperBound(i);
1293 cumul_soft_bounds_[i].coefficient =
1294 dimension.GetCumulVarSoftUpperBoundCoefficient(i);
1296 if (dimension.HasCumulVarSoftLowerBound(i)) {
1297 has_cumul_soft_lower_bounds =
true;
1298 cumul_soft_lower_bounds_[i].bound =
1299 dimension.GetCumulVarSoftLowerBound(i);
1300 cumul_soft_lower_bounds_[i].coefficient =
1301 dimension.GetCumulVarSoftLowerBoundCoefficient(i);
1303 if (dimension.HasCumulVarPiecewiseLinearCost(i)) {
1304 has_cumul_piecewise_linear_costs =
true;
1305 cumul_piecewise_linear_costs_[i] =
1306 dimension.GetCumulVarPiecewiseLinearCost(i);
1308 IntVar*
const cumul_var =
cumuls_[i];
1309 if (cumul_var->Min() > 0 || cumul_var->Max() <
kint64max) {
1310 has_cumul_hard_bounds =
true;
1313 if (!has_cumul_soft_bounds) {
1314 cumul_soft_bounds_.clear();
1316 if (!has_cumul_soft_lower_bounds) {
1317 cumul_soft_lower_bounds_.clear();
1319 if (!has_cumul_piecewise_linear_costs) {
1320 cumul_piecewise_linear_costs_.clear();
1322 if (!has_cumul_hard_bounds) {
1327 vehicle_span_cost_coefficients_.assign(routing_model.vehicles(), 0);
1328 has_nonzero_vehicle_span_cost_coefficients_ =
false;
1330 start_to_vehicle_.resize(Size(), -1);
1331 for (
int i = 0; i < routing_model.vehicles(); ++i) {
1332 start_to_vehicle_[routing_model.Start(i)] = i;
1333 evaluators_[i] = &dimension.transit_evaluator(i);
1336 const std::vector<RoutingDimension::NodePrecedence>& node_precedences =
1337 dimension.GetNodePrecedences();
1338 if (!node_precedences.empty()) {
1339 current_min_max_node_cumuls_.resize(
cumuls_.size(), {-1, -1});
1340 node_index_to_precedences_.resize(
cumuls_.size());
1341 for (
const auto& node_precedence : node_precedences) {
1342 node_index_to_precedences_[node_precedence.first_node].push_back(
1344 node_index_to_precedences_[node_precedence.second_node].push_back(
1351 if (can_use_lp_ && optimizer_ ==
nullptr) {
1352 DCHECK(mp_optimizer_ ==
nullptr);
1353 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
1354 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1357 if (optimizer_ ==
nullptr) {
1361 internal_optimizer_ = absl::make_unique<LocalDimensionCumulOptimizer>(
1362 &dimension,
parameters.continuous_scheduling_solver());
1363 optimizer_ = internal_optimizer_.get();
1365 if (FilterBreakCost(vehicle) || FilterDimensionForbiddenIntervals()) {
1366 internal_mp_optimizer_ =
1367 absl::make_unique<LocalDimensionCumulOptimizer>(
1368 &dimension,
parameters.mixed_integer_scheduling_solver());
1369 mp_optimizer_ = internal_mp_optimizer_.get();
1377 if (node < cumul_soft_bounds_.size()) {
1378 const int64 bound = cumul_soft_bounds_[node].bound;
1387 int64 PathCumulFilter::GetCumulPiecewiseLinearCost(
int64 node,
1389 if (node < cumul_piecewise_linear_costs_.size()) {
1390 const PiecewiseLinearFunction*
cost = cumul_piecewise_linear_costs_[node];
1391 if (
cost !=
nullptr) {
1398 int64 PathCumulFilter::GetCumulSoftLowerBoundCost(
int64 node,
1400 if (node < cumul_soft_lower_bounds_.size()) {
1401 const int64 bound = cumul_soft_lower_bounds_[node].bound;
1410 int64 PathCumulFilter::GetPathCumulSoftLowerBoundCost(
1411 const PathTransits& path_transits,
int path)
const {
1412 int64 node = path_transits.Node(path, path_transits.PathSize(path) - 1);
1414 int64 current_cumul_cost_value = GetCumulSoftLowerBoundCost(node, cumul);
1415 for (
int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
1416 node = path_transits.Node(path, i);
1417 cumul =
CapSub(cumul, path_transits.Transit(path, i));
1419 current_cumul_cost_value =
CapAdd(current_cumul_cost_value,
1420 GetCumulSoftLowerBoundCost(node, cumul));
1422 return current_cumul_cost_value;
1425 void PathCumulFilter::OnBeforeSynchronizePaths() {
1426 total_current_cumul_cost_value_ = 0;
1427 cumul_cost_delta_ = 0;
1428 current_cumul_cost_values_.clear();
1429 if (NumPaths() > 0 &&
1430 (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1431 FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1432 FilterPrecedences() || FilterSoftSpanCost() ||
1433 FilterSoftSpanQuadraticCost())) {
1434 InitializeSupportedPathCumul(¤t_min_start_,
kint64max);
1435 InitializeSupportedPathCumul(¤t_max_end_,
kint64min);
1436 current_path_transits_.Clear();
1437 current_path_transits_.AddPaths(NumPaths());
1439 for (
int r = 0; r < NumPaths(); ++r) {
1440 int64 node = Start(r);
1441 const int vehicle = start_to_vehicle_[Start(r)];
1444 int number_of_route_arcs = 0;
1445 while (node < Size()) {
1446 ++number_of_route_arcs;
1449 current_path_transits_.ReserveTransits(r, number_of_route_arcs);
1453 std::vector<int64> min_path_cumuls;
1454 min_path_cumuls.reserve(number_of_route_arcs + 1);
1455 min_path_cumuls.push_back(cumul);
1457 int64 current_cumul_cost_value = GetCumulSoftCost(node, cumul);
1458 current_cumul_cost_value =
CapAdd(
1459 current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1461 int64 total_transit = 0;
1462 while (node < Size()) {
1464 const int64 transit = (*evaluators_[vehicle])(node,
next);
1465 total_transit =
CapAdd(total_transit, transit);
1466 const int64 transit_slack =
CapAdd(transit, slacks_[node]->Min());
1467 current_path_transits_.PushTransit(r, node,
next, transit_slack);
1468 cumul =
CapAdd(cumul, transit_slack);
1470 dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
next, cumul);
1472 min_path_cumuls.push_back(cumul);
1474 current_cumul_cost_value =
1475 CapAdd(current_cumul_cost_value, GetCumulSoftCost(node, cumul));
1476 current_cumul_cost_value =
CapAdd(
1477 current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1479 if (FilterPrecedences()) {
1480 StoreMinMaxCumulOfNodesOnPath(r, min_path_cumuls,
1483 if (number_of_route_arcs == 1 &&
1484 !routing_model_.AreEmptyRouteCostsConsideredForVehicle(vehicle)) {
1487 current_cumul_cost_values_[Start(r)] = 0;
1488 current_path_transits_.ClearPath(r);
1491 if (FilterSlackCost() || FilterSoftSpanCost() ||
1492 FilterSoftSpanQuadraticCost()) {
1493 const int64 start = ComputePathMaxStartFromEndCumul(
1494 current_path_transits_, r, Start(r), cumul);
1495 const int64 span_lower_bound =
CapSub(cumul, start);
1496 if (FilterSlackCost()) {
1497 current_cumul_cost_value =
1498 CapAdd(current_cumul_cost_value,
1499 CapProd(vehicle_span_cost_coefficients_[vehicle],
1500 CapSub(span_lower_bound, total_transit)));
1502 if (FilterSoftSpanCost()) {
1503 const SimpleBoundCosts::BoundCost bound_cost =
1504 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1505 if (bound_cost.bound < span_lower_bound) {
1506 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1507 current_cumul_cost_value =
CapAdd(
1508 current_cumul_cost_value,
CapProd(bound_cost.cost, violation));
1511 if (FilterSoftSpanQuadraticCost()) {
1512 const SimpleBoundCosts::BoundCost bound_cost =
1513 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1514 if (bound_cost.bound < span_lower_bound) {
1515 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1516 current_cumul_cost_value =
1517 CapAdd(current_cumul_cost_value,
1522 if (FilterCumulSoftLowerBounds()) {
1523 current_cumul_cost_value =
1524 CapAdd(current_cumul_cost_value,
1525 GetPathCumulSoftLowerBoundCost(current_path_transits_, r));
1527 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1530 int64 lp_cumul_cost_value = 0;
1531 LocalDimensionCumulOptimizer*
const optimizer =
1532 FilterBreakCost(vehicle) ? mp_optimizer_ : optimizer_;
1533 DCHECK(optimizer !=
nullptr);
1535 optimizer->ComputeRouteCumulCostWithoutFixedTransits(
1536 vehicle, [
this](
int64 node) {
return Value(node); },
1537 &lp_cumul_cost_value);
1540 lp_cumul_cost_value = 0;
1542 case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
1543 DCHECK(mp_optimizer_ !=
nullptr);
1544 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1545 vehicle, [
this](
int64 node) { return Value(node); },
1546 &lp_cumul_cost_value) ==
1548 lp_cumul_cost_value = 0;
1554 current_cumul_cost_value =
1555 std::max(current_cumul_cost_value, lp_cumul_cost_value);
1557 current_cumul_cost_values_[Start(r)] = current_cumul_cost_value;
1558 current_max_end_.path_values[r] = cumul;
1559 if (current_max_end_.cumul_value < cumul) {
1560 current_max_end_.cumul_value = cumul;
1561 current_max_end_.cumul_value_support = r;
1563 total_current_cumul_cost_value_ =
1564 CapAdd(total_current_cumul_cost_value_, current_cumul_cost_value);
1566 if (FilterPrecedences()) {
1568 for (
int64 node : GetNewSynchronizedUnperformedNodes()) {
1569 current_min_max_node_cumuls_[node] = {-1, -1};
1574 for (
int r = 0; r < NumPaths(); ++r) {
1575 const int64 start = ComputePathMaxStartFromEndCumul(
1576 current_path_transits_, r, Start(r), current_max_end_.cumul_value);
1577 current_min_start_.path_values[r] = start;
1578 if (current_min_start_.cumul_value > start) {
1579 current_min_start_.cumul_value = start;
1580 current_min_start_.cumul_value_support = r;
1586 lns_detected_ =
false;
1588 DCHECK_GE(current_max_end_.cumul_value, current_min_start_.cumul_value);
1589 synchronized_objective_value_ =
1590 CapAdd(total_current_cumul_cost_value_,
1591 CapProd(global_span_cost_coefficient_,
1592 CapSub(current_max_end_.cumul_value,
1593 current_min_start_.cumul_value)));
1596 bool PathCumulFilter::AcceptPath(
int64 path_start,
int64 chain_start,
1598 int64 node = path_start;
1600 int64 cumul_cost_delta = 0;
1601 int64 total_transit = 0;
1602 const int path = delta_path_transits_.AddPaths(1);
1603 const int vehicle = start_to_vehicle_[path_start];
1605 const bool filter_vehicle_costs =
1606 !routing_model_.IsEnd(GetNext(node)) ||
1607 routing_model_.AreEmptyRouteCostsConsideredForVehicle(vehicle);
1608 if (filter_vehicle_costs) {
1609 cumul_cost_delta =
CapAdd(GetCumulSoftCost(node, cumul),
1610 GetCumulPiecewiseLinearCost(node, cumul));
1613 int number_of_route_arcs = 0;
1614 while (node < Size()) {
1620 lns_detected_ =
true;
1623 ++number_of_route_arcs;
1626 delta_path_transits_.ReserveTransits(path, number_of_route_arcs);
1627 std::vector<int64> min_path_cumuls;
1628 min_path_cumuls.reserve(number_of_route_arcs + 1);
1629 min_path_cumuls.push_back(cumul);
1634 while (node < Size()) {
1636 const int64 transit = (*evaluators_[vehicle])(node,
next);
1637 total_transit =
CapAdd(total_transit, transit);
1638 const int64 transit_slack =
CapAdd(transit, slacks_[node]->Min());
1639 delta_path_transits_.PushTransit(path, node,
next, transit_slack);
1640 cumul =
CapAdd(cumul, transit_slack);
1641 cumul = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
next, cumul);
1646 min_path_cumuls.push_back(cumul);
1648 if (filter_vehicle_costs) {
1650 CapAdd(cumul_cost_delta, GetCumulSoftCost(node, cumul));
1652 CapAdd(cumul_cost_delta, GetCumulPiecewiseLinearCost(node, cumul));
1655 const int64 min_end = cumul;
1657 if (!PickupToDeliveryLimitsRespected(delta_path_transits_, path,
1661 if (FilterSlackCost() || FilterBreakCost(vehicle) ||
1662 FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1664 if (vehicle_span_upper_bounds_[vehicle] <
kint64max) {
1665 const int64 span_max = vehicle_span_upper_bounds_[vehicle];
1666 slack_max =
std::min(slack_max,
CapSub(span_max, total_transit));
1668 const int64 max_start_from_min_end = ComputePathMaxStartFromEndCumul(
1669 delta_path_transits_, path, path_start, min_end);
1670 const int64 span_lb =
CapSub(min_end, max_start_from_min_end);
1671 int64 min_total_slack =
CapSub(span_lb, total_transit);
1672 if (min_total_slack > slack_max)
return false;
1674 if (dimension_.HasBreakConstraints()) {
1675 for (
const auto [limit, min_break_duration] :
1676 dimension_.GetBreakDistanceDurationOfVehicle(vehicle)) {
1681 if (limit == 0 || total_transit == 0)
continue;
1682 const int num_breaks_lb = (total_transit - 1) / limit;
1683 const int64 slack_lb =
CapProd(num_breaks_lb, min_break_duration);
1684 if (slack_lb > slack_max)
return false;
1685 min_total_slack =
std::max(min_total_slack, slack_lb);
1691 int64 min_total_break = 0;
1692 int64 max_path_end =
cumuls_[routing_model_.End(vehicle)]->Max();
1693 const int64 max_start = ComputePathMaxStartFromEndCumul(
1694 delta_path_transits_, path, path_start, max_path_end);
1695 for (
const IntervalVar* br :
1696 dimension_.GetBreakIntervalsOfVehicle(vehicle)) {
1697 if (!br->MustBePerformed())
continue;
1698 if (max_start < br->EndMin() && br->StartMax() < min_end) {
1699 min_total_break =
CapAdd(min_total_break, br->DurationMin());
1702 if (min_total_break > slack_max)
return false;
1703 min_total_slack =
std::max(min_total_slack, min_total_break);
1705 if (filter_vehicle_costs) {
1706 cumul_cost_delta =
CapAdd(
1708 CapProd(vehicle_span_cost_coefficients_[vehicle], min_total_slack));
1709 const int64 span_lower_bound =
CapAdd(total_transit, min_total_slack);
1710 if (FilterSoftSpanCost()) {
1711 const SimpleBoundCosts::BoundCost bound_cost =
1712 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1713 if (bound_cost.bound < span_lower_bound) {
1714 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1716 CapAdd(cumul_cost_delta,
CapProd(bound_cost.cost, violation));
1719 if (FilterSoftSpanQuadraticCost()) {
1720 const SimpleBoundCosts::BoundCost bound_cost =
1721 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1722 if (bound_cost.bound < span_lower_bound) {
1723 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1730 if (
CapAdd(total_transit, min_total_slack) >
1731 vehicle_span_upper_bounds_[vehicle]) {
1735 if (FilterCumulSoftLowerBounds() && filter_vehicle_costs) {
1738 GetPathCumulSoftLowerBoundCost(delta_path_transits_, path));
1740 if (FilterPrecedences()) {
1741 StoreMinMaxCumulOfNodesOnPath(path, min_path_cumuls,
true);
1743 if (!filter_vehicle_costs) {
1746 cumul_cost_delta = 0;
1747 delta_path_transits_.ClearPath(path);
1749 if (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1750 FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1751 FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1752 delta_paths_.insert(GetPath(path_start));
1753 delta_path_cumul_cost_values_[vehicle] = cumul_cost_delta;
1755 CapSub(cumul_cost_delta, current_cumul_cost_values_[path_start]);
1756 if (filter_vehicle_costs) {
1757 delta_max_end_cumul_ =
std::max(delta_max_end_cumul_, min_end);
1760 cumul_cost_delta_ =
CapAdd(cumul_cost_delta_, cumul_cost_delta);
1764 bool PathCumulFilter::FinalizeAcceptPath(
const Assignment*
delta,
1765 int64 objective_min,
1766 int64 objective_max) {
1767 if ((!FilterSpanCost() && !FilterCumulSoftBounds() && !FilterSlackCost() &&
1768 !FilterCumulSoftLowerBounds() && !FilterCumulPiecewiseLinearCosts() &&
1769 !FilterPrecedences() && !FilterSoftSpanCost() &&
1770 !FilterSoftSpanQuadraticCost()) ||
1774 if (FilterPrecedences()) {
1775 for (
int64 node : delta_nodes_with_precedences_and_changed_cumul_
1776 .PositionsSetAtLeastOnce()) {
1777 const std::pair<int64, int64> node_min_max_cumul_in_delta =
1782 DCHECK(node_min_max_cumul_in_delta.first >= 0 &&
1783 node_min_max_cumul_in_delta.second >= 0);
1784 for (
const RoutingDimension::NodePrecedence& precedence :
1785 node_index_to_precedences_[node]) {
1786 const bool node_is_first = (precedence.first_node == node);
1787 const int64 other_node =
1788 node_is_first ? precedence.second_node : precedence.first_node;
1790 GetNext(other_node) == other_node) {
1797 const std::pair<int64, int64>& other_min_max_cumul_in_delta =
1800 current_min_max_node_cumuls_[other_node]);
1802 const int64 first_min_cumul = node_is_first
1803 ? node_min_max_cumul_in_delta.first
1804 : other_min_max_cumul_in_delta.first;
1805 const int64 second_max_cumul = node_is_first
1806 ? other_min_max_cumul_in_delta.second
1807 : node_min_max_cumul_in_delta.second;
1809 if (second_max_cumul < first_min_cumul + precedence.offset) {
1815 int64 new_max_end = delta_max_end_cumul_;
1817 if (FilterSpanCost()) {
1818 if (new_max_end < current_max_end_.cumul_value) {
1823 current_max_end_.cumul_value_support)) {
1824 new_max_end = current_max_end_.cumul_value;
1826 for (
int i = 0; i < current_max_end_.path_values.size(); ++i) {
1827 if (current_max_end_.path_values[i] > new_max_end &&
1829 new_max_end = current_max_end_.path_values[i];
1837 for (
int r = 0; r < delta_path_transits_.NumPaths(); ++r) {
1839 std::min(ComputePathMaxStartFromEndCumul(delta_path_transits_, r,
1840 Start(r), new_max_end),
1843 if (new_max_end != current_max_end_.cumul_value) {
1844 for (
int r = 0; r < NumPaths(); ++r) {
1848 new_min_start =
std::min(new_min_start, ComputePathMaxStartFromEndCumul(
1849 current_path_transits_, r,
1850 Start(r), new_max_end));
1852 }
else if (new_min_start > current_min_start_.cumul_value) {
1857 current_min_start_.cumul_value_support)) {
1858 new_min_start = current_min_start_.cumul_value;
1860 for (
int i = 0; i < current_min_start_.path_values.size(); ++i) {
1861 if (current_min_start_.path_values[i] < new_min_start &&
1863 new_min_start = current_min_start_.path_values[i];
1871 accepted_objective_value_ =
1872 CapAdd(cumul_cost_delta_,
CapProd(global_span_cost_coefficient_,
1873 CapSub(new_max_end, new_min_start)));
1875 if (can_use_lp_ && optimizer_ !=
nullptr &&
1876 accepted_objective_value_ <= objective_max) {
1877 const size_t num_touched_paths = GetTouchedPathStarts().size();
1878 std::vector<int64> path_delta_cost_values(num_touched_paths, 0);
1879 std::vector<bool> requires_mp(num_touched_paths,
false);
1880 for (
int i = 0; i < num_touched_paths; ++i) {
1881 const int64 start = GetTouchedPathStarts()[i];
1882 const int vehicle = start_to_vehicle_[start];
1883 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1886 int64 path_delta_cost_with_lp = 0;
1888 optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1889 vehicle, [
this](
int64 node) {
return GetNext(node); },
1890 &path_delta_cost_with_lp);
1896 path_delta_cost_with_lp, delta_path_cumul_cost_values_[vehicle]);
1897 if (path_cost_diff_with_lp > 0) {
1898 path_delta_cost_values[i] = path_delta_cost_with_lp;
1899 accepted_objective_value_ =
1900 CapAdd(accepted_objective_value_, path_cost_diff_with_lp);
1901 if (accepted_objective_value_ > objective_max) {
1905 path_delta_cost_values[i] = delta_path_cumul_cost_values_[vehicle];
1907 if (mp_optimizer_ !=
nullptr) {
1909 FilterBreakCost(vehicle) ||
1910 (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY);
1913 if (mp_optimizer_ ==
nullptr) {
1914 return accepted_objective_value_ <= objective_max;
1916 for (
int i = 0; i < num_touched_paths; ++i) {
1917 if (!requires_mp[i]) {
1920 const int64 start = GetTouchedPathStarts()[i];
1921 const int vehicle = start_to_vehicle_[start];
1922 int64 path_delta_cost_with_mp = 0;
1923 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1924 vehicle, [
this](
int64 node) { return GetNext(node); },
1925 &path_delta_cost_with_mp) ==
1930 const int64 path_cost_diff_with_mp =
1931 CapSub(path_delta_cost_with_mp, path_delta_cost_values[i]);
1932 if (path_cost_diff_with_mp > 0) {
1933 accepted_objective_value_ =
1934 CapAdd(accepted_objective_value_, path_cost_diff_with_mp);
1935 if (accepted_objective_value_ > objective_max) {
1942 return accepted_objective_value_ <= objective_max;
1945 void PathCumulFilter::InitializeSupportedPathCumul(
1946 SupportedPathCumul* supported_cumul,
int64 default_value) {
1947 supported_cumul->cumul_value = default_value;
1948 supported_cumul->cumul_value_support = -1;
1949 supported_cumul->path_values.resize(NumPaths(), default_value);
1952 bool PathCumulFilter::PickupToDeliveryLimitsRespected(
1953 const PathTransits& path_transits,
int path,
1954 const std::vector<int64>& min_path_cumuls)
const {
1955 if (!dimension_.HasPickupToDeliveryLimits()) {
1958 const int num_pairs = routing_model_.GetPickupAndDeliveryPairs().size();
1960 std::vector<std::pair<int, int64>> visited_delivery_and_min_cumul_per_pair(
1961 num_pairs, {-1, -1});
1963 const int path_size = path_transits.PathSize(path);
1964 CHECK_EQ(min_path_cumuls.size(), path_size);
1966 int64 max_cumul = min_path_cumuls.back();
1967 for (
int i = path_transits.PathSize(path) - 2; i >= 0; i--) {
1968 const int node_index = path_transits.Node(path, i);
1969 max_cumul =
CapSub(max_cumul, path_transits.Transit(path, i));
1972 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1973 routing_model_.GetPickupIndexPairs(node_index);
1974 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1975 routing_model_.GetDeliveryIndexPairs(node_index);
1976 if (!pickup_index_pairs.empty()) {
1980 DCHECK(delivery_index_pairs.empty());
1981 DCHECK_EQ(pickup_index_pairs.size(), 1);
1982 const int pair_index = pickup_index_pairs[0].first;
1984 const int delivery_index =
1985 visited_delivery_and_min_cumul_per_pair[pair_index].first;
1986 if (delivery_index < 0) {
1990 const int64 cumul_diff_limit = dimension_.GetPickupToDeliveryLimitForPair(
1991 pair_index, pickup_index_pairs[0].second, delivery_index);
1992 if (
CapSub(visited_delivery_and_min_cumul_per_pair[pair_index].second,
1993 max_cumul) > cumul_diff_limit) {
1997 if (!delivery_index_pairs.empty()) {
2000 DCHECK(pickup_index_pairs.empty());
2001 DCHECK_EQ(delivery_index_pairs.size(), 1);
2002 const int pair_index = delivery_index_pairs[0].first;
2003 std::pair<int, int64>& delivery_index_and_cumul =
2004 visited_delivery_and_min_cumul_per_pair[pair_index];
2005 int& delivery_index = delivery_index_and_cumul.first;
2007 delivery_index = delivery_index_pairs[0].second;
2008 delivery_index_and_cumul.second = min_path_cumuls[i];
2014 void PathCumulFilter::StoreMinMaxCumulOfNodesOnPath(
2015 int path,
const std::vector<int64>& min_path_cumuls,
bool is_delta) {
2016 const PathTransits& path_transits =
2017 is_delta ? delta_path_transits_ : current_path_transits_;
2019 const int path_size = path_transits.PathSize(path);
2020 DCHECK_EQ(min_path_cumuls.size(), path_size);
2022 int64 max_cumul =
cumuls_[path_transits.Node(path, path_size - 1)]->Max();
2023 for (
int i = path_size - 1; i >= 0; i--) {
2024 const int node_index = path_transits.Node(path, i);
2026 if (i < path_size - 1) {
2027 max_cumul =
CapSub(max_cumul, path_transits.Transit(path, i));
2031 if (is_delta && node_index_to_precedences_[node_index].empty()) {
2036 std::pair<int64, int64>& min_max_cumuls =
2037 is_delta ? node_with_precedence_to_delta_min_max_cumuls_[node_index]
2038 : current_min_max_node_cumuls_[node_index];
2039 min_max_cumuls.first = min_path_cumuls[i];
2040 min_max_cumuls.second = max_cumul;
2042 if (is_delta && !routing_model_.IsEnd(node_index) &&
2043 (min_max_cumuls.first !=
2044 current_min_max_node_cumuls_[node_index].first ||
2045 max_cumul != current_min_max_node_cumuls_[node_index].second)) {
2046 delta_nodes_with_precedences_and_changed_cumul_.
Set(node_index);
2051 int64 PathCumulFilter::ComputePathMaxStartFromEndCumul(
2052 const PathTransits& path_transits,
int path,
int64 path_start,
2053 int64 min_end_cumul)
const {
2054 int64 cumul_from_min_end = min_end_cumul;
2055 int64 cumul_from_max_end =
2056 cumuls_[routing_model_.End(start_to_vehicle_[path_start])]->Max();
2057 for (
int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
2058 const int64 transit = path_transits.Transit(path, i);
2059 const int64 node = path_transits.Node(path, i);
2060 cumul_from_min_end =
2062 cumul_from_max_end = dimension_.GetLastPossibleLessOrEqualValueForNode(
2063 node,
CapSub(cumul_from_max_end, transit));
2065 return std::min(cumul_from_min_end, cumul_from_max_end);
2073 bool propagate_own_objective_value,
bool filter_objective_cost,
2076 return model.solver()->RevAlloc(
new PathCumulFilter(
2078 filter_objective_cost, can_use_lp));
2083 bool DimensionHasCumulCost(
const RoutingDimension& dimension) {
2084 if (dimension.global_span_cost_coefficient() != 0)
return true;
2085 if (dimension.HasSoftSpanUpperBounds())
return true;
2086 if (dimension.HasQuadraticCostSoftSpanUpperBounds())
return true;
2087 for (
const int64 coefficient : dimension.vehicle_span_cost_coefficients()) {
2090 for (
int i = 0; i < dimension.cumuls().size(); ++i) {
2091 if (dimension.HasCumulVarSoftUpperBound(i))
return true;
2092 if (dimension.HasCumulVarSoftLowerBound(i))
return true;
2093 if (dimension.HasCumulVarPiecewiseLinearCost(i))
return true;
2098 bool DimensionHasCumulConstraint(
const RoutingDimension& dimension) {
2099 if (dimension.HasBreakConstraints())
return true;
2100 if (dimension.HasPickupToDeliveryLimits())
return true;
2101 if (!dimension.GetNodePrecedences().empty())
return true;
2102 for (
const int64 upper_bound : dimension.vehicle_span_upper_bounds()) {
2103 if (upper_bound !=
kint64max)
return true;
2105 for (
const IntVar*
const slack : dimension.slacks()) {
2106 if (slack->Min() > 0)
return true;
2108 const std::vector<IntVar*>& cumuls = dimension.cumuls();
2109 for (
int i = 0; i < cumuls.size(); ++i) {
2110 IntVar*
const cumul_var = cumuls[i];
2111 if (cumul_var->Min() > 0 && cumul_var->Max() <
kint64max &&
2112 !dimension.model()->IsEnd(i)) {
2115 if (dimension.forbidden_intervals()[i].NumIntervals() > 0)
return true;
2124 const std::vector<RoutingDimension*>& dimensions,
2125 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2129 if (dimension->GetUnaryTransitEvaluator(0) ==
nullptr)
continue;
2131 using Intervals = std::vector<UnaryDimensionChecker::Interval>;
2133 const int num_vehicles = dimension->model()->vehicles();
2134 Intervals path_capacity(num_vehicles);
2135 std::vector<int> path_class(num_vehicles);
2136 for (
int v = 0; v < num_vehicles; ++v) {
2137 const auto& vehicle_capacities = dimension->vehicle_capacities();
2138 path_capacity[v] = {0, vehicle_capacities[v]};
2139 path_class[v] = dimension->vehicle_to_class(v);
2146 const int num_vehicle_classes =
2147 1 + *std::max_element(path_class.begin(), path_class.end());
2148 std::vector<Intervals> demands(num_vehicle_classes);
2149 const int num_cumuls = dimension->cumuls().size();
2150 const int num_slacks = dimension->slacks().size();
2151 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
2152 const int vehicle_class = path_class[vehicle];
2153 if (!demands[vehicle_class].empty())
continue;
2154 const auto& evaluator = dimension->GetUnaryTransitEvaluator(vehicle);
2155 Intervals class_demands(num_cumuls);
2156 for (
int node = 0; node < num_cumuls; ++node) {
2157 if (node < num_slacks) {
2158 const int64 demand_min = evaluator(node);
2159 const int64 slack_max = dimension->SlackVar(node)->Max();
2160 class_demands[node] = {demand_min,
CapAdd(demand_min, slack_max)};
2162 class_demands[node] = {0, 0};
2165 demands[vehicle_class] = std::move(class_demands);
2168 Intervals node_capacity(num_cumuls);
2169 for (
int node = 0; node < num_cumuls; ++node) {
2170 const IntVar* cumul = dimension->CumulVar(node);
2171 node_capacity[node] = {cumul->
Min(), cumul->
Max()};
2174 auto checker = absl::make_unique<UnaryDimensionChecker>(
2175 path_state, std::move(path_capacity), std::move(path_class),
2176 std::move(demands), std::move(node_capacity));
2177 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
2179 dimension->model()->solver(), std::move(checker));
2180 filters->push_back({filter, kAccept});
2185 const std::vector<RoutingDimension*>& dimensions,
2186 const RoutingSearchParameters&
parameters,
bool filter_objective_cost,
2187 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2188 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
2196 const int num_dimensions = dimensions.size();
2198 std::vector<bool> use_path_cumul_filter(num_dimensions);
2199 std::vector<bool> use_cumul_bounds_propagator_filter(num_dimensions);
2200 std::vector<bool> use_global_lp_filter(num_dimensions);
2201 std::vector<int> filtering_difficulty(num_dimensions);
2202 for (
int d = 0; d < num_dimensions; d++) {
2204 const bool has_cumul_cost = DimensionHasCumulCost(dimension);
2205 use_path_cumul_filter[d] =
2206 has_cumul_cost || DimensionHasCumulConstraint(dimension);
2208 const bool can_use_cumul_bounds_propagator_filter =
2210 (!filter_objective_cost || !has_cumul_cost);
2212 use_global_lp_filter[d] =
2213 (has_precedences && !can_use_cumul_bounds_propagator_filter) ||
2216 use_cumul_bounds_propagator_filter[d] =
2217 has_precedences && !use_global_lp_filter[d];
2219 filtering_difficulty[d] = 4 * use_global_lp_filter[d] +
2220 2 * use_cumul_bounds_propagator_filter[d] +
2221 use_path_cumul_filter[d];
2224 std::vector<int> sorted_dimension_indices(num_dimensions);
2225 std::iota(sorted_dimension_indices.begin(), sorted_dimension_indices.end(),
2227 std::sort(sorted_dimension_indices.begin(), sorted_dimension_indices.end(),
2228 [&filtering_difficulty](
int d1,
int d2) {
2229 return filtering_difficulty[d1] < filtering_difficulty[d2];
2232 for (
const int d : sorted_dimension_indices) {
2239 const bool use_global_lp = use_global_lp_filter[d];
2240 if (use_path_cumul_filter[d]) {
2243 filter_objective_cost),
2247 {
model.solver()->RevAlloc(
new ChainCumulFilter(
model, dimension)),
2251 if (use_global_lp) {
2252 DCHECK(
model.GetMutableGlobalCumulOptimizer(dimension) !=
nullptr);
2254 model.GetMutableGlobalCumulOptimizer(dimension),
2255 filter_objective_cost),
2257 }
else if (use_cumul_bounds_propagator_filter[d]) {
2266 class PickupDeliveryFilter :
public BasePathFilter {
2268 PickupDeliveryFilter(
const std::vector<IntVar*>& nexts,
int next_domain_size,
2269 const RoutingModel::IndexPairs& pairs,
2270 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2272 ~PickupDeliveryFilter()
override {}
2273 bool AcceptPath(
int64 path_start,
int64 chain_start,
2274 int64 chain_end)
override;
2275 std::string DebugString()
const override {
return "PickupDeliveryFilter"; }
2278 bool AcceptPathDefault(
int64 path_start);
2279 template <
bool lifo>
2280 bool AcceptPathOrdered(
int64 path_start);
2282 std::vector<int> pair_firsts_;
2283 std::vector<int> pair_seconds_;
2284 const RoutingModel::IndexPairs pairs_;
2285 SparseBitset<> visited_;
2286 std::deque<int> visited_deque_;
2287 const std::vector<RoutingModel::PickupAndDeliveryPolicy> vehicle_policies_;
2290 PickupDeliveryFilter::PickupDeliveryFilter(
2291 const std::vector<IntVar*>& nexts,
int next_domain_size,
2292 const RoutingModel::IndexPairs& pairs,
2293 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
2294 : BasePathFilter(nexts, next_domain_size),
2299 vehicle_policies_(vehicle_policies) {
2300 for (
int i = 0; i < pairs.size(); ++i) {
2301 const auto& index_pair = pairs[i];
2302 for (
int first : index_pair.first) {
2303 pair_firsts_[first] = i;
2305 for (
int second : index_pair.second) {
2306 pair_seconds_[second] = i;
2311 bool PickupDeliveryFilter::AcceptPath(
int64 path_start,
int64 chain_start,
2313 switch (vehicle_policies_[GetPath(path_start)]) {
2314 case RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER:
2315 return AcceptPathDefault(path_start);
2316 case RoutingModel::PICKUP_AND_DELIVERY_LIFO:
2317 return AcceptPathOrdered<true>(path_start);
2318 case RoutingModel::PICKUP_AND_DELIVERY_FIFO:
2319 return AcceptPathOrdered<false>(path_start);
2325 bool PickupDeliveryFilter::AcceptPathDefault(
int64 path_start) {
2326 visited_.ClearAll();
2327 int64 node = path_start;
2328 int64 path_length = 1;
2329 while (node < Size()) {
2331 if (path_length > Size()) {
2338 for (
int second : pairs_[pair_firsts_[node]].second) {
2339 if (visited_[second]) {
2345 bool found_first =
false;
2346 bool some_synced =
false;
2347 for (
int first : pairs_[pair_seconds_[node]].first) {
2348 if (visited_[first]) {
2352 if (IsVarSynced(first)) {
2356 if (!found_first && some_synced) {
2369 for (
const int64 node : visited_.PositionsSetAtLeastOnce()) {
2371 bool found_second =
false;
2372 bool some_synced =
false;
2373 for (
int second : pairs_[pair_firsts_[node]].second) {
2374 if (visited_[second]) {
2375 found_second =
true;
2378 if (IsVarSynced(second)) {
2382 if (!found_second && some_synced) {
2390 template <
bool lifo>
2391 bool PickupDeliveryFilter::AcceptPathOrdered(
int64 path_start) {
2392 visited_deque_.clear();
2393 int64 node = path_start;
2394 int64 path_length = 1;
2395 while (node < Size()) {
2397 if (path_length > Size()) {
2402 visited_deque_.push_back(node);
2404 visited_deque_.push_front(node);
2408 bool found_first =
false;
2409 bool some_synced =
false;
2410 for (
int first : pairs_[pair_seconds_[node]].first) {
2411 if (!visited_deque_.empty() && visited_deque_.back() == first) {
2415 if (IsVarSynced(first)) {
2419 if (!found_first && some_synced) {
2421 }
else if (!visited_deque_.empty()) {
2422 visited_deque_.pop_back();
2433 while (!visited_deque_.empty()) {
2434 for (
int second : pairs_[pair_firsts_[visited_deque_.back()]].second) {
2435 if (IsVarSynced(second)) {
2439 visited_deque_.pop_back();
2448 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2450 return routing_model.
solver()->
RevAlloc(
new PickupDeliveryFilter(
2452 pairs, vehicle_policies));
2458 class VehicleVarFilter :
public BasePathFilter {
2460 explicit VehicleVarFilter(
const RoutingModel& routing_model);
2461 ~VehicleVarFilter()
override {}
2462 bool AcceptPath(
int64 path_start,
int64 chain_start,
2463 int64 chain_end)
override;
2464 std::string DebugString()
const override {
return "VehicleVariableFilter"; }
2467 bool DisableFiltering()
const override;
2468 bool IsVehicleVariableConstrained(
int index)
const;
2470 std::vector<int64> start_to_vehicle_;
2471 std::vector<IntVar*> vehicle_vars_;
2472 const int64 unconstrained_vehicle_var_domain_size_;
2475 VehicleVarFilter::VehicleVarFilter(
const RoutingModel& routing_model)
2476 : BasePathFilter(routing_model.Nexts(),
2477 routing_model.Size() + routing_model.vehicles()),
2478 vehicle_vars_(routing_model.VehicleVars()),
2479 unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) {
2480 start_to_vehicle_.resize(Size(), -1);
2481 for (
int i = 0; i < routing_model.vehicles(); ++i) {
2482 start_to_vehicle_[routing_model.Start(i)] = i;
2486 bool VehicleVarFilter::AcceptPath(
int64 path_start,
int64 chain_start,
2488 const int64 vehicle = start_to_vehicle_[path_start];
2489 int64 node = chain_start;
2490 while (node != chain_end) {
2491 if (!vehicle_vars_[node]->Contains(vehicle)) {
2494 node = GetNext(node);
2496 return vehicle_vars_[node]->Contains(vehicle);
2499 bool VehicleVarFilter::DisableFiltering()
const {
2500 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
2501 if (IsVehicleVariableConstrained(i))
return false;
2506 bool VehicleVarFilter::IsVehicleVariableConstrained(
int index)
const {
2507 const IntVar*
const vehicle_var = vehicle_vars_[
index];
2511 const int adjusted_unconstrained_vehicle_var_domain_size =
2512 vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_
2513 : unconstrained_vehicle_var_domain_size_ + 1;
2514 return vehicle_var->Size() != adjusted_unconstrained_vehicle_var_domain_size;
2521 return routing_model.
solver()->
RevAlloc(
new VehicleVarFilter(routing_model));
2526 class CumulBoundsPropagatorFilter :
public IntVarLocalSearchFilter {
2528 explicit CumulBoundsPropagatorFilter(
const RoutingDimension& dimension);
2529 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2530 int64 objective_min,
int64 objective_max)
override;
2531 std::string DebugString()
const override {
2532 return "CumulBoundsPropagatorFilter(" + propagator_.dimension().name() +
2537 CumulBoundsPropagator propagator_;
2538 const int64 cumul_offset_;
2539 SparseBitset<int64> delta_touched_;
2540 std::vector<int64> delta_nexts_;
2543 CumulBoundsPropagatorFilter::CumulBoundsPropagatorFilter(
2544 const RoutingDimension& dimension)
2545 : IntVarLocalSearchFilter(dimension.
model()->Nexts()),
2546 propagator_(&dimension),
2547 cumul_offset_(dimension.GetGlobalOptimizerOffset()),
2548 delta_touched_(Size()),
2549 delta_nexts_(Size()) {}
2551 bool CumulBoundsPropagatorFilter::Accept(
const Assignment*
delta,
2552 const Assignment* deltadelta,
2553 int64 objective_min,
2554 int64 objective_max) {
2556 for (
const IntVarElement& delta_element :
2557 delta->IntVarContainer().elements()) {
2559 if (FindIndex(delta_element.Var(), &
index)) {
2560 if (!delta_element.Bound()) {
2565 delta_nexts_[
index] = delta_element.Value();
2568 const auto& next_accessor = [
this](
int64 index) {
2572 return propagator_.PropagateCumulBounds(next_accessor, cumul_offset_);
2580 new CumulBoundsPropagatorFilter(dimension));
2585 class LPCumulFilter :
public IntVarLocalSearchFilter {
2587 LPCumulFilter(
const std::vector<IntVar*>& nexts,
2588 GlobalDimensionCumulOptimizer* optimizer,
2589 bool filter_objective_cost);
2590 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2591 int64 objective_min,
int64 objective_max)
override;
2592 int64 GetAcceptedObjectiveValue()
const override;
2593 void OnSynchronize(
const Assignment*
delta)
override;
2594 int64 GetSynchronizedObjectiveValue()
const override;
2595 std::string DebugString()
const override {
2596 return "LPCumulFilter(" + optimizer_.dimension()->name() +
")";
2600 GlobalDimensionCumulOptimizer& optimizer_;
2601 const bool filter_objective_cost_;
2602 int64 synchronized_cost_without_transit_;
2603 int64 delta_cost_without_transit_;
2604 SparseBitset<int64> delta_touched_;
2605 std::vector<int64> delta_nexts_;
2608 LPCumulFilter::LPCumulFilter(
const std::vector<IntVar*>& nexts,
2609 GlobalDimensionCumulOptimizer* optimizer,
2610 bool filter_objective_cost)
2611 : IntVarLocalSearchFilter(nexts),
2612 optimizer_(*optimizer),
2613 filter_objective_cost_(filter_objective_cost),
2614 synchronized_cost_without_transit_(-1),
2615 delta_cost_without_transit_(-1),
2616 delta_touched_(Size()),
2617 delta_nexts_(Size()) {}
2619 bool LPCumulFilter::Accept(
const Assignment*
delta,
2620 const Assignment* deltadelta,
int64 objective_min,
2621 int64 objective_max) {
2623 for (
const IntVarElement& delta_element :
2624 delta->IntVarContainer().elements()) {
2626 if (FindIndex(delta_element.Var(), &
index)) {
2627 if (!delta_element.Bound()) {
2632 delta_nexts_[
index] = delta_element.Value();
2635 const auto& next_accessor = [
this](
int64 index) {
2639 if (!filter_objective_cost_) {
2641 delta_cost_without_transit_ = 0;
2642 return optimizer_.IsFeasible(next_accessor);
2645 if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
2646 next_accessor, &delta_cost_without_transit_)) {
2648 delta_cost_without_transit_ =
kint64max;
2651 return delta_cost_without_transit_ <= objective_max;
2654 int64 LPCumulFilter::GetAcceptedObjectiveValue()
const {
2655 return delta_cost_without_transit_;
2658 void LPCumulFilter::OnSynchronize(
const Assignment*
delta) {
2661 const RoutingModel&
model = *optimizer_.dimension()->model();
2662 if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
2664 return IsVarSynced(index) ? Value(index)
2665 : model.IsStart(index) ? model.End(model.VehicleIndex(index))
2668 &synchronized_cost_without_transit_)) {
2671 synchronized_cost_without_transit_ = 0;
2675 int64 LPCumulFilter::GetSynchronizedObjectiveValue()
const {
2676 return synchronized_cost_without_transit_;
2684 return model.solver()->RevAlloc(
2685 new LPCumulFilter(
model.Nexts(), optimizer, filter_objective_cost));
2692 model_(routing_model),
2693 solver_(routing_model->solver()),
2694 assignment_(solver_->MakeAssignment()),
2695 temp_assignment_(solver_->MakeAssignment()),
2696 restore_(solver_->MakeRestoreAssignment(temp_assignment_)),
2697 limit_(solver_->MakeCustomLimit(
2698 [routing_model]() {
return routing_model->
CheckLimit(); })) {
2699 assignment_->
Add(routing_model->Nexts());
2705 temp_assignment_->
Copy(assignment_);
2706 AddDeltaToAssignment(
delta, temp_assignment_);
2708 return solver_->
Solve(restore_, limit_);
2712 AddDeltaToAssignment(
delta, assignment_);
2715 void CPFeasibilityFilter::AddDeltaToAssignment(
const Assignment*
delta,
2717 if (
delta ==
nullptr) {
2723 const int delta_size = delta_container.Size();
2725 for (
int i = 0; i < delta_size; i++) {
2726 const IntVarElement& delta_element = delta_container.Element(i);
2727 IntVar*
const var = delta_element.Var();
2737 container->MutableElement(
index)->Deactivate();
2740 container->MutableElement(
index)->Activate();
2757 int type, std::function<
bool(
int)> vehicle_is_compatible) {
2758 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
2759 sorted_vehicle_classes_per_type_[type];
2760 auto vehicle_class_it = sorted_classes.begin();
2762 while (vehicle_class_it != sorted_classes.end()) {
2763 const int vehicle_class = vehicle_class_it->vehicle_class;
2764 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
2765 DCHECK(!vehicles.empty());
2767 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
2769 const int vehicle = *vehicle_it;
2770 if (vehicle_is_compatible(vehicle)) {
2771 vehicles.erase(vehicle_it);
2772 if (vehicles.empty()) {
2773 sorted_classes.erase(vehicle_class_it);
2791 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
2792 : heuristic_(std::move(heuristic)) {}
2795 Assignment*
const assignment = heuristic_->BuildSolution();
2796 if (assignment !=
nullptr) {
2797 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
2798 VLOG(2) <<
"Number of rejected decisions: "
2799 << heuristic_->number_of_rejects();
2808 return heuristic_->number_of_decisions();
2812 return heuristic_->number_of_rejects();
2816 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
2817 heuristic_->DebugString(),
")");
2825 Solver* solver,
const std::vector<IntVar*>& vars,
2827 : assignment_(solver->MakeAssignment()),
2830 delta_(solver->MakeAssignment()),
2831 is_in_delta_(
vars_.size(), false),
2832 empty_(solver->MakeAssignment()),
2833 filter_manager_(filter_manager),
2834 number_of_decisions_(0),
2835 number_of_rejects_(0) {
2837 delta_indices_.reserve(vars_.size());
2841 number_of_decisions_ = 0;
2842 number_of_rejects_ = 0;
2863 const std::function<
int64(
int64)>& next_accessor) {
2868 start_chain_ends_.resize(
model()->vehicles());
2869 end_chain_starts_.resize(
model()->vehicles());
2871 for (
int v = 0; v < model_->
vehicles(); v++) {
2873 while (!model_->
IsEnd(node)) {
2883 end_chain_starts_[v] =
model()->
End(v);
2896 ++number_of_decisions_;
2897 const bool accept = FilterAccept();
2900 const int delta_size = delta_container.
Size();
2903 for (
int i = 0; i < delta_size; ++i) {
2912 ++number_of_rejects_;
2915 for (
const int delta_index : delta_indices_) {
2916 is_in_delta_[delta_index] =
false;
2919 delta_indices_.clear();
2927 bool IntVarFilteredHeuristic::FilterAccept() {
2928 if (!filter_manager_)
return true;
2940 bool RoutingFilteredHeuristic::InitializeSolution() {
2950 start_chain_ends_.clear();
2951 start_chain_ends_.resize(
model()->vehicles(), -1);
2952 end_chain_starts_.clear();
2953 end_chain_starts_.resize(
model()->vehicles(), -1);
2956 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2958 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
2964 start_chain_ends_[vehicle] = node;
2967 std::vector<int64> starts(
Size() +
model()->vehicles(), -1);
2968 std::vector<int64> ends(
Size() +
model()->vehicles(), -1);
2971 starts[node] = node;
2974 std::vector<bool> touched(
Size(),
false);
2975 for (
int node = 0; node <
Size(); ++node) {
2977 while (!
model()->
IsEnd(current) && !touched[current]) {
2978 touched[current] =
true;
2979 IntVar*
const next_var =
Var(current);
2980 if (next_var->Bound()) {
2981 current = next_var->Value();
2986 starts[ends[current]] = starts[node];
2987 ends[starts[node]] = ends[current];
2992 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2993 end_chain_starts_[vehicle] = starts[
model()->
End(vehicle)];
2994 int64 node = start_chain_ends_[vehicle];
2995 if (!
model()->IsEnd(node)) {
3000 while (!
model()->IsEnd(node)) {
3018 node, 1, [
this, node](
int alternate) {
3019 if (node != alternate && !
Contains(alternate)) {
3034 std::vector<bool> to_make_unperformed(
Size(),
false);
3035 for (
const auto& [pickups, deliveries] :
3036 model()->GetPickupAndDeliveryPairs()) {
3037 int64 performed_pickup = -1;
3038 for (
int64 pickup : pickups) {
3040 performed_pickup = pickup;
3044 int64 performed_delivery = -1;
3045 for (
int64 delivery : deliveries) {
3047 performed_delivery = delivery;
3051 if ((performed_pickup == -1) != (performed_delivery == -1)) {
3052 if (performed_pickup != -1) {
3053 to_make_unperformed[performed_pickup] =
true;
3055 if (performed_delivery != -1) {
3056 to_make_unperformed[performed_delivery] =
true;
3067 next = next_of_next;
3076 std::function<
int64(
int64)> penalty_evaluator,
3080 penalty_evaluator_(std::move(penalty_evaluator)) {}
3082 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
3084 const std::vector<int>& vehicles) {
3085 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
3088 for (
int node = 0; node <
model()->
Size(); node++) {
3090 std::vector<StartEndValue>& start_end_distances =
3091 start_end_distances_per_node[node];
3093 for (
const int vehicle : vehicles) {
3098 const int64 distance =
3099 CapAdd(
model()->GetArcCostForVehicle(start, node, vehicle),
3100 model()->GetArcCostForVehicle(node, end, vehicle));
3101 start_end_distances.push_back({distance, vehicle});
3105 std::sort(start_end_distances.begin(), start_end_distances.end(),
3107 return second < first;
3110 return start_end_distances_per_node;
3113 template <
class Queue>
3115 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
3116 Queue* priority_queue) {
3118 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
3120 for (
int node = 0; node < num_nodes; node++) {
3122 std::vector<StartEndValue>& start_end_distances =
3123 (*start_end_distances_per_node)[node];
3124 if (start_end_distances.empty()) {
3128 const StartEndValue& start_end_value = start_end_distances.back();
3129 priority_queue->push(std::make_pair(start_end_value, node));
3130 start_end_distances.pop_back();
3144 std::vector<ValuedPosition>* valued_positions) {
3145 CHECK(valued_positions !=
nullptr);
3146 int64 insert_after = start;
3147 while (!
model()->IsEnd(insert_after)) {
3148 const int64 insert_before =
3149 (insert_after == start) ? next_after_start :
Value(insert_after);
3150 valued_positions->push_back(std::make_pair(
3152 insert_before, vehicle),
3154 insert_after = insert_before;
3160 int vehicle)
const {
3162 evaluator_(node_to_insert, insert_before, vehicle)),
3163 evaluator_(insert_after, insert_before, vehicle));
3167 int64 node_to_insert)
const {
3176 void SortAndExtractPairSeconds(std::vector<std::pair<int64, T>>* pairs,
3177 std::vector<T>* sorted_seconds) {
3178 CHECK(pairs !=
nullptr);
3179 CHECK(sorted_seconds !=
nullptr);
3180 std::sort(pairs->begin(), pairs->end());
3181 sorted_seconds->reserve(pairs->size());
3182 for (
const std::pair<int64, T>& p : *pairs) {
3183 sorted_seconds->push_back(p.second);
3207 if (value_ != other.value_) {
3208 return value_ > other.value_;
3210 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
3211 return vehicle_ == -1;
3213 return std::tie(pickup_insert_after_, pickup_to_insert_,
3214 delivery_insert_after_, delivery_to_insert_, vehicle_) >
3215 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
3216 other.delivery_insert_after_, other.delivery_to_insert_,
3232 const int pickup_to_insert_;
3233 const int pickup_insert_after_;
3234 const int delivery_to_insert_;
3235 const int delivery_insert_after_;
3250 if (value_ != other.value_) {
3251 return value_ > other.value_;
3253 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
3254 return vehicle_ == -1;
3256 return std::tie(insert_after_, node_to_insert_, vehicle_) >
3257 std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
3270 const int node_to_insert_;
3271 const int insert_after_;
3281 std::function<
int64(
int64)> penalty_evaluator,
3285 std::move(penalty_evaluator),
3288 node_index_to_vehicle_(
model->Size(), -1) {
3292 const int64 num_non_start_end_nodes = NumNonStartEndNodes();
3293 const int64 num_neighbors =
3296 if (num_neighbors >= num_non_start_end_nodes - 1) {
3305 std::iota(all_nodes_.begin(), all_nodes_.end(), 0);
3309 void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
3311 !node_index_to_neighbors_by_cost_class_.empty()) {
3317 const int64 num_non_start_end_nodes = NumNonStartEndNodes();
3318 const int64 num_neighbors =
3322 DCHECK_LT(num_neighbors, num_non_start_end_nodes - 1);
3324 const RoutingModel& routing_model = *
model();
3325 const int64 size = routing_model.Size();
3326 node_index_to_neighbors_by_cost_class_.resize(size);
3327 const int num_cost_classes = routing_model.GetCostClassesCount();
3328 for (
int64 node_index = 0; node_index < size; node_index++) {
3329 node_index_to_neighbors_by_cost_class_[node_index].resize(num_cost_classes);
3330 for (
int cc = 0; cc < num_cost_classes; cc++) {
3331 node_index_to_neighbors_by_cost_class_[node_index][cc] =
3332 absl::make_unique<SparseBitset<int64>>(size);
3336 for (
int64 node_index = 0; node_index < size; ++node_index) {
3337 DCHECK(!routing_model.IsEnd(node_index));
3338 if (routing_model.IsStart(node_index)) {
3345 for (
int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
3346 if (!routing_model.HasVehicleWithCostClassIndex(
3347 RoutingCostClassIndex(cost_class))) {
3351 std::vector<std::pair<
int64,
int64>> costed_after_nodes;
3352 costed_after_nodes.reserve(size);
3353 for (
int after_node = 0; after_node < size; ++after_node) {
3354 if (after_node != node_index && !routing_model.IsStart(after_node)) {
3355 costed_after_nodes.push_back(
3356 std::make_pair(routing_model.GetArcCostForClass(
3357 node_index, after_node, cost_class),
3361 std::nth_element(costed_after_nodes.begin(),
3362 costed_after_nodes.begin() + num_neighbors - 1,
3363 costed_after_nodes.end());
3364 costed_after_nodes.resize(num_neighbors);
3366 for (
auto [
cost, neighbor] : costed_after_nodes) {
3367 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
3371 DCHECK(!routing_model.IsEnd(neighbor) &&
3372 !routing_model.IsStart(neighbor));
3373 node_index_to_neighbors_by_cost_class_[neighbor][cost_class]->Set(
3377 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
3378 const int64 vehicle_start = routing_model.Start(vehicle);
3379 node_index_to_neighbors_by_cost_class_[node_index][cost_class]->Set(
3381 node_index_to_neighbors_by_cost_class_[vehicle_start][cost_class]->Set(
3388 bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
3389 int cost_class,
int64 node_index,
int64 neighbor_index)
const {
3391 (*node_index_to_neighbors_by_cost_class_[node_index]
3392 [cost_class])[neighbor_index];
3395 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
3396 std::vector<bool> node_is_visited(
model()->
Size(), -1);
3399 node =
Value(node)) {
3400 if (node_index_to_vehicle_[node] != v) {
3403 node_is_visited[node] =
true;
3407 for (
int node = 0; node <
model()->
Size(); node++) {
3408 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
3417 ComputeNeighborhoods();
3421 std::vector<int> pairs_to_insert;
3422 absl::flat_hash_map<int, std::vector<int>> vehicle_to_pair_nodes;
3425 int pickup_vehicle = -1;
3426 for (
int64 pickup : index_pair.first) {
3428 pickup_vehicle = node_index_to_vehicle_[pickup];
3432 int delivery_vehicle = -1;
3433 for (
int64 delivery : index_pair.second) {
3435 delivery_vehicle = node_index_to_vehicle_[delivery];
3439 if (pickup_vehicle < 0 && delivery_vehicle < 0) {
3440 pairs_to_insert.push_back(
index);
3442 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
3443 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle];
3444 for (
int64 delivery : index_pair.second) {
3445 pair_nodes.push_back(delivery);
3448 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
3449 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle];
3450 for (
int64 pickup : index_pair.first) {
3451 pair_nodes.push_back(pickup);
3455 for (
const auto& vehicle_and_nodes : vehicle_to_pair_nodes) {
3456 InsertNodesOnRoutes(vehicle_and_nodes.second, {vehicle_and_nodes.first});
3459 InsertPairsAndNodesByRequirementTopologicalOrder();
3463 InsertPairs(pairs_to_insert);
3464 std::vector<int> nodes;
3465 for (
int node = 0; node <
model()->
Size(); ++node) {
3468 nodes.push_back(node);
3471 InsertFarthestNodesAsSeeds();
3473 SequentialInsertNodes(nodes);
3475 InsertNodesOnRoutes(nodes, {});
3478 DCHECK(CheckVehicleIndices());
3482 void GlobalCheapestInsertionFilteredHeuristic::
3483 InsertPairsAndNodesByRequirementTopologicalOrder() {
3484 for (
const std::vector<int>& types :
3485 model()->GetTopologicallySortedVisitTypes()) {
3486 for (
int type : types) {
3487 InsertPairs(
model()->GetPairIndicesOfType(type));
3488 InsertNodesOnRoutes(
model()->GetSingleNodesOfType(type), {});
3493 void GlobalCheapestInsertionFilteredHeuristic::InsertPairs(
3494 const std::vector<int>& pair_indices) {
3496 std::vector<PairEntries> pickup_to_entries;
3497 std::vector<PairEntries> delivery_to_entries;
3498 InitializePairPositions(pair_indices, &priority_queue, &pickup_to_entries,
3499 &delivery_to_entries);
3500 while (!priority_queue.
IsEmpty()) {
3502 for (PairEntry*
const entry : *priority_queue.
Raw()) {
3507 PairEntry*
const entry = priority_queue.
Top();
3508 if (
Contains(entry->pickup_to_insert()) ||
3509 Contains(entry->delivery_to_insert())) {
3510 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3511 &delivery_to_entries);
3515 if (entry->vehicle() == -1) {
3517 SetValue(entry->pickup_to_insert(), entry->pickup_to_insert());
3518 SetValue(entry->delivery_to_insert(), entry->delivery_to_insert());
3520 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3521 &delivery_to_entries);
3527 const int64 pickup_insert_before =
Value(entry->pickup_insert_after());
3528 InsertBetween(entry->pickup_to_insert(), entry->pickup_insert_after(),
3529 pickup_insert_before);
3530 const int64 delivery_insert_before =
3531 (entry->pickup_to_insert() == entry->delivery_insert_after())
3532 ? pickup_insert_before
3533 :
Value(entry->delivery_insert_after());
3534 InsertBetween(entry->delivery_to_insert(), entry->delivery_insert_after(),
3535 delivery_insert_before);
3537 const int64 pickup_after = entry->pickup_insert_after();
3538 const int64 pickup = entry->pickup_to_insert();
3539 const int64 delivery_after = entry->delivery_insert_after();
3540 const int64 delivery = entry->delivery_to_insert();
3541 const int vehicle = entry->vehicle();
3542 UpdatePairPositions(pair_indices, vehicle, pickup_after, &priority_queue,
3543 &pickup_to_entries, &delivery_to_entries);
3544 UpdatePairPositions(pair_indices, vehicle, pickup, &priority_queue,
3545 &pickup_to_entries, &delivery_to_entries);
3546 UpdatePairPositions(pair_indices, vehicle, delivery, &priority_queue,
3547 &pickup_to_entries, &delivery_to_entries);
3548 if (pickup != delivery_after) {
3549 UpdatePairPositions(pair_indices, vehicle, delivery_after,
3550 &priority_queue, &pickup_to_entries,
3551 &delivery_to_entries);
3553 SetVehicleIndex(pickup, vehicle);
3554 SetVehicleIndex(delivery, vehicle);
3556 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3557 &delivery_to_entries);
3562 void GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
3563 const std::vector<int>& nodes,
const absl::flat_hash_set<int>& vehicles) {
3565 std::vector<NodeEntries> position_to_node_entries;
3566 InitializePositions(nodes, &priority_queue, &position_to_node_entries,
3573 const bool all_vehicles =
3576 while (!priority_queue.
IsEmpty()) {
3577 NodeEntry*
const node_entry = priority_queue.
Top();
3579 for (NodeEntry*
const entry : *priority_queue.
Raw()) {
3586 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3590 if (node_entry->vehicle() == -1) {
3593 SetValue(node_to_insert, node_to_insert);
3595 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3601 const int64 insert_after = node_entry->insert_after();
3604 const int vehicle = node_entry->vehicle();
3605 UpdatePositions(nodes, vehicle, node_to_insert, all_vehicles,
3606 &priority_queue, &position_to_node_entries);
3607 UpdatePositions(nodes, vehicle, insert_after, all_vehicles,
3608 &priority_queue, &position_to_node_entries);
3609 SetVehicleIndex(node_to_insert, vehicle);
3611 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3616 void GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
3617 const std::vector<int>& nodes) {
3618 std::vector<bool> is_vehicle_used;
3619 absl::flat_hash_set<int> used_vehicles;
3620 std::vector<int> unused_vehicles;
3622 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
3623 if (!used_vehicles.empty()) {
3624 InsertNodesOnRoutes(nodes, used_vehicles);
3627 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
3629 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
3633 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
3636 while (vehicle >= 0) {
3637 InsertNodesOnRoutes(nodes, {vehicle});
3638 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
3643 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
3644 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
3645 absl::flat_hash_set<int>* used_vehicles) {
3646 is_vehicle_used->clear();
3647 is_vehicle_used->resize(
model()->vehicles());
3649 used_vehicles->clear();
3650 used_vehicles->reserve(
model()->vehicles());
3652 unused_vehicles->clear();
3653 unused_vehicles->reserve(
model()->vehicles());
3655 for (
int vehicle = 0; vehicle <
model()->
vehicles(); vehicle++) {
3657 (*is_vehicle_used)[vehicle] =
true;
3658 used_vehicles->insert(vehicle);
3660 (*is_vehicle_used)[vehicle] =
false;
3661 unused_vehicles->push_back(vehicle);
3666 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
3669 const int num_seeds =
static_cast<int>(
3672 std::vector<bool> is_vehicle_used;
3673 absl::flat_hash_set<int> used_vehicles;
3674 std::vector<int> unused_vehicles;
3675 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
3676 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
3681 std::priority_queue<Seed> farthest_node_queue;
3684 int inserted_seeds = 0;
3685 while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) {
3686 InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
3692 template <
class Queue>
3693 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
3694 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
3695 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
3696 while (!priority_queue->empty()) {
3698 const Seed& seed = priority_queue->top();
3700 const int seed_node = seed.second;
3701 const int seed_vehicle = seed.first.vehicle;
3703 std::vector<StartEndValue>& other_start_end_values =
3704 (*start_end_distances_per_node)[seed_node];
3709 priority_queue->pop();
3710 other_start_end_values.clear();
3713 if (!(*is_vehicle_used)[seed_vehicle]) {
3720 priority_queue->pop();
3721 (*is_vehicle_used)[seed_vehicle] =
true;
3722 other_start_end_values.clear();
3723 SetVehicleIndex(seed_node, seed_vehicle);
3724 return seed_vehicle;
3731 priority_queue->pop();
3732 if (!other_start_end_values.empty()) {
3733 const StartEndValue& next_seed_value = other_start_end_values.back();
3734 priority_queue->push(std::make_pair(next_seed_value, seed_node));
3735 other_start_end_values.pop_back();
3742 void GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
3743 const std::vector<int>& pair_indices,
3745 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3746 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3748 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3749 delivery_to_entries) {
3750 priority_queue->Clear();
3751 pickup_to_entries->clear();
3752 pickup_to_entries->resize(
model()->
Size());
3753 delivery_to_entries->clear();
3754 delivery_to_entries->resize(
model()->
Size());
3757 for (
int index : pair_indices) {
3759 for (
int64 pickup : index_pair.first) {
3761 for (
int64 delivery : index_pair.second) {
3765 const int64 penalty =
3766 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
3767 ?
CapAdd(pickup_penalty, delivery_penalty)
3774 index_pair.first.size() == 1 && index_pair.second.size() == 1 &&
3776 PairEntry*
const entry =
new PairEntry(pickup, -1, delivery, -1, -1);
3778 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
3780 :
CapAdd(pickup_penalty, delivery_penalty));
3781 priority_queue->Add(entry);
3784 InitializeInsertionEntriesPerformingPair(
3785 pickup, delivery, penalty, priority_queue, pickup_to_entries,
3786 delivery_to_entries);
3792 void GlobalCheapestInsertionFilteredHeuristic::
3793 InitializeInsertionEntriesPerformingPair(
3796 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
3798 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3800 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3801 delivery_to_entries) {
3803 std::vector<std::pair<std::pair<int64, int>, std::pair<int64, int64>>>
3805 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3806 std::vector<ValuedPosition> valued_pickup_positions;
3809 &valued_pickup_positions);
3811 valued_pickup_positions) {
3812 const int64 pickup_position = valued_pickup_position.second;
3814 std::vector<ValuedPosition> valued_delivery_positions;
3816 vehicle, &valued_delivery_positions);
3818 valued_delivery_positions) {
3819 valued_positions.push_back(std::make_pair(
3820 std::make_pair(
CapAdd(valued_pickup_position.first,
3821 valued_delivery_position.first),
3823 std::make_pair(pickup_position,
3824 valued_delivery_position.second)));
3828 for (
const std::pair<std::pair<int64, int>, std::pair<int64, int64>>&
3829 valued_position : valued_positions) {
3830 PairEntry*
const entry =
new PairEntry(
3831 pickup, valued_position.second.first, delivery,
3832 valued_position.second.second, valued_position.first.second);
3833 entry->set_value(
CapSub(valued_position.first.first, penalty));
3834 pickup_to_entries->at(valued_position.second.first).insert(entry);
3835 DCHECK_NE(valued_position.second.first, valued_position.second.second);
3836 delivery_to_entries->at(valued_position.second.second).insert(entry);
3837 priority_queue->Add(entry);
3846 absl::flat_hash_set<std::pair<int64, int64>> existing_insertion_positions;
3848 for (
const int64 pickup_insert_after :
3849 GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
3850 if (!
Contains(pickup_insert_after)) {
3853 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
3859 pickup, pickup_insert_after,
Value(pickup_insert_after), vehicle);
3860 int64 delivery_insert_after = pickup;
3861 while (!
model()->IsEnd(delivery_insert_after)) {
3862 const std::pair<int64, int64> insertion_position = {
3863 pickup_insert_after, delivery_insert_after};
3865 insertion_position));
3866 existing_insertion_positions.insert(insertion_position);
3867 PairEntry*
const entry =
3868 new PairEntry(pickup, pickup_insert_after, delivery,
3869 delivery_insert_after, vehicle);
3870 pickup_to_entries->at(pickup_insert_after).insert(entry);
3871 delivery_to_entries->at(delivery_insert_after).insert(entry);
3873 const int64 delivery_insert_before = (delivery_insert_after == pickup)
3874 ?
Value(pickup_insert_after)
3875 :
Value(delivery_insert_after);
3877 delivery, delivery_insert_after, delivery_insert_before, vehicle);
3878 entry->set_value(
CapSub(
CapAdd(pickup_value, delivery_value), penalty));
3879 priority_queue->Add(entry);
3880 delivery_insert_after = delivery_insert_before;
3885 for (
const int64 delivery_insert_after :
3886 GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
3887 if (!
Contains(delivery_insert_after)) {
3890 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
3891 if (
model()->GetCostClassIndexOfVehicle(vehicle).
value() != cost_class) {
3896 delivery, delivery_insert_after,
Value(delivery_insert_after),
3899 while (pickup_insert_after != delivery_insert_after) {
3900 const int64 pickup_insert_before =
Value(pickup_insert_after);
3902 existing_insertion_positions,
3903 std::make_pair(pickup_insert_after, delivery_insert_after))) {
3904 pickup_insert_after = pickup_insert_before;
3907 PairEntry*
const entry =
3908 new PairEntry(pickup, pickup_insert_after, delivery,
3909 delivery_insert_after, vehicle);
3910 pickup_to_entries->at(pickup_insert_after).insert(entry);
3911 delivery_to_entries->at(delivery_insert_after).insert(entry);
3913 pickup, pickup_insert_after, pickup_insert_before, vehicle);
3914 entry->set_value(
CapSub(
CapAdd(pickup_value, delivery_value), penalty));
3915 priority_queue->Add(entry);
3916 pickup_insert_after = pickup_insert_before;
3922 void GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
3923 const std::vector<int>& pair_indices,
int vehicle,
3924 int64 pickup_insert_after,
3926 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3927 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3929 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3930 delivery_to_entries) {
3933 using Pair = std::pair<int64, int64>;
3934 using Insertion = std::pair<Pair,
int64>;
3935 absl::flat_hash_set<Insertion> existing_insertions;
3936 std::vector<PairEntry*> to_remove;
3937 for (PairEntry*
const pair_entry :
3938 pickup_to_entries->at(pickup_insert_after)) {
3939 DCHECK(priority_queue->Contains(pair_entry));
3940 DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
3941 if (
Contains(pair_entry->pickup_to_insert()) ||
3942 Contains(pair_entry->delivery_to_insert())) {
3943 to_remove.push_back(pair_entry);
3945 existing_insertions.insert(
3946 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
3947 pair_entry->delivery_insert_after()});
3950 for (PairEntry*
const pair_entry : to_remove) {
3951 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
3952 delivery_to_entries);
3957 const int64 pickup_insert_before =
Value(pickup_insert_after);
3960 for (
int pair_index : pair_indices) {
3962 pickup_delivery_pairs[pair_index];
3963 for (
int64 pickup : index_pair.first) {
3965 !IsNeighborForCostClass(cost_class, pickup_insert_after, pickup)) {
3968 for (
int64 delivery : index_pair.second) {
3972 int64 delivery_insert_after = pickup;
3973 while (!
model()->IsEnd(delivery_insert_after)) {
3974 const std::pair<Pair, int64> insertion = {{pickup, delivery},
3975 delivery_insert_after};
3977 PairEntry*
const entry =
3978 new PairEntry(pickup, pickup_insert_after, delivery,
3979 delivery_insert_after, vehicle);
3980 pickup_to_entries->at(pickup_insert_after).insert(entry);
3981 delivery_to_entries->at(delivery_insert_after).insert(entry);
3983 if (delivery_insert_after == pickup) {
3984 delivery_insert_after = pickup_insert_before;
3986 delivery_insert_after =
Value(delivery_insert_after);
3995 for (PairEntry*
const pair_entry :
3996 pickup_to_entries->at(pickup_insert_after)) {
3997 const int64 pickup = pair_entry->pickup_to_insert();
3998 const int64 delivery = pair_entry->delivery_to_insert();
3999 DCHECK_EQ(pickup_insert_after, pair_entry->pickup_insert_after());
4001 pickup, pickup_insert_after, pickup_insert_before, vehicle);
4002 const int64 delivery_insert_after = pair_entry->delivery_insert_after();
4003 const int64 delivery_insert_before = (delivery_insert_after == pickup)
4004 ? pickup_insert_before
4005 :
Value(delivery_insert_after);
4007 delivery, delivery_insert_after, delivery_insert_before, vehicle);
4008 const int64 penalty =
4009 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
4012 pair_entry->set_value(
4013 CapSub(
CapAdd(pickup_value, delivery_value), penalty));
4014 const std::pair<Pair, int64> insertion = {{pickup, delivery},
4015 delivery_insert_after};
4017 DCHECK(priority_queue->Contains(pair_entry));
4018 priority_queue->NoteChangedPriority(pair_entry);
4020 DCHECK(!priority_queue->Contains(pair_entry));
4021 priority_queue->Add(pair_entry);
4026 void GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
4027 const std::vector<int>& pair_indices,
int vehicle,
4028 int64 delivery_insert_after,
4030 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
4031 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
4033 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
4034 delivery_to_entries) {
4037 using Pair = std::pair<int64, int64>;
4038 using Insertion = std::pair<Pair,
int64>;
4039 absl::flat_hash_set<Insertion> existing_insertions;
4040 std::vector<PairEntry*> to_remove;
4041 for (PairEntry*
const pair_entry :
4042 delivery_to_entries->at(delivery_insert_after)) {
4043 DCHECK(priority_queue->Contains(pair_entry));
4044 DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
4045 if (
Contains(pair_entry->pickup_to_insert()) ||
4046 Contains(pair_entry->delivery_to_insert())) {
4047 to_remove.push_back(pair_entry);
4049 existing_insertions.insert(
4050 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
4051 pair_entry->pickup_insert_after()});
4054 for (PairEntry*
const pair_entry : to_remove) {
4055 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
4056 delivery_to_entries);
4061 const int64 delivery_insert_before =
Value(delivery_insert_after);
4064 for (
int pair_index : pair_indices) {
4066 pickup_delivery_pairs[pair_index];
4067 for (
int64 delivery : index_pair.second) {
4069 !IsNeighborForCostClass(cost_class, delivery_insert_after,
4073 for (
int64 pickup : index_pair.first) {
4078 while (pickup_insert_after != delivery_insert_after) {
4079 std::pair<Pair, int64> insertion = {{pickup, delivery},
4080 pickup_insert_after};
4082 PairEntry*
const entry =
4083 new PairEntry(pickup, pickup_insert_after, delivery,
4084 delivery_insert_after, vehicle);
4085 pickup_to_entries->at(pickup_insert_after).insert(entry);
4086 delivery_to_entries->at(delivery_insert_after).insert(entry);
4088 pickup_insert_after =
Value(pickup_insert_after);
4096 for (PairEntry*
const pair_entry :
4097 delivery_to_entries->at(delivery_insert_after)) {
4098 const int64 pickup = pair_entry->pickup_to_insert();
4099 const int64 delivery = pair_entry->delivery_to_insert();
4100 DCHECK_EQ(delivery_insert_after, pair_entry->delivery_insert_after());
4101 const int64 pickup_insert_after = pair_entry->pickup_insert_after();
4103 pickup, pickup_insert_after,
Value(pickup_insert_after), vehicle);
4105 delivery, delivery_insert_after, delivery_insert_before, vehicle);
4106 const int64 penalty =
4107 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
4110 pair_entry->set_value(
4111 CapSub(
CapAdd(pickup_value, delivery_value), penalty));
4112 const std::pair<Pair, int64> insertion = {{pickup, delivery},
4113 pickup_insert_after};
4115 DCHECK(priority_queue->Contains(pair_entry));
4116 priority_queue->NoteChangedPriority(pair_entry);
4118 DCHECK(!priority_queue->Contains(pair_entry));
4119 priority_queue->Add(pair_entry);
4124 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
4125 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
4127 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
4128 std::vector<PairEntries>* pickup_to_entries,
4129 std::vector<PairEntries>* delivery_to_entries) {
4130 priority_queue->Remove(entry);
4131 if (entry->pickup_insert_after() != -1) {
4132 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
4134 if (entry->delivery_insert_after() != -1) {
4135 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
4140 void GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
4141 const std::vector<int>& nodes,
4143 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4144 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4145 position_to_node_entries,
4146 const absl::flat_hash_set<int>& vehicles) {
4147 priority_queue->Clear();
4148 position_to_node_entries->clear();
4149 position_to_node_entries->resize(
model()->
Size());
4151 const int num_vehicles =
4153 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
4155 for (
int node : nodes) {
4160 const int64 penalty =
4161 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
4169 NodeEntry*
const node_entry =
new NodeEntry(node, -1, -1);
4170 node_entry->set_value(
4171 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
4174 priority_queue->Add(node_entry);
4177 InitializeInsertionEntriesPerformingNode(
4178 node, penalty, vehicles, priority_queue, position_to_node_entries);
4182 void GlobalCheapestInsertionFilteredHeuristic::
4183 InitializeInsertionEntriesPerformingNode(
4184 int64 node,
int64 penalty,
const absl::flat_hash_set<int>& vehicles,
4186 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
4188 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4189 position_to_node_entries) {
4190 const int num_vehicles =
4192 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
4195 auto vehicles_it = vehicles.begin();
4196 for (
int v = 0; v < num_vehicles; v++) {
4197 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
4199 std::vector<ValuedPosition> valued_positions;
4203 for (
const std::pair<int64, int64>& valued_position : valued_positions) {
4204 if (!all_vehicles && valued_position.first > unperformed_value) {
4210 NodeEntry*
const node_entry =
4211 new NodeEntry(node, valued_position.second, vehicle);
4212 node_entry->set_value(
CapSub(valued_position.first, penalty));
4213 position_to_node_entries->at(valued_position.second).insert(node_entry);
4214 priority_queue->Add(node_entry);
4222 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
4223 int v,
int cost_class) {
4225 (all_vehicles || vehicles.contains(v));
4229 for (
const int64 insert_after :
4230 GetNeighborsOfNodeForCostClass(cost_class, node)) {
4234 const int vehicle = node_index_to_vehicle_[insert_after];
4235 if (vehicle == -1 ||
4236 !insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
4240 node, insert_after,
Value(insert_after), vehicle);
4241 if (!all_vehicles && insertion_cost > unperformed_value) {
4247 NodeEntry*
const node_entry =
new NodeEntry(node, insert_after, vehicle);
4248 node_entry->set_value(
CapSub(insertion_cost, penalty));
4249 position_to_node_entries->at(insert_after).insert(node_entry);
4250 priority_queue->Add(node_entry);
4255 void GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
4256 const std::vector<int>& nodes,
int vehicle,
int64 insert_after,
4259 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4260 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4264 std::vector<NodeEntry*> to_remove;
4265 absl::flat_hash_set<int> existing_insertions;
4266 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
4267 DCHECK(priority_queue->Contains(node_entry));
4268 DCHECK_EQ(node_entry->insert_after(), insert_after);
4269 if (
Contains(node_entry->node_to_insert())) {
4270 to_remove.push_back(node_entry);
4272 existing_insertions.insert(node_entry->node_to_insert());
4275 for (NodeEntry*
const node_entry : to_remove) {
4276 DeleteNodeEntry(node_entry, priority_queue, node_entries);
4279 for (
int node_to_insert : nodes) {
4281 !existing_insertions.contains(node_to_insert) &&
4282 IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
4284 node_to_insert, insert_after,
Value(insert_after), vehicle);
4285 if (!all_vehicles &&
4292 NodeEntry*
const node_entry =
4293 new NodeEntry(node_to_insert, insert_after, vehicle);
4294 node_entries->at(insert_after).insert(node_entry);
4301 const int64 insert_before =
Value(insert_after);
4302 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
4303 DCHECK_EQ(node_entry->insert_after(), insert_after);
4305 node_entry->node_to_insert(), insert_after, insert_before, vehicle);
4306 const int64 penalty =
4307 absl::GetFlag(FLAGS_routing_shift_insertion_cost_by_penalty)
4312 DCHECK(priority_queue->Contains(node_entry));
4313 priority_queue->NoteChangedPriority(node_entry);
4315 DCHECK(!priority_queue->Contains(node_entry));
4316 priority_queue->Add(node_entry);
4321 void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
4322 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
4324 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4325 std::vector<NodeEntries>* node_entries) {
4326 priority_queue->Remove(entry);
4327 if (entry->insert_after() != -1) {
4328 node_entries->at(entry->insert_after()).erase(entry);
4343 std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
4345 start_end_distances_per_node_ =
4351 std::vector<bool> visited(
model()->
Size(),
false);
4353 std::vector<int64> insertion_positions;
4356 std::vector<int64> delivery_insertion_positions;
4360 for (
const auto& index_pair : index_pairs) {
4361 for (
int64 pickup : index_pair.first) {
4365 for (
int64 delivery : index_pair.second) {
4372 visited[pickup] =
true;
4373 visited[delivery] =
true;
4374 ComputeEvaluatorSortedPositions(pickup, &insertion_positions);
4375 for (
const int64 pickup_insertion : insertion_positions) {
4376 const int pickup_insertion_next =
Value(pickup_insertion);
4377 ComputeEvaluatorSortedPositionsOnRouteAfter(
4378 delivery, pickup, pickup_insertion_next,
4379 &delivery_insertion_positions);
4381 for (
const int64 delivery_insertion : delivery_insertion_positions) {
4382 InsertBetween(pickup, pickup_insertion, pickup_insertion_next);
4383 const int64 delivery_insertion_next =
4384 (delivery_insertion == pickup_insertion) ? pickup
4385 : (delivery_insertion == pickup) ? pickup_insertion_next
4386 :
Value(delivery_insertion);
4388 delivery_insertion_next);
4402 std::priority_queue<Seed> node_queue;
4405 while (!node_queue.empty()) {
4406 const int node = node_queue.top().second;
4408 if (
Contains(node) || visited[node])
continue;
4409 ComputeEvaluatorSortedPositions(node, &insertion_positions);
4410 for (
const int64 insertion : insertion_positions) {
4422 void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
4423 int64 node, std::vector<int64>* sorted_positions) {
4424 CHECK(sorted_positions !=
nullptr);
4426 sorted_positions->clear();
4429 std::vector<std::pair<int64, int64>> valued_positions;
4430 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
4435 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
4439 void LocalCheapestInsertionFilteredHeuristic::
4440 ComputeEvaluatorSortedPositionsOnRouteAfter(
4442 std::vector<int64>* sorted_positions) {
4443 CHECK(sorted_positions !=
nullptr);
4445 sorted_positions->clear();
4449 std::vector<std::pair<int64, int64>> valued_positions;
4452 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
4465 std::vector<std::vector<int64>> deliveries(
Size());
4466 std::vector<std::vector<int64>> pickups(
Size());
4468 for (
int first : pair.first) {
4469 for (
int second : pair.second) {
4470 deliveries[first].push_back(second);
4471 pickups[second].push_back(first);
4478 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
4479 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
4480 sorted_vehicles[vehicle] = vehicle;
4482 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
4483 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
4485 for (
const int vehicle : sorted_vehicles) {
4487 bool extend_route =
true;
4492 while (extend_route) {
4493 extend_route =
false;
4504 std::vector<int64> neighbors;
4506 std::unique_ptr<IntVarIterator> it(
4507 model()->Nexts()[
index]->MakeDomainIterator(
false));
4509 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
4512 for (
int i = 0; !found && i < neighbors.size(); ++i) {
4516 next = FindTopSuccessor(
index, neighbors);
4519 SortSuccessors(
index, &neighbors);
4520 ABSL_FALLTHROUGH_INTENDED;
4522 next = neighbors[i];
4529 bool contains_pickups =
false;
4532 contains_pickups =
true;
4536 if (!contains_pickups) {
4540 std::vector<int64> next_deliveries;
4541 if (
next < deliveries.size()) {
4542 next_deliveries = GetPossibleNextsFromIterator(
4543 next, deliveries[
next].begin(), deliveries[
next].end());
4545 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
4546 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
4551 delivery = FindTopSuccessor(
next, next_deliveries);
4554 SortSuccessors(
next, &next_deliveries);
4555 ABSL_FALLTHROUGH_INTENDED;
4557 delivery = next_deliveries[j];
4575 if (
model()->IsEnd(end) && last_node != delivery) {
4576 last_node = delivery;
4577 extend_route =
true;
4592 bool CheapestAdditionFilteredHeuristic::
4593 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
4594 int vehicle2)
const {
4595 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
4596 builder_.GetStartChainEnd(vehicle1));
4597 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
4598 builder_.GetStartChainEnd(vehicle2));
4599 if (has_partial_route1 == has_partial_route2) {
4600 return vehicle2 < vehicle1;
4602 return has_partial_route2 < has_partial_route1;
4615 int64 EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
4616 int64 node,
const std::vector<int64>& successors) {
4618 int64 best_successor = -1;
4619 for (
int64 successor : successors) {
4620 const int64 evaluation =
4621 (successor >= 0) ? evaluator_(node, successor) :
kint64max;
4622 if (evaluation < best_evaluation ||
4623 (evaluation == best_evaluation && successor > best_successor)) {
4624 best_evaluation = evaluation;
4625 best_successor = successor;
4628 return best_successor;
4631 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
4632 int64 node, std::vector<int64>* successors) {
4633 std::vector<std::pair<int64, int64>> values;
4634 values.reserve(successors->size());
4635 for (
int64 successor : *successors) {
4638 values.push_back({evaluator_(node, successor), -successor});
4640 std::sort(values.begin(), values.end());
4641 successors->clear();
4642 for (
auto value : values) {
4643 successors->push_back(-
value.second);
4654 comparator_(std::move(comparator)) {}
4656 int64 ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
4657 int64 node,
const std::vector<int64>& successors) {
4658 return *std::min_element(successors.begin(), successors.end(),
4659 [
this, node](
int successor1,
int successor2) {
4660 return comparator_(node, successor1, successor2);
4664 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
4665 int64 node, std::vector<int64>* successors) {
4666 std::sort(successors->begin(), successors->end(),
4667 [
this, node](
int successor1,
int successor2) {
4668 return comparator_(node, successor1, successor2);
4720 template <
typename Saving>
4725 : savings_db_(savings_db),
4726 vehicle_types_(vehicle_types),
4727 index_in_sorted_savings_(0),
4728 single_vehicle_type_(vehicle_types == 1),
4729 using_incoming_reinjected_saving_(false),
4734 sorted_savings_per_vehicle_type_.clear();
4735 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
4736 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
4737 savings.reserve(size * saving_neighbors);
4740 sorted_savings_.clear();
4741 costs_and_savings_per_arc_.clear();
4742 arc_indices_per_before_node_.clear();
4744 if (!single_vehicle_type_) {
4745 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
4746 arc_indices_per_before_node_.resize(size);
4747 for (
int before_node = 0; before_node < size; before_node++) {
4748 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
4751 skipped_savings_starting_at_.clear();
4752 skipped_savings_starting_at_.resize(size);
4753 skipped_savings_ending_at_.clear();
4754 skipped_savings_ending_at_.resize(size);
4755 incoming_reinjected_savings_ =
nullptr;
4756 outgoing_reinjected_savings_ =
nullptr;
4757 incoming_new_reinjected_savings_ =
nullptr;
4758 outgoing_new_reinjected_savings_ =
nullptr;
4762 int64 after_node,
int vehicle_type) {
4763 CHECK(!sorted_savings_per_vehicle_type_.empty())
4764 <<
"Container not initialized!";
4765 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
4766 UpdateArcIndicesCostsAndSavings(before_node, after_node,
4767 {total_cost, saving});
4771 CHECK(!sorted_) <<
"Container already sorted!";
4773 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
4774 std::sort(savings.begin(), savings.end());
4777 if (single_vehicle_type_) {
4778 const auto& savings = sorted_savings_per_vehicle_type_[0];
4779 sorted_savings_.resize(savings.size());
4780 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
4781 [](
const Saving& saving) {
4782 return SavingAndArc({saving, -1});
4788 sorted_savings_.reserve(vehicle_types_ *
4789 costs_and_savings_per_arc_.size());
4791 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
4793 std::vector<std::pair<int64, Saving>>& costs_and_savings =
4794 costs_and_savings_per_arc_[arc_index];
4795 DCHECK(!costs_and_savings.empty());
4798 costs_and_savings.begin(), costs_and_savings.end(),
4799 [](
const std::pair<int64, Saving>& cs1,
4800 const std::pair<int64, Saving>& cs2) { return cs1 > cs2; });
4805 const int64 cost = costs_and_savings.back().first;
4806 while (!costs_and_savings.empty() &&
4807 costs_and_savings.back().first ==
cost) {
4808 sorted_savings_.push_back(
4809 {costs_and_savings.back().second, arc_index});
4810 costs_and_savings.pop_back();
4813 std::sort(sorted_savings_.begin(), sorted_savings_.end());
4814 next_saving_type_and_index_for_arc_.clear();
4815 next_saving_type_and_index_for_arc_.resize(
4816 costs_and_savings_per_arc_.size(), {-1, -1});
4819 index_in_sorted_savings_ = 0;
4824 return index_in_sorted_savings_ < sorted_savings_.size() ||
4825 HasReinjectedSavings();
4829 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
4831 <<
"Update() should be called between two calls to GetSaving() !";
4835 if (HasReinjectedSavings()) {
4836 if (incoming_reinjected_savings_ !=
nullptr &&
4837 outgoing_reinjected_savings_ !=
nullptr) {
4839 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
4840 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
4841 if (incoming_saving < outgoing_saving) {
4842 current_saving_ = incoming_saving;
4843 using_incoming_reinjected_saving_ =
true;
4845 current_saving_ = outgoing_saving;
4846 using_incoming_reinjected_saving_ =
false;
4849 if (incoming_reinjected_savings_ !=
nullptr) {
4850 current_saving_ = incoming_reinjected_savings_->front();
4851 using_incoming_reinjected_saving_ =
true;
4853 if (outgoing_reinjected_savings_ !=
nullptr) {
4854 current_saving_ = outgoing_reinjected_savings_->front();
4855 using_incoming_reinjected_saving_ =
false;
4859 current_saving_ = sorted_savings_[index_in_sorted_savings_];
4861 return current_saving_.saving;
4864 void Update(
bool update_best_saving,
int type = -1) {
4865 CHECK(to_update_) <<
"Container already up to date!";
4866 if (update_best_saving) {
4867 const int64 arc_index = current_saving_.arc_index;
4868 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
4870 if (!HasReinjectedSavings()) {
4871 index_in_sorted_savings_++;
4873 if (index_in_sorted_savings_ == sorted_savings_.size()) {
4874 sorted_savings_.swap(next_savings_);
4876 index_in_sorted_savings_ = 0;
4878 std::sort(sorted_savings_.begin(), sorted_savings_.end());
4879 next_saving_type_and_index_for_arc_.clear();
4880 next_saving_type_and_index_for_arc_.resize(
4881 costs_and_savings_per_arc_.size(), {-1, -1});
4884 UpdateReinjectedSavings();
4889 CHECK(!single_vehicle_type_);
4890 Update(
true, type);
4894 CHECK(sorted_) <<
"Savings not sorted yet!";
4896 return sorted_savings_per_vehicle_type_[type];
4900 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
4901 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
4905 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
4906 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
4910 struct SavingAndArc {
4914 bool operator<(
const SavingAndArc& other)
const {
4915 return std::tie(saving, arc_index) <
4916 std::tie(other.saving, other.arc_index);
4922 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
4923 const Saving& saving = saving_and_arc.saving;
4924 const int64 before_node = savings_db_->GetBeforeNodeFromSaving(saving);
4925 const int64 after_node = savings_db_->GetAfterNodeFromSaving(saving);
4926 if (!savings_db_->Contains(before_node)) {
4927 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
4929 if (!savings_db_->Contains(after_node)) {
4930 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
4944 void UpdateNextAndSkippedSavingsForArcWithType(
int64 arc_index,
int type) {
4945 if (single_vehicle_type_) {
4948 SkipSavingForArc(current_saving_);
4952 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
4953 const int previous_index = type_and_index.second;
4954 const int previous_type = type_and_index.first;
4955 bool next_saving_added =
false;
4958 if (previous_index >= 0) {
4961 if (type == -1 || previous_type == type) {
4964 next_saving_added =
true;
4965 next_saving = next_savings_[previous_index].saving;
4969 if (!next_saving_added &&
4970 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
4971 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
4972 if (previous_index >= 0) {
4974 next_savings_[previous_index] = {next_saving, arc_index};
4977 type_and_index.second = next_savings_.size();
4978 next_savings_.push_back({next_saving, arc_index});
4980 next_saving_added =
true;
4986 SkipSavingForArc(current_saving_);
4990 if (next_saving_added) {
4991 SkipSavingForArc({next_saving, arc_index});
4996 void UpdateReinjectedSavings() {
4997 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
4998 &incoming_reinjected_savings_,
4999 using_incoming_reinjected_saving_);
5000 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
5001 &outgoing_reinjected_savings_,
5002 !using_incoming_reinjected_saving_);
5003 incoming_new_reinjected_savings_ =
nullptr;
5004 outgoing_new_reinjected_savings_ =
nullptr;
5007 void UpdateGivenReinjectedSavings(
5008 std::deque<SavingAndArc>* new_reinjected_savings,
5009 std::deque<SavingAndArc>** reinjected_savings,
5010 bool using_reinjected_savings) {
5011 if (new_reinjected_savings ==
nullptr) {
5013 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
5014 CHECK(!(*reinjected_savings)->empty());
5015 (*reinjected_savings)->pop_front();
5016 if ((*reinjected_savings)->empty()) {
5017 *reinjected_savings =
nullptr;
5026 if (*reinjected_savings !=
nullptr) {
5027 (*reinjected_savings)->clear();
5029 *reinjected_savings =
nullptr;
5030 if (!new_reinjected_savings->empty()) {
5031 *reinjected_savings = new_reinjected_savings;
5035 bool HasReinjectedSavings() {
5036 return outgoing_reinjected_savings_ !=
nullptr ||
5037 incoming_reinjected_savings_ !=
nullptr;
5040 void UpdateArcIndicesCostsAndSavings(
5042 const std::pair<int64, Saving>& cost_and_saving) {
5043 if (single_vehicle_type_) {
5046 absl::flat_hash_map<int, int>& arc_indices =
5047 arc_indices_per_before_node_[before_node];
5048 const auto& arc_inserted = arc_indices.insert(
5049 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
5050 const int index = arc_inserted.first->second;
5051 if (arc_inserted.second) {
5052 costs_and_savings_per_arc_.push_back({cost_and_saving});
5055 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
5059 bool GetNextSavingForArcWithType(
int64 arc_index,
int type,
5060 Saving* next_saving) {
5061 std::vector<std::pair<int64, Saving>>& costs_and_savings =
5062 costs_and_savings_per_arc_[arc_index];
5064 bool found_saving =
false;
5065 while (!costs_and_savings.empty() && !found_saving) {
5066 const Saving& saving = costs_and_savings.back().second;
5067 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
5068 *next_saving = saving;
5069 found_saving =
true;
5071 costs_and_savings.pop_back();
5073 return found_saving;
5076 const SavingsFilteredHeuristic*
const savings_db_;
5077 const int vehicle_types_;
5078 int64 index_in_sorted_savings_;
5079 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
5080 std::vector<SavingAndArc> sorted_savings_;
5081 std::vector<SavingAndArc> next_savings_;
5082 std::vector<std::pair< int,
int>>
5083 next_saving_type_and_index_for_arc_;
5084 SavingAndArc current_saving_;
5085 const bool single_vehicle_type_;
5086 std::vector<std::vector<std::pair<
int64, Saving>>>
5087 costs_and_savings_per_arc_;
5088 std::vector<absl::flat_hash_map< int,
int>>
5089 arc_indices_per_before_node_;
5090 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
5091 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
5092 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
5093 std::deque<SavingAndArc>* incoming_reinjected_savings_;
5094 bool using_incoming_reinjected_saving_;
5095 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
5096 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
5103 SavingsFilteredHeuristic::SavingsFilteredHeuristic(
5107 vehicle_type_curator_(nullptr),
5115 size_squared_ = size * size;
5123 model()->GetVehicleTypeContainer());
5131 if (!
Commit())
return false;
5137 int type,
int64 before_node,
int64 after_node) {
5138 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
5153 type, vehicle_is_compatible);
5157 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
5158 std::vector<std::vector<int64>>* adjacency_lists) {
5159 for (
int64 node = 0; node < adjacency_lists->size(); node++) {
5160 for (
int64 neighbor : (*adjacency_lists)[node]) {
5161 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
5164 (*adjacency_lists)[neighbor].push_back(node);
5167 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
5168 adjacency_lists->begin(), [](std::vector<int64> vec) {
5169 std::sort(vec.begin(), vec.end());
5170 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
5186 void SavingsFilteredHeuristic::ComputeSavings() {
5190 std::vector<int64> uncontained_non_start_end_nodes;
5191 uncontained_non_start_end_nodes.reserve(size);
5192 for (
int node = 0; node < size; node++) {
5194 uncontained_non_start_end_nodes.push_back(node);
5198 const int64 saving_neighbors =
5199 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
5200 static_cast<int64>(uncontained_non_start_end_nodes.size()));
5203 absl::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
5206 std::vector<std::vector<int64>> adjacency_lists(size);
5208 for (
int type = 0; type < num_vehicle_types; ++type) {
5214 const int64 cost_class =
5222 for (
int before_node : uncontained_non_start_end_nodes) {
5223 std::vector<std::pair<
int64,
int64>> costed_after_nodes;
5224 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
5225 for (
int after_node : uncontained_non_start_end_nodes) {
5226 if (after_node != before_node) {
5227 costed_after_nodes.push_back(std::make_pair(
5228 model()->GetArcCostForClass(before_node, after_node, cost_class),
5232 if (saving_neighbors < costed_after_nodes.size()) {
5233 std::nth_element(costed_after_nodes.begin(),
5234 costed_after_nodes.begin() + saving_neighbors,
5235 costed_after_nodes.end());
5236 costed_after_nodes.resize(saving_neighbors);
5238 adjacency_lists[before_node].resize(costed_after_nodes.size());
5239 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
5240 adjacency_lists[before_node].begin(),
5241 [](std::pair<int64, int64> cost_and_node) {
5242 return cost_and_node.second;
5246 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
5250 for (
int before_node : uncontained_non_start_end_nodes) {
5251 const int64 before_to_end_cost =
5253 const int64 start_to_before_cost =
5254 CapSub(
model()->GetArcCostForClass(start, before_node, cost_class),
5256 for (
int64 after_node : adjacency_lists[before_node]) {
5257 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
5258 before_node == after_node ||
Contains(after_node)) {
5261 const int64 arc_cost =
5263 const int64 start_to_after_cost =
5264 CapSub(
model()->GetArcCostForClass(start, after_node, cost_class),
5266 const int64 after_to_end_cost =
5269 const double weighted_arc_cost_fp =
5271 const int64 weighted_arc_cost =
5273 ?
static_cast<int64>(weighted_arc_cost_fp)
5276 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
5279 BuildSaving(-saving_value, type, before_node, after_node);
5281 const int64 total_cost =
5282 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
5292 int64 SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
5293 int num_vehicle_types)
const {
5296 const int64 num_neighbors_with_ratio =
5303 const double max_memory_usage_in_savings_unit =
5321 if (num_vehicle_types > 1) {
5322 multiplicative_factor += 1.5;
5324 const double num_savings =
5325 max_memory_usage_in_savings_unit / multiplicative_factor;
5326 const int64 num_neighbors_with_memory_restriction =
5327 std::max(1.0, num_savings / (num_vehicle_types * size));
5329 return std::min(num_neighbors_with_ratio,
5330 num_neighbors_with_memory_restriction);
5335 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
5341 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
5342 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
5343 for (
int type = 0; type < vehicle_types; type++) {
5344 const int vehicle_type_offset = type * size;
5345 const std::vector<Saving>& sorted_savings_for_type =
5347 for (
const Saving& saving : sorted_savings_for_type) {
5350 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
5352 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
5362 const bool nodes_not_contained =
5365 bool committed =
false;
5367 if (nodes_not_contained) {
5380 const int saving_offset = type * size;
5382 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
5384 out_savings_ptr[saving_offset + before_node].size()) {
5387 int before_before_node = -1;
5388 int after_after_node = -1;
5389 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
5390 const Saving& in_saving =
5391 *(in_savings_ptr[saving_offset + after_node][in_index]);
5393 out_savings_ptr[saving_offset + before_node].size()) {
5394 const Saving& out_saving =
5395 *(out_savings_ptr[saving_offset + before_node][out_index]);
5410 *(out_savings_ptr[saving_offset + before_node][out_index]));
5413 if (after_after_node != -1) {
5417 SetValue(after_node, after_after_node);
5421 after_node = after_after_node;
5431 if (!
Contains(before_before_node)) {
5432 SetValue(start, before_before_node);
5433 SetValue(before_before_node, before_node);
5436 before_node = before_before_node;
5453 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
5459 first_node_on_route_.resize(vehicles, -1);
5460 last_node_on_route_.resize(vehicles, -1);
5461 vehicle_of_first_or_last_node_.resize(size, -1);
5463 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
5471 vehicle_of_first_or_last_node_[node] = vehicle;
5472 first_node_on_route_[vehicle] = node;
5475 while (
next != end) {
5479 vehicle_of_first_or_last_node_[node] = vehicle;
5480 last_node_on_route_[vehicle] = node;
5493 bool committed =
false;
5501 vehicle_of_first_or_last_node_[before_node] = vehicle;
5502 vehicle_of_first_or_last_node_[after_node] = vehicle;
5503 first_node_on_route_[vehicle] = before_node;
5504 last_node_on_route_[vehicle] = after_node;
5516 const int v1 = vehicle_of_first_or_last_node_[before_node];
5517 const int64 last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
5519 const int v2 = vehicle_of_first_or_last_node_[after_node];
5520 const int64 first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
5522 if (before_node == last_node && after_node == first_node && v1 != v2 &&
5531 MergeRoutes(v1, v2, before_node, after_node);
5536 const int vehicle = vehicle_of_first_or_last_node_[before_node];
5537 const int64 last_node = vehicle == -1 ? -1 : last_node_on_route_[vehicle];
5539 if (before_node == last_node) {
5544 if (type != route_type) {
5555 if (first_node_on_route_[vehicle] != before_node) {
5558 vehicle_of_first_or_last_node_[before_node] = -1;
5560 vehicle_of_first_or_last_node_[after_node] = vehicle;
5561 last_node_on_route_[vehicle] = after_node;
5568 const int vehicle = vehicle_of_first_or_last_node_[after_node];
5569 const int64 first_node =
5570 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
5572 if (after_node == first_node) {
5577 if (type != route_type) {
5588 if (last_node_on_route_[vehicle] != after_node) {
5591 vehicle_of_first_or_last_node_[after_node] = -1;
5593 vehicle_of_first_or_last_node_[before_node] = vehicle;
5594 first_node_on_route_[vehicle] = before_node;
5603 void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
5608 const int64 new_first_node = first_node_on_route_[first_vehicle];
5609 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
5611 const int64 new_last_node = last_node_on_route_[second_vehicle];
5612 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
5616 int used_vehicle = first_vehicle;
5617 int unused_vehicle = second_vehicle;
5618 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
5619 model()->GetFixedCostOfVehicle(second_vehicle)) {
5620 used_vehicle = second_vehicle;
5621 unused_vehicle = first_vehicle;
5626 if (used_vehicle == first_vehicle) {
5631 bool committed =
Commit();
5633 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
5634 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
5636 std::swap(used_vehicle, unused_vehicle);
5639 if (used_vehicle == first_vehicle) {
5650 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
5651 model()->GetFixedCostOfVehicle(unused_vehicle));
5654 first_node_on_route_[unused_vehicle] = -1;
5655 last_node_on_route_[unused_vehicle] = -1;
5656 vehicle_of_first_or_last_node_[before_node] = -1;
5657 vehicle_of_first_or_last_node_[after_node] = -1;
5658 first_node_on_route_[used_vehicle] = new_first_node;
5659 last_node_on_route_[used_vehicle] = new_last_node;
5660 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
5661 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
5668 bool use_minimum_matching)
5670 use_minimum_matching_(use_minimum_matching) {}
5680 std::vector<int> indices(1, 0);
5681 for (
int i = 1; i < size; ++i) {
5682 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
5683 indices.push_back(i);
5687 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
5688 std::vector<bool> class_covered(num_cost_classes,
false);
5689 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
5690 const int64 cost_class =
5692 if (!class_covered[cost_class]) {
5693 class_covered[cost_class] =
true;
5696 auto cost = [
this, &indices, start, end, cost_class](
int from,
int to) {
5699 const int from_index = (from == 0) ? start : indices[from];
5700 const int to_index = (to == 0) ? end : indices[to];
5709 using Cost = decltype(
cost);
5711 indices.size(),
cost);
5712 if (use_minimum_matching_) {
5715 MINIMUM_WEIGHT_MATCHING);
5717 path_per_cost_class[cost_class] =
5722 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
5723 const int64 cost_class =
5725 const std::vector<int>& path = path_per_cost_class[cost_class];
5730 const int end =
model()->
End(vehicle);
5731 for (
int i = 1; i < path.size() - 1 && prev != end; ++i) {
5733 int next = indices[path[i]];
5757 int64 ChooseVariable();
5761 const std::function<
int64(
int64)> initializer_;
5763 std::vector<int64> initial_values_;
5771 GuidedSlackFinalizer::GuidedSlackFinalizer(
5772 const RoutingDimension* dimension, RoutingModel*
model,
5776 initializer_(std::move(initializer)),
5777 is_initialized_(dimension->slacks().size(), false),
5778 initial_values_(dimension->slacks().size(),
kint64min),
5779 current_index_(model_->Start(0)),
5781 last_delta_used_(dimension->slacks().size(), 0) {}
5783 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
5785 const int node_idx = ChooseVariable();
5786 CHECK(node_idx == -1 ||
5787 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
5788 if (node_idx != -1) {
5789 if (!is_initialized_[node_idx]) {
5790 initial_values_[node_idx] = initializer_(node_idx);
5791 is_initialized_.SetValue(solver, node_idx,
true);
5794 IntVar*
const slack_variable = dimension_->
SlackVar(node_idx);
5795 return solver->MakeAssignVariableValue(slack_variable,
value);
5801 const IntVar*
const slack_variable = dimension_->
SlackVar(
index);
5803 const int64 max_delta =
5804 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
5810 while (std::abs(
delta) < max_delta &&
5811 !slack_variable->Contains(center +
delta)) {
5819 return center +
delta;
5822 int64 GuidedSlackFinalizer::ChooseVariable() {
5823 int64 int_current_node = current_index_.Value();
5824 int64 int_current_route = current_route_.Value();
5826 while (int_current_route < model_->vehicles()) {
5827 while (!model_->
IsEnd(int_current_node) &&
5829 int_current_node = model_->
NextVar(int_current_node)->
Value();
5831 if (!model_->
IsEnd(int_current_node)) {
5834 int_current_route += 1;
5835 if (int_current_route < model_->vehicles()) {
5836 int_current_node = model_->
Start(int_current_route);
5842 current_index_.SetValue(model_->
solver(), int_current_node);
5843 current_route_.SetValue(model_->
solver(), int_current_route);
5844 if (int_current_route < model_->vehicles()) {
5845 return int_current_node;
5855 return solver_->RevAlloc(
5856 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
5874 state_dependent_class_evaluators_
5875 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
5880 const int64 optimal_next_cumul =
5882 next_cumul_min, next_cumul_max + 1);
5884 DCHECK_LE(next_cumul_min, optimal_next_cumul);
5885 DCHECK_LE(optimal_next_cumul, next_cumul_max);
5892 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
5893 const int64 current_state_dependent_transit =
5896 state_dependent_class_evaluators_
5897 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
5899 .transit->Query(current_cumul);
5900 const int64 optimal_slack = optimal_next_cumul - current_cumul -
5901 current_state_independent_transit -
5902 current_state_dependent_transit;
5905 return optimal_slack;
5911 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
5914 void Start(
const Assignment* assignment)
override;
5919 const std::vector<IntVar*> variables_;
5921 int64 current_step_;
5928 int64 current_direction_;
5933 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
5934 : variables_(std::move(variables)),
5937 current_direction_(0) {}
5939 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
5941 static const int64 sings[] = {1, -1};
5942 for (; 1 <= current_step_; current_step_ /= 2) {
5943 for (; current_direction_ < 2 * variables_.size();) {
5944 const int64 variable_idx = current_direction_ / 2;
5945 IntVar*
const variable = variables_[variable_idx];
5946 const int64 sign_index = current_direction_ % 2;
5947 const int64 sign = sings[sign_index];
5948 const int64 offset = sign * current_step_;
5949 const int64 new_value = center_->
Value(variable) + offset;
5950 ++current_direction_;
5951 if (variable->Contains(new_value)) {
5952 delta->Add(variable);
5953 delta->SetValue(variable, new_value);
5957 current_direction_ = 0;
5962 void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
5963 CHECK(assignment !=
nullptr);
5964 current_step_ = FindMaxDistanceToDomain(assignment);
5965 center_ = assignment;
5968 int64 GreedyDescentLSOperator::FindMaxDistanceToDomain(
5969 const Assignment* assignment) {
5971 for (
const IntVar*
const var : variables_) {
5980 std::vector<IntVar*> variables) {
5981 return std::unique_ptr<LocalSearchOperator>(
5982 new GreedyDescentLSOperator(std::move(variables)));
5987 CHECK(dimension !=
nullptr);
5995 solver_->MakeSolveOnce(guided_finalizer);
5996 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
5997 for (
int64 vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
5998 start_cumuls[vehicle_idx] = dimension->
CumulVar(starts_[vehicle_idx]);
6001 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
6003 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
6005 Assignment*
const first_solution = solver_->MakeAssignment();
6006 first_solution->
Add(start_cumuls);
6007 for (
IntVar*
const cumul : start_cumuls) {
6008 first_solution->
SetValue(cumul, cumul->Min());
6011 solver_->MakeLocalSearchPhase(first_solution,
parameters);