26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
42 "Run stronger checks in debug; these stronger tests might change "
43 "the complexity of the code in particular.");
45 "Shift insertion costs by the penalty of the inserted node(s).");
53 class MaxActiveVehiclesFilter :
public IntVarLocalSearchFilter {
55 explicit MaxActiveVehiclesFilter(
const RoutingModel& routing_model)
56 : IntVarLocalSearchFilter(routing_model.Nexts()),
57 routing_model_(routing_model),
58 is_active_(routing_model.vehicles(), false),
59 active_vehicles_(0) {}
60 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
61 int64 objective_min,
int64 objective_max)
override {
63 const Assignment::IntContainer& container =
delta->IntVarContainer();
64 const int delta_size = container.Size();
65 int current_active_vehicles = active_vehicles_;
66 for (
int i = 0; i < delta_size; ++i) {
67 const IntVarElement& new_element = container.Element(i);
68 IntVar*
const var = new_element.Var();
70 if (FindIndex(
var, &
index) && routing_model_.IsStart(
index)) {
71 if (new_element.Min() != new_element.Max()) {
75 const int vehicle = routing_model_.VehicleIndex(
index);
76 const bool is_active =
77 (new_element.Min() != routing_model_.End(vehicle));
78 if (is_active && !is_active_[vehicle]) {
79 ++current_active_vehicles;
80 }
else if (!is_active && is_active_[vehicle]) {
81 --current_active_vehicles;
85 return current_active_vehicles <=
86 routing_model_.GetMaximumNumberOfActiveVehicles();
90 void OnSynchronize(
const Assignment*
delta)
override {
92 for (
int i = 0; i < routing_model_.vehicles(); ++i) {
93 const int index = routing_model_.Start(i);
98 is_active_[i] =
false;
103 const RoutingModel& routing_model_;
104 std::vector<bool> is_active_;
105 int active_vehicles_;
111 return routing_model.
solver()->RevAlloc(
112 new MaxActiveVehiclesFilter(routing_model));
119 class NodeDisjunctionFilter :
public IntVarLocalSearchFilter {
121 explicit NodeDisjunctionFilter(
const RoutingModel& routing_model)
122 : IntVarLocalSearchFilter(routing_model.Nexts()),
123 routing_model_(routing_model),
124 active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
125 inactive_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
126 synchronized_objective_value_(
kint64min),
129 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
130 int64 objective_min,
int64 objective_max)
override {
132 const Assignment::IntContainer& container =
delta->IntVarContainer();
133 const int delta_size = container.Size();
135 disjunction_active_deltas;
137 disjunction_inactive_deltas;
138 bool lns_detected =
false;
140 for (
int i = 0; i < delta_size; ++i) {
141 const IntVarElement& new_element = container.Element(i);
142 IntVar*
const var = new_element.Var();
145 const bool is_inactive =
146 (new_element.Min() <=
index && new_element.Max() >=
index);
147 if (new_element.Min() != new_element.Max()) {
151 routing_model_.GetDisjunctionIndices(
index)) {
152 const bool active_state_changed =
154 if (active_state_changed) {
157 disjunction_index, 0);
158 if (IsVarSynced(
index)) {
160 disjunction_index, 0);
164 disjunction_index, 0);
165 if (IsVarSynced(
index)) {
167 disjunction_index, 0);
175 for (
const std::pair<RoutingModel::DisjunctionIndex, int>
176 disjunction_active_delta : disjunction_active_deltas) {
177 const int current_active_nodes =
178 active_per_disjunction_[disjunction_active_delta.first];
179 const int active_nodes =
180 current_active_nodes + disjunction_active_delta.second;
181 const int max_cardinality = routing_model_.GetDisjunctionMaxCardinality(
182 disjunction_active_delta.first);
184 if (active_nodes > max_cardinality) {
189 accepted_objective_value_ = synchronized_objective_value_;
190 for (
const std::pair<RoutingModel::DisjunctionIndex, int>
191 disjunction_inactive_delta : disjunction_inactive_deltas) {
192 const int64 penalty = routing_model_.GetDisjunctionPenalty(
193 disjunction_inactive_delta.first);
194 if (penalty != 0 && !lns_detected) {
196 disjunction_inactive_delta.first;
197 const int current_inactive_nodes =
198 inactive_per_disjunction_[disjunction_index];
199 const int inactive_nodes =
200 current_inactive_nodes + disjunction_inactive_delta.second;
201 const int max_inactive_cardinality =
202 routing_model_.GetDisjunctionIndices(disjunction_index).size() -
203 routing_model_.GetDisjunctionMaxCardinality(disjunction_index);
205 if (inactive_nodes > max_inactive_cardinality) {
210 }
else if (current_inactive_nodes <= max_inactive_cardinality) {
213 accepted_objective_value_ =
214 CapAdd(accepted_objective_value_, penalty);
216 }
else if (current_inactive_nodes > max_inactive_cardinality) {
219 accepted_objective_value_ =
220 CapSub(accepted_objective_value_, penalty);
225 accepted_objective_value_ = 0;
229 return accepted_objective_value_ <= objective_max;
232 std::string DebugString()
const override {
return "NodeDisjunctionFilter"; }
233 int64 GetSynchronizedObjectiveValue()
const override {
234 return synchronized_objective_value_;
236 int64 GetAcceptedObjectiveValue()
const override {
237 return accepted_objective_value_;
241 void OnSynchronize(
const Assignment*
delta)
override {
242 synchronized_objective_value_ = 0;
244 i < active_per_disjunction_.size(); ++i) {
245 active_per_disjunction_[i] = 0;
246 inactive_per_disjunction_[i] = 0;
247 const std::vector<int64>& disjunction_indices =
248 routing_model_.GetDisjunctionIndices(i);
249 for (
const int64 index : disjunction_indices) {
250 const bool index_synced = IsVarSynced(
index);
253 ++active_per_disjunction_[i];
255 ++inactive_per_disjunction_[i];
259 const int64 penalty = routing_model_.GetDisjunctionPenalty(i);
260 const int max_cardinality =
261 routing_model_.GetDisjunctionMaxCardinality(i);
262 if (inactive_per_disjunction_[i] >
263 disjunction_indices.size() - max_cardinality &&
265 synchronized_objective_value_ =
266 CapAdd(synchronized_objective_value_, penalty);
271 const RoutingModel& routing_model_;
275 int64 synchronized_objective_value_;
276 int64 accepted_objective_value_;
282 return routing_model.
solver()->RevAlloc(
283 new NodeDisjunctionFilter(routing_model));
289 int next_domain_size)
292 paths_(nexts.size(), -1),
293 new_synchronized_unperformed_nodes_(nexts.size()),
295 touched_paths_(nexts.size()),
296 touched_path_nodes_(next_domain_size),
297 ranks_(next_domain_size, -1),
301 const Assignment* deltadelta,
int64 objective_min,
302 int64 objective_max) {
304 for (
const int touched : delta_touched_) {
307 delta_touched_.clear();
308 const Assignment::IntContainer& container =
delta->IntVarContainer();
309 const int delta_size = container.Size();
310 delta_touched_.reserve(delta_size);
316 for (
int i = 0; i < delta_size; ++i) {
317 const IntVarElement& new_element = container.Element(i);
318 IntVar*
const var = new_element.Var();
321 if (!new_element.Bound()) {
325 new_nexts_[
index] = new_element.Value();
326 delta_touched_.push_back(
index);
329 touched_path_nodes_.
Set(new_nexts_[
index]);
331 touched_paths_.
Set(start);
336 InitializeAcceptPath();
350 for (
const int64 touched_path_node :
352 if (node_path_starts_[touched_path_node] == touched_start) {
353 const int rank = ranks_[touched_path_node];
356 start = touched_path_node;
360 end = touched_path_node;
364 if (!AcceptPath(touched_start, start, end)) {
370 return accept && FinalizeAcceptPath(
delta, objective_min, objective_max);
373 void BasePathFilter::ComputePathStarts(std::vector<int64>* path_starts,
374 std::vector<int>* index_to_path) {
375 path_starts->clear();
376 const int nexts_size =
Size();
379 for (
int i = 0; i < nexts_size; ++i) {
384 if (
next < nexts_size) {
389 for (
int i = 0; i < nexts_size; ++i) {
391 (*index_to_path)[i] = path_starts->size();
392 path_starts->push_back(i);
397 bool BasePathFilter::HavePathsChanged() {
398 std::vector<int64> path_starts;
400 ComputePathStarts(&path_starts, &index_to_path);
401 if (path_starts.size() != starts_.size()) {
404 for (
int i = 0; i < path_starts.size(); ++i) {
405 if (path_starts[i] != starts_[i]) {
409 for (
int i = 0; i <
Size(); ++i) {
410 if (index_to_path[i] != paths_[i]) {
417 void BasePathFilter::SynchronizeFullAssignment() {
421 ComputePathStarts(&starts_, &paths_);
426 new_synchronized_unperformed_nodes_.
Set(
index);
430 node_path_starts_.assign(node_path_starts_.size(),
kUnassigned);
432 const int nexts_size =
Size();
433 for (
const int64 start : starts_) {
435 node_path_starts_[node] = start;
438 while (
next < nexts_size) {
440 node_path_starts_[node] = start;
444 node_path_starts_[
next] = start;
446 OnBeforeSynchronizePaths();
448 OnAfterSynchronizePaths();
452 if (status_ == BasePathFilter::UNKNOWN) {
454 DisableFiltering() ? BasePathFilter::DISABLED : BasePathFilter::ENABLED;
457 new_synchronized_unperformed_nodes_.
ClearAll();
458 if (
delta ==
nullptr ||
delta->Empty() || starts_.empty()) {
459 SynchronizeFullAssignment();
466 DCHECK(!FLAGS_routing_strong_debug_checks || !HavePathsChanged());
467 const Assignment::IntContainer& container =
delta->IntVarContainer();
469 for (
int i = 0; i < container.Size(); ++i) {
470 const IntVarElement& new_element = container.Element(i);
475 touched_paths_.
Set(start);
478 DCHECK_LT(
index, new_nexts_.size());
479 new_synchronized_unperformed_nodes_.
Set(
index);
485 OnBeforeSynchronizePaths();
487 int64 node = touched_start;
488 while (node <
Size()) {
489 node_path_starts_[node] = touched_start;
492 node_path_starts_[node] = touched_start;
493 UpdatePathRanksFromStart(touched_start);
494 OnSynchronizePathFromStart(touched_start);
496 OnAfterSynchronizePaths();
499 void BasePathFilter::UpdateAllRanks() {
500 for (
int i = 0; i < ranks_.size(); ++i) {
503 for (
int r = 0; r <
NumPaths(); ++r) {
504 UpdatePathRanksFromStart(
Start(r));
505 OnSynchronizePathFromStart(
Start(r));
509 void BasePathFilter::UpdatePathRanksFromStart(
int start) {
512 while (node <
Size()) {
522 class VehicleAmortizedCostFilter :
public BasePathFilter {
524 explicit VehicleAmortizedCostFilter(
const RoutingModel& routing_model);
525 ~VehicleAmortizedCostFilter()
override {}
526 std::string DebugString()
const override {
527 return "VehicleAmortizedCostFilter";
529 int64 GetSynchronizedObjectiveValue()
const override {
530 return current_vehicle_cost_;
532 int64 GetAcceptedObjectiveValue()
const override {
533 return delta_vehicle_cost_;
537 void OnSynchronizePathFromStart(
int64 start)
override;
538 void OnAfterSynchronizePaths()
override;
539 void InitializeAcceptPath()
override;
540 bool AcceptPath(
int64 path_start,
int64 chain_start,
541 int64 chain_end)
override;
542 bool FinalizeAcceptPath(
const Assignment*
delta,
int64 objective_min,
543 int64 objective_max)
override;
545 int64 current_vehicle_cost_;
546 int64 delta_vehicle_cost_;
547 std::vector<int> current_route_lengths_;
548 std::vector<int64> start_to_end_;
549 std::vector<int> start_to_vehicle_;
550 std::vector<int64> vehicle_to_start_;
551 const std::vector<int64>& linear_cost_factor_of_vehicle_;
552 const std::vector<int64>& quadratic_cost_factor_of_vehicle_;
555 VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
556 const RoutingModel& routing_model)
557 : BasePathFilter(routing_model.Nexts(),
558 routing_model.Size() + routing_model.vehicles()),
559 current_vehicle_cost_(0),
560 delta_vehicle_cost_(0),
561 current_route_lengths_(Size(), -1),
562 linear_cost_factor_of_vehicle_(
563 routing_model.GetAmortizedLinearCostFactorOfVehicles()),
564 quadratic_cost_factor_of_vehicle_(
565 routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) {
566 start_to_end_.resize(
Size(), -1);
567 start_to_vehicle_.resize(
Size(), -1);
568 vehicle_to_start_.resize(routing_model.vehicles());
569 for (
int v = 0; v < routing_model.vehicles(); v++) {
570 const int64 start = routing_model.Start(v);
571 start_to_vehicle_[start] = v;
572 start_to_end_[start] = routing_model.End(v);
573 vehicle_to_start_[v] = start;
577 void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(
int64 start) {
578 const int64 end = start_to_end_[start];
580 const int route_length = Rank(end) - 1;
581 CHECK_GE(route_length, 0);
582 current_route_lengths_[start] = route_length;
585 void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
586 current_vehicle_cost_ = 0;
587 for (
int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
588 const int64 start = vehicle_to_start_[vehicle];
589 DCHECK_EQ(vehicle, start_to_vehicle_[start]);
591 const int route_length = current_route_lengths_[start];
592 DCHECK_GE(route_length, 0);
594 if (route_length == 0) {
599 const int64 linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle];
600 const int64 route_length_cost =
601 CapProd(quadratic_cost_factor_of_vehicle_[vehicle],
602 route_length * route_length);
604 current_vehicle_cost_ =
CapAdd(
605 current_vehicle_cost_,
CapSub(linear_cost_factor, route_length_cost));
609 void VehicleAmortizedCostFilter::InitializeAcceptPath() {
610 delta_vehicle_cost_ = current_vehicle_cost_;
613 bool VehicleAmortizedCostFilter::AcceptPath(
int64 path_start,
int64 chain_start,
616 const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start);
617 CHECK_GE(previous_chain_nodes, 0);
618 int new_chain_nodes = 0;
619 int64 node = GetNext(chain_start);
620 while (node != chain_end) {
622 node = GetNext(node);
625 const int previous_route_length = current_route_lengths_[path_start];
626 CHECK_GE(previous_route_length, 0);
627 const int new_route_length =
628 previous_route_length - previous_chain_nodes + new_chain_nodes;
630 const int vehicle = start_to_vehicle_[path_start];
631 CHECK_GE(vehicle, 0);
632 DCHECK_EQ(path_start, vehicle_to_start_[vehicle]);
636 if (previous_route_length == 0) {
638 CHECK_GT(new_route_length, 0);
639 delta_vehicle_cost_ =
640 CapAdd(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
641 }
else if (new_route_length == 0) {
643 delta_vehicle_cost_ =
644 CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
648 const int64 quadratic_cost_factor =
649 quadratic_cost_factor_of_vehicle_[vehicle];
650 delta_vehicle_cost_ =
651 CapAdd(delta_vehicle_cost_,
653 previous_route_length * previous_route_length));
654 delta_vehicle_cost_ =
CapSub(
656 CapProd(quadratic_cost_factor, new_route_length * new_route_length));
661 bool VehicleAmortizedCostFilter::FinalizeAcceptPath(
const Assignment*
delta,
663 int64 objective_max) {
664 return delta_vehicle_cost_ <= objective_max;
671 return routing_model.
solver()->RevAlloc(
672 new VehicleAmortizedCostFilter(routing_model));
677 class TypeRegulationsFilter :
public BasePathFilter {
679 explicit TypeRegulationsFilter(
const RoutingModel&
model);
680 ~TypeRegulationsFilter()
override {}
681 std::string DebugString()
const override {
return "TypeRegulationsFilter"; }
684 void OnSynchronizePathFromStart(
int64 start)
override;
685 bool AcceptPath(
int64 path_start,
int64 chain_start,
686 int64 chain_end)
override;
688 bool HardIncompatibilitiesRespected(
int vehicle,
int64 chain_start,
691 const RoutingModel& routing_model_;
692 std::vector<int> start_to_vehicle_;
695 std::vector<std::vector<int>> hard_incompatibility_type_counts_per_vehicle_;
697 TypeIncompatibilityChecker temporal_incompatibility_checker_;
698 TypeRequirementChecker requirement_checker_;
701 TypeRegulationsFilter::TypeRegulationsFilter(
const RoutingModel&
model)
703 routing_model_(
model),
704 start_to_vehicle_(
model.Size(), -1),
705 temporal_incompatibility_checker_(
model,
707 requirement_checker_(
model) {
708 const int num_vehicles =
model.vehicles();
709 const bool has_hard_type_incompatibilities =
710 model.HasHardTypeIncompatibilities();
711 if (has_hard_type_incompatibilities) {
712 hard_incompatibility_type_counts_per_vehicle_.resize(num_vehicles);
714 const int num_visit_types =
model.GetNumberOfVisitTypes();
715 for (
int vehicle = 0; vehicle < num_vehicles; vehicle++) {
717 start_to_vehicle_[start] = vehicle;
718 if (has_hard_type_incompatibilities) {
719 hard_incompatibility_type_counts_per_vehicle_[vehicle].resize(
725 void TypeRegulationsFilter::OnSynchronizePathFromStart(
int64 start) {
726 if (!routing_model_.HasHardTypeIncompatibilities())
return;
728 const int vehicle = start_to_vehicle_[start];
729 CHECK_GE(vehicle, 0);
730 std::vector<int>& type_counts =
731 hard_incompatibility_type_counts_per_vehicle_[vehicle];
732 std::fill(type_counts.begin(), type_counts.end(), 0);
733 const int num_types = type_counts.size();
736 while (node < Size()) {
737 DCHECK(IsVarSynced(node));
738 const int type = routing_model_.GetVisitType(node);
739 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
740 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
741 CHECK_LT(type, num_types);
748 bool TypeRegulationsFilter::HardIncompatibilitiesRespected(
int vehicle,
751 if (!routing_model_.HasHardTypeIncompatibilities())
return true;
753 const std::vector<int>& previous_type_counts =
754 hard_incompatibility_type_counts_per_vehicle_[vehicle];
756 absl::flat_hash_map< int,
int> new_type_counts;
757 absl::flat_hash_set<int> types_to_check;
760 int64 node = GetNext(chain_start);
761 while (node != chain_end) {
762 const int type = routing_model_.GetVisitType(node);
763 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
764 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
765 DCHECK_LT(type, previous_type_counts.size());
767 previous_type_counts[type]);
768 if (type_count++ == 0) {
770 types_to_check.insert(type);
773 node = GetNext(node);
778 node =
Value(chain_start);
779 while (node != chain_end) {
780 const int type = routing_model_.GetVisitType(node);
781 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
782 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
783 DCHECK_LT(type, previous_type_counts.size());
785 previous_type_counts[type]);
786 CHECK_GE(type_count, 1);
793 for (
int type : types_to_check) {
794 for (
int incompatible_type :
795 routing_model_.GetHardTypeIncompatibilitiesOfType(type)) {
797 previous_type_counts[incompatible_type]) > 0) {
805 bool TypeRegulationsFilter::AcceptPath(
int64 path_start,
int64 chain_start,
807 const int vehicle = start_to_vehicle_[path_start];
808 CHECK_GE(vehicle, 0);
809 const auto next_accessor = [
this](
int64 node) {
return GetNext(node); };
810 return HardIncompatibilitiesRespected(vehicle, chain_start, chain_end) &&
811 temporal_incompatibility_checker_.CheckVehicle(vehicle,
813 requirement_checker_.CheckVehicle(vehicle, next_accessor);
820 return routing_model.
solver()->RevAlloc(
821 new TypeRegulationsFilter(routing_model));
831 class ChainCumulFilter :
public BasePathFilter {
833 ChainCumulFilter(
const RoutingModel& routing_model,
834 const RoutingDimension& dimension);
835 ~ChainCumulFilter()
override {}
836 std::string DebugString()
const override {
837 return "ChainCumulFilter(" + name_ +
")";
841 void OnSynchronizePathFromStart(
int64 start)
override;
842 bool AcceptPath(
int64 path_start,
int64 chain_start,
843 int64 chain_end)
override;
845 const std::vector<IntVar*>
cumuls_;
846 std::vector<int64> start_to_vehicle_;
847 std::vector<int64> start_to_end_;
848 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
849 const std::vector<int64> vehicle_capacities_;
850 std::vector<int64> current_path_cumul_mins_;
851 std::vector<int64> current_max_of_path_end_cumul_mins_;
852 std::vector<int64> old_nexts_;
853 std::vector<int> old_vehicles_;
854 std::vector<int64> current_transits_;
855 const std::string name_;
858 ChainCumulFilter::ChainCumulFilter(
const RoutingModel& routing_model,
859 const RoutingDimension& dimension)
860 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
862 evaluators_(routing_model.vehicles(), nullptr),
863 vehicle_capacities_(dimension.vehicle_capacities()),
864 current_path_cumul_mins_(dimension.cumuls().size(), 0),
865 current_max_of_path_end_cumul_mins_(dimension.cumuls().size(), 0),
868 current_transits_(routing_model.Size(), 0),
869 name_(dimension.
name()) {
870 start_to_vehicle_.resize(Size(), -1);
871 start_to_end_.resize(Size(), -1);
872 for (
int i = 0; i < routing_model.vehicles(); ++i) {
873 start_to_vehicle_[routing_model.Start(i)] = i;
874 start_to_end_[routing_model.Start(i)] = routing_model.End(i);
875 evaluators_[i] = &dimension.transit_evaluator(i);
882 void ChainCumulFilter::OnSynchronizePathFromStart(
int64 start) {
883 const int vehicle = start_to_vehicle_[start];
884 std::vector<int64> path_nodes;
887 while (node < Size()) {
888 path_nodes.push_back(node);
889 current_path_cumul_mins_[node] = cumul;
891 if (
next != old_nexts_[node] || vehicle != old_vehicles_[node]) {
892 old_nexts_[node] =
next;
893 old_vehicles_[node] = vehicle;
894 current_transits_[node] = (*evaluators_[vehicle])(node,
next);
896 cumul =
CapAdd(cumul, current_transits_[node]);
900 path_nodes.push_back(node);
901 current_path_cumul_mins_[node] = cumul;
902 int64 max_cumuls = cumul;
903 for (
int i = path_nodes.size() - 1; i >= 0; --i) {
904 const int64 node = path_nodes[i];
905 max_cumuls =
std::max(max_cumuls, current_path_cumul_mins_[node]);
906 current_max_of_path_end_cumul_mins_[node] = max_cumuls;
911 bool ChainCumulFilter::AcceptPath(
int64 path_start,
int64 chain_start,
913 const int vehicle = start_to_vehicle_[path_start];
915 int64 node = chain_start;
916 int64 cumul = current_path_cumul_mins_[node];
917 while (node != chain_end) {
919 if (IsVarSynced(node) &&
next ==
Value(node) &&
920 vehicle == old_vehicles_[node]) {
921 cumul =
CapAdd(cumul, current_transits_[node]);
923 cumul =
CapAdd(cumul, (*evaluators_[vehicle])(node,
next));
929 const int64 end = start_to_end_[path_start];
930 const int64 end_cumul_delta =
931 CapSub(current_path_cumul_mins_[end], current_path_cumul_mins_[node]);
932 const int64 after_chain_cumul_delta =
933 CapSub(current_max_of_path_end_cumul_mins_[node],
934 current_path_cumul_mins_[node]);
941 class PathCumulFilter :
public BasePathFilter {
943 PathCumulFilter(
const RoutingModel& routing_model,
944 const RoutingDimension& dimension,
946 bool propagate_own_objective_value,
947 bool filter_objective_cost,
bool can_use_lp);
948 ~PathCumulFilter()
override {}
949 std::string DebugString()
const override {
950 return "PathCumulFilter(" + name_ +
")";
952 int64 GetSynchronizedObjectiveValue()
const override {
953 return propagate_own_objective_value_ ? synchronized_objective_value_ : 0;
955 int64 GetAcceptedObjectiveValue()
const override {
956 return propagate_own_objective_value_ ? accepted_objective_value_ : 0;
963 struct SupportedPathCumul {
984 void ClearPath(
int path) {
985 paths_[path].clear();
986 transits_[path].clear();
988 int AddPaths(
int num_paths) {
989 const int first_path = paths_.size();
990 paths_.resize(first_path + num_paths);
991 transits_.resize(first_path + num_paths);
994 void ReserveTransits(
int path,
int number_of_route_arcs) {
995 transits_[path].reserve(number_of_route_arcs);
996 paths_[path].reserve(number_of_route_arcs + 1);
1000 void PushTransit(
int path,
int node,
int next,
int64 transit) {
1001 transits_[path].push_back(transit);
1002 if (paths_[path].empty()) {
1003 paths_[path].push_back(node);
1005 DCHECK_EQ(paths_[path].back(), node);
1006 paths_[path].push_back(
next);
1008 int NumPaths()
const {
return paths_.size(); }
1009 int PathSize(
int path)
const {
return paths_[path].size(); }
1010 int Node(
int path,
int position)
const {
return paths_[path][position]; }
1011 int64 Transit(
int path,
int position)
const {
1012 return transits_[path][position];
1017 std::vector<std::vector<int64>> paths_;
1020 std::vector<std::vector<int64>> transits_;
1023 void InitializeAcceptPath()
override {
1024 cumul_cost_delta_ = total_current_cumul_cost_value_;
1025 node_with_precedence_to_delta_min_max_cumuls_.clear();
1028 delta_paths_.clear();
1029 delta_path_transits_.Clear();
1030 lns_detected_ =
false;
1031 delta_nodes_with_precedences_and_changed_cumul_.
ClearAll();
1033 bool AcceptPath(
int64 path_start,
int64 chain_start,
1034 int64 chain_end)
override;
1035 bool FinalizeAcceptPath(
const Assignment*
delta,
int64 objective_min,
1036 int64 objective_max)
override;
1037 void OnBeforeSynchronizePaths()
override;
1039 bool FilterSpanCost()
const {
return global_span_cost_coefficient_ != 0; }
1041 bool FilterSlackCost()
const {
1042 return has_nonzero_vehicle_span_cost_coefficients_ ||
1043 has_vehicle_span_upper_bounds_;
1046 bool FilterBreakCost(
int vehicle)
const {
1047 return dimension_.HasBreakConstraints() &&
1048 !dimension_.GetBreakIntervalsOfVehicle(vehicle).empty();
1051 bool FilterCumulSoftBounds()
const {
return !cumul_soft_bounds_.empty(); }
1055 bool FilterCumulPiecewiseLinearCosts()
const {
1056 return !cumul_piecewise_linear_costs_.empty();
1059 bool FilterWithDimensionCumulOptimizerForVehicle(
int vehicle)
const {
1060 if (!can_use_lp_ || FilterCumulPiecewiseLinearCosts()) {
1064 int num_linear_constraints = 0;
1065 if (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0)
1066 ++num_linear_constraints;
1067 if (FilterSoftSpanCost(vehicle)) ++num_linear_constraints;
1068 if (FilterCumulSoftLowerBounds()) ++num_linear_constraints;
1069 if (FilterCumulSoftBounds()) ++num_linear_constraints;
1070 if (vehicle_span_upper_bounds_[vehicle] <
kint64max)
1071 ++num_linear_constraints;
1072 const bool has_breaks = FilterBreakCost(vehicle);
1073 if (has_breaks) ++num_linear_constraints;
1083 return num_linear_constraints >= 2 &&
1084 (has_breaks || filter_objective_cost_);
1087 bool FilterDimensionForbiddenIntervals()
const {
1088 for (
const SortedDisjointIntervalList& intervals :
1089 dimension_.forbidden_intervals()) {
1092 if (intervals.NumIntervals() > 0) {
1101 bool FilterCumulSoftLowerBounds()
const {
1102 return !cumul_soft_lower_bounds_.empty();
1105 bool FilterPrecedences()
const {
return !node_index_to_precedences_.empty(); }
1107 bool FilterSoftSpanCost()
const {
1108 return dimension_.HasSoftSpanUpperBounds();
1110 bool FilterSoftSpanCost(
int vehicle)
const {
1111 return dimension_.HasSoftSpanUpperBounds() &&
1112 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle).cost > 0;
1114 bool FilterSoftSpanQuadraticCost()
const {
1115 return dimension_.HasQuadraticCostSoftSpanUpperBounds();
1117 bool FilterSoftSpanQuadraticCost(
int vehicle)
const {
1118 return dimension_.HasQuadraticCostSoftSpanUpperBounds() &&
1119 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle)
1125 int64 GetPathCumulSoftLowerBoundCost(
const PathTransits& path_transits,
1128 void InitializeSupportedPathCumul(SupportedPathCumul* supported_cumul,
1129 int64 default_value);
1139 bool PickupToDeliveryLimitsRespected(
1140 const PathTransits& path_transits,
int path,
1141 const std::vector<int64>& min_path_cumuls)
const;
1151 void StoreMinMaxCumulOfNodesOnPath(
int path,
1152 const std::vector<int64>& min_path_cumuls,
1162 int64 ComputePathMaxStartFromEndCumul(
const PathTransits& path_transits,
1163 int path,
int64 path_start,
1164 int64 min_end_cumul)
const;
1166 const RoutingModel& routing_model_;
1167 const RoutingDimension& dimension_;
1168 const std::vector<IntVar*>
cumuls_;
1169 const std::vector<IntVar*> slacks_;
1170 std::vector<int64> start_to_vehicle_;
1171 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1172 std::vector<int64> vehicle_span_upper_bounds_;
1173 bool has_vehicle_span_upper_bounds_;
1174 int64 total_current_cumul_cost_value_;
1175 int64 synchronized_objective_value_;
1176 int64 accepted_objective_value_;
1179 absl::flat_hash_map<int64, int64> current_cumul_cost_values_;
1180 int64 cumul_cost_delta_;
1182 std::vector<int64> delta_path_cumul_cost_values_;
1183 const int64 global_span_cost_coefficient_;
1184 std::vector<SoftBound> cumul_soft_bounds_;
1185 std::vector<SoftBound> cumul_soft_lower_bounds_;
1186 std::vector<const PiecewiseLinearFunction*> cumul_piecewise_linear_costs_;
1187 std::vector<int64> vehicle_span_cost_coefficients_;
1188 bool has_nonzero_vehicle_span_cost_coefficients_;
1189 const std::vector<int64> vehicle_capacities_;
1193 std::vector<std::vector<RoutingDimension::NodePrecedence>>
1194 node_index_to_precedences_;
1197 SupportedPathCumul current_min_start_;
1198 SupportedPathCumul current_max_end_;
1199 PathTransits current_path_transits_;
1201 std::vector<std::pair<int64, int64>> current_min_max_node_cumuls_;
1204 PathTransits delta_path_transits_;
1205 int64 delta_max_end_cumul_;
1206 SparseBitset<int64> delta_nodes_with_precedences_and_changed_cumul_;
1207 absl::flat_hash_map<int64, std::pair<int64, int64>>
1208 node_with_precedence_to_delta_min_max_cumuls_;
1211 const std::string name_;
1213 LocalDimensionCumulOptimizer* optimizer_;
1214 std::unique_ptr<LocalDimensionCumulOptimizer> internal_optimizer_;
1215 LocalDimensionCumulOptimizer* mp_optimizer_;
1216 std::unique_ptr<LocalDimensionCumulOptimizer> internal_mp_optimizer_;
1217 const bool filter_objective_cost_;
1220 const bool can_use_lp_;
1221 const bool propagate_own_objective_value_;
1224 DisjunctivePropagator disjunctive_propagator_;
1225 DisjunctivePropagator::Tasks tasks_;
1226 TravelBounds travel_bounds_;
1227 std::vector<int64> current_path_;
1232 PathCumulFilter::PathCumulFilter(
const RoutingModel& routing_model,
1233 const RoutingDimension& dimension,
1235 bool propagate_own_objective_value,
1236 bool filter_objective_cost,
bool can_use_lp)
1237 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
1238 routing_model_(routing_model),
1239 dimension_(dimension),
1241 slacks_(dimension.slacks()),
1242 evaluators_(routing_model.vehicles(), nullptr),
1243 vehicle_span_upper_bounds_(dimension.vehicle_span_upper_bounds()),
1244 has_vehicle_span_upper_bounds_(false),
1245 total_current_cumul_cost_value_(0),
1246 synchronized_objective_value_(0),
1247 accepted_objective_value_(0),
1248 current_cumul_cost_values_(),
1249 cumul_cost_delta_(0),
1250 delta_path_cumul_cost_values_(routing_model.vehicles(),
kint64min),
1251 global_span_cost_coefficient_(dimension.global_span_cost_coefficient()),
1252 vehicle_span_cost_coefficients_(
1253 dimension.vehicle_span_cost_coefficients()),
1254 has_nonzero_vehicle_span_cost_coefficients_(false),
1255 vehicle_capacities_(dimension.vehicle_capacities()),
1256 delta_max_end_cumul_(0),
1257 delta_nodes_with_precedences_and_changed_cumul_(routing_model.Size()),
1258 name_(dimension.
name()),
1259 optimizer_(routing_model.GetMutableLocalCumulOptimizer(dimension)),
1260 mp_optimizer_(routing_model.GetMutableLocalCumulMPOptimizer(dimension)),
1261 filter_objective_cost_(filter_objective_cost),
1262 can_use_lp_(can_use_lp),
1263 propagate_own_objective_value_(propagate_own_objective_value),
1264 lns_detected_(false) {
1265 for (
const int64 upper_bound : vehicle_span_upper_bounds_) {
1267 has_vehicle_span_upper_bounds_ =
true;
1273 has_nonzero_vehicle_span_cost_coefficients_ =
true;
1277 cumul_soft_bounds_.resize(
cumuls_.size());
1278 cumul_soft_lower_bounds_.resize(
cumuls_.size());
1279 cumul_piecewise_linear_costs_.resize(
cumuls_.size());
1280 bool has_cumul_soft_bounds =
false;
1281 bool has_cumul_soft_lower_bounds =
false;
1282 bool has_cumul_piecewise_linear_costs =
false;
1283 bool has_cumul_hard_bounds =
false;
1284 for (
const IntVar*
const slack : slacks_) {
1285 if (slack->Min() > 0) {
1286 has_cumul_hard_bounds =
true;
1290 for (
int i = 0; i <
cumuls_.size(); ++i) {
1291 if (dimension.HasCumulVarSoftUpperBound(i)) {
1292 has_cumul_soft_bounds =
true;
1293 cumul_soft_bounds_[i].bound = dimension.GetCumulVarSoftUpperBound(i);
1294 cumul_soft_bounds_[i].coefficient =
1295 dimension.GetCumulVarSoftUpperBoundCoefficient(i);
1297 if (dimension.HasCumulVarSoftLowerBound(i)) {
1298 has_cumul_soft_lower_bounds =
true;
1299 cumul_soft_lower_bounds_[i].bound =
1300 dimension.GetCumulVarSoftLowerBound(i);
1301 cumul_soft_lower_bounds_[i].coefficient =
1302 dimension.GetCumulVarSoftLowerBoundCoefficient(i);
1304 if (dimension.HasCumulVarPiecewiseLinearCost(i)) {
1305 has_cumul_piecewise_linear_costs =
true;
1306 cumul_piecewise_linear_costs_[i] =
1307 dimension.GetCumulVarPiecewiseLinearCost(i);
1309 IntVar*
const cumul_var =
cumuls_[i];
1310 if (cumul_var->Min() > 0 || cumul_var->Max() <
kint64max) {
1311 has_cumul_hard_bounds =
true;
1314 if (!has_cumul_soft_bounds) {
1315 cumul_soft_bounds_.clear();
1317 if (!has_cumul_soft_lower_bounds) {
1318 cumul_soft_lower_bounds_.clear();
1320 if (!has_cumul_piecewise_linear_costs) {
1321 cumul_piecewise_linear_costs_.clear();
1323 if (!has_cumul_hard_bounds) {
1328 vehicle_span_cost_coefficients_.assign(routing_model.vehicles(), 0);
1329 has_nonzero_vehicle_span_cost_coefficients_ =
false;
1331 start_to_vehicle_.resize(Size(), -1);
1332 for (
int i = 0; i < routing_model.vehicles(); ++i) {
1333 start_to_vehicle_[routing_model.Start(i)] = i;
1334 evaluators_[i] = &dimension.transit_evaluator(i);
1337 const std::vector<RoutingDimension::NodePrecedence>& node_precedences =
1338 dimension.GetNodePrecedences();
1339 if (!node_precedences.empty()) {
1340 current_min_max_node_cumuls_.resize(
cumuls_.size(), {-1, -1});
1341 node_index_to_precedences_.resize(
cumuls_.size());
1342 for (
const auto& node_precedence : node_precedences) {
1343 node_index_to_precedences_[node_precedence.first_node].push_back(
1345 node_index_to_precedences_[node_precedence.second_node].push_back(
1352 if (can_use_lp_ && optimizer_ ==
nullptr) {
1354 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
1355 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1358 if (optimizer_ ==
nullptr) {
1362 internal_optimizer_ = absl::make_unique<LocalDimensionCumulOptimizer>(
1363 &dimension,
parameters.continuous_scheduling_solver());
1364 optimizer_ = internal_optimizer_.get();
1366 if (FilterBreakCost(vehicle) || FilterDimensionForbiddenIntervals()) {
1367 internal_mp_optimizer_ =
1368 absl::make_unique<LocalDimensionCumulOptimizer>(
1369 &dimension,
parameters.mixed_integer_scheduling_solver());
1370 mp_optimizer_ = internal_mp_optimizer_.get();
1378 if (node < cumul_soft_bounds_.size()) {
1379 const int64 bound = cumul_soft_bounds_[node].bound;
1388 int64 PathCumulFilter::GetCumulPiecewiseLinearCost(
int64 node,
1390 if (node < cumul_piecewise_linear_costs_.size()) {
1391 const PiecewiseLinearFunction*
cost = cumul_piecewise_linear_costs_[node];
1392 if (
cost !=
nullptr) {
1399 int64 PathCumulFilter::GetCumulSoftLowerBoundCost(
int64 node,
1401 if (node < cumul_soft_lower_bounds_.size()) {
1402 const int64 bound = cumul_soft_lower_bounds_[node].bound;
1411 int64 PathCumulFilter::GetPathCumulSoftLowerBoundCost(
1412 const PathTransits& path_transits,
int path)
const {
1413 int64 node = path_transits.Node(path, path_transits.PathSize(path) - 1);
1415 int64 current_cumul_cost_value = GetCumulSoftLowerBoundCost(node, cumul);
1416 for (
int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
1417 node = path_transits.Node(path, i);
1418 cumul =
CapSub(cumul, path_transits.Transit(path, i));
1420 current_cumul_cost_value =
CapAdd(current_cumul_cost_value,
1421 GetCumulSoftLowerBoundCost(node, cumul));
1423 return current_cumul_cost_value;
1426 void PathCumulFilter::OnBeforeSynchronizePaths() {
1427 total_current_cumul_cost_value_ = 0;
1428 cumul_cost_delta_ = 0;
1429 current_cumul_cost_values_.clear();
1430 if (NumPaths() > 0 &&
1431 (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1432 FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1433 FilterPrecedences() || FilterSoftSpanCost() ||
1434 FilterSoftSpanQuadraticCost())) {
1435 InitializeSupportedPathCumul(¤t_min_start_,
kint64max);
1436 InitializeSupportedPathCumul(¤t_max_end_,
kint64min);
1437 current_path_transits_.Clear();
1438 current_path_transits_.AddPaths(NumPaths());
1440 for (
int r = 0; r < NumPaths(); ++r) {
1441 int64 node = Start(r);
1442 const int vehicle = start_to_vehicle_[Start(r)];
1445 int number_of_route_arcs = 0;
1446 while (node < Size()) {
1447 ++number_of_route_arcs;
1450 current_path_transits_.ReserveTransits(r, number_of_route_arcs);
1454 std::vector<int64> min_path_cumuls;
1455 min_path_cumuls.reserve(number_of_route_arcs + 1);
1456 min_path_cumuls.push_back(cumul);
1458 int64 current_cumul_cost_value = GetCumulSoftCost(node, cumul);
1459 current_cumul_cost_value =
CapAdd(
1460 current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1462 int64 total_transit = 0;
1463 while (node < Size()) {
1465 const int64 transit = (*evaluators_[vehicle])(node,
next);
1466 total_transit =
CapAdd(total_transit, transit);
1467 const int64 transit_slack =
CapAdd(transit, slacks_[node]->Min());
1468 current_path_transits_.PushTransit(r, node,
next, transit_slack);
1469 cumul =
CapAdd(cumul, transit_slack);
1471 dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
next, cumul);
1473 min_path_cumuls.push_back(cumul);
1475 current_cumul_cost_value =
1476 CapAdd(current_cumul_cost_value, GetCumulSoftCost(node, cumul));
1477 current_cumul_cost_value =
CapAdd(
1478 current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1480 if (FilterPrecedences()) {
1481 StoreMinMaxCumulOfNodesOnPath(r, min_path_cumuls,
1484 if (number_of_route_arcs == 1 &&
1485 !routing_model_.AreEmptyRouteCostsConsideredForVehicle(vehicle)) {
1488 current_cumul_cost_values_[Start(r)] = 0;
1489 current_path_transits_.ClearPath(r);
1492 if (FilterSlackCost() || FilterSoftSpanCost() ||
1493 FilterSoftSpanQuadraticCost()) {
1494 const int64 start = ComputePathMaxStartFromEndCumul(
1495 current_path_transits_, r, Start(r), cumul);
1496 const int64 span_lower_bound =
CapSub(cumul, start);
1497 if (FilterSlackCost()) {
1498 current_cumul_cost_value =
1499 CapAdd(current_cumul_cost_value,
1500 CapProd(vehicle_span_cost_coefficients_[vehicle],
1501 CapSub(span_lower_bound, total_transit)));
1503 if (FilterSoftSpanCost()) {
1504 const SimpleBoundCosts::BoundCost bound_cost =
1505 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1506 if (bound_cost.bound < span_lower_bound) {
1507 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1508 current_cumul_cost_value =
CapAdd(
1509 current_cumul_cost_value,
CapProd(bound_cost.cost, violation));
1512 if (FilterSoftSpanQuadraticCost()) {
1513 const SimpleBoundCosts::BoundCost bound_cost =
1514 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1515 if (bound_cost.bound < span_lower_bound) {
1516 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1517 current_cumul_cost_value =
1518 CapAdd(current_cumul_cost_value,
1523 if (FilterCumulSoftLowerBounds()) {
1524 current_cumul_cost_value =
1525 CapAdd(current_cumul_cost_value,
1526 GetPathCumulSoftLowerBoundCost(current_path_transits_, r));
1528 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1531 int64 lp_cumul_cost_value = 0;
1532 LocalDimensionCumulOptimizer*
const optimizer =
1533 FilterBreakCost(vehicle) ? mp_optimizer_ : optimizer_;
1536 optimizer->ComputeRouteCumulCostWithoutFixedTransits(
1537 vehicle, [
this](
int64 node) {
return Value(node); },
1538 &lp_cumul_cost_value);
1541 lp_cumul_cost_value = 0;
1543 case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
1545 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1546 vehicle, [
this](
int64 node) { return Value(node); },
1547 &lp_cumul_cost_value) ==
1549 lp_cumul_cost_value = 0;
1555 current_cumul_cost_value =
1556 std::max(current_cumul_cost_value, lp_cumul_cost_value);
1558 current_cumul_cost_values_[Start(r)] = current_cumul_cost_value;
1559 current_max_end_.path_values[r] = cumul;
1560 if (current_max_end_.cumul_value < cumul) {
1561 current_max_end_.cumul_value = cumul;
1562 current_max_end_.cumul_value_support = r;
1564 total_current_cumul_cost_value_ =
1565 CapAdd(total_current_cumul_cost_value_, current_cumul_cost_value);
1567 if (FilterPrecedences()) {
1569 for (
int64 node : GetNewSynchronizedUnperformedNodes()) {
1570 current_min_max_node_cumuls_[node] = {-1, -1};
1575 for (
int r = 0; r < NumPaths(); ++r) {
1576 const int64 start = ComputePathMaxStartFromEndCumul(
1577 current_path_transits_, r, Start(r), current_max_end_.cumul_value);
1578 current_min_start_.path_values[r] = start;
1579 if (current_min_start_.cumul_value > start) {
1580 current_min_start_.cumul_value = start;
1581 current_min_start_.cumul_value_support = r;
1587 lns_detected_ =
false;
1589 DCHECK_GE(current_max_end_.cumul_value, current_min_start_.cumul_value);
1590 synchronized_objective_value_ =
1591 CapAdd(total_current_cumul_cost_value_,
1592 CapProd(global_span_cost_coefficient_,
1593 CapSub(current_max_end_.cumul_value,
1594 current_min_start_.cumul_value)));
1597 bool PathCumulFilter::AcceptPath(
int64 path_start,
int64 chain_start,
1599 int64 node = path_start;
1601 int64 cumul_cost_delta = 0;
1602 int64 total_transit = 0;
1603 const int path = delta_path_transits_.AddPaths(1);
1604 const int vehicle = start_to_vehicle_[path_start];
1606 const bool filter_vehicle_costs =
1607 !routing_model_.IsEnd(GetNext(node)) ||
1608 routing_model_.AreEmptyRouteCostsConsideredForVehicle(vehicle);
1609 if (filter_vehicle_costs) {
1610 cumul_cost_delta =
CapAdd(GetCumulSoftCost(node, cumul),
1611 GetCumulPiecewiseLinearCost(node, cumul));
1614 int number_of_route_arcs = 0;
1615 while (node < Size()) {
1621 lns_detected_ =
true;
1624 ++number_of_route_arcs;
1627 delta_path_transits_.ReserveTransits(path, number_of_route_arcs);
1628 std::vector<int64> min_path_cumuls;
1629 min_path_cumuls.reserve(number_of_route_arcs + 1);
1630 min_path_cumuls.push_back(cumul);
1635 while (node < Size()) {
1637 const int64 transit = (*evaluators_[vehicle])(node,
next);
1638 total_transit =
CapAdd(total_transit, transit);
1639 const int64 transit_slack =
CapAdd(transit, slacks_[node]->Min());
1640 delta_path_transits_.PushTransit(path, node,
next, transit_slack);
1641 cumul =
CapAdd(cumul, transit_slack);
1642 cumul = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
next, cumul);
1647 min_path_cumuls.push_back(cumul);
1649 if (filter_vehicle_costs) {
1651 CapAdd(cumul_cost_delta, GetCumulSoftCost(node, cumul));
1653 CapAdd(cumul_cost_delta, GetCumulPiecewiseLinearCost(node, cumul));
1656 const int64 min_end = cumul;
1658 if (!PickupToDeliveryLimitsRespected(delta_path_transits_, path,
1662 if (FilterSlackCost() || FilterBreakCost(vehicle) ||
1663 FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1664 const int64 max_start_from_min_end = ComputePathMaxStartFromEndCumul(
1665 delta_path_transits_, path, path_start, min_end);
1666 int64 min_total_slack =
1667 CapSub(
CapSub(min_end, max_start_from_min_end), total_transit);
1668 if (FilterBreakCost(vehicle)) {
1671 current_path_.clear();
1672 int64 node = path_start;
1673 while (node < Size()) {
1674 current_path_.push_back(node);
1675 node = GetNext(node);
1677 current_path_.push_back(node);
1683 tasks_.num_chain_tasks = tasks_.start_min.size();
1686 tasks_.distance_duration =
1687 dimension_.GetBreakDistanceDurationOfVehicle(vehicle);
1688 if (!disjunctive_propagator_.Precedences(&tasks_) ||
1689 !disjunctive_propagator_.ChainSpanMin(&tasks_)) {
1693 std::max(min_total_slack,
CapSub(tasks_.span_min, total_transit));
1695 if (filter_vehicle_costs) {
1696 cumul_cost_delta =
CapAdd(
1698 CapProd(vehicle_span_cost_coefficients_[vehicle], min_total_slack));
1699 const int64 span_lower_bound =
CapAdd(total_transit, min_total_slack);
1700 if (FilterSoftSpanCost()) {
1701 const SimpleBoundCosts::BoundCost bound_cost =
1702 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1703 if (bound_cost.bound < span_lower_bound) {
1704 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1706 CapAdd(cumul_cost_delta,
CapProd(bound_cost.cost, violation));
1709 if (FilterSoftSpanQuadraticCost()) {
1710 const SimpleBoundCosts::BoundCost bound_cost =
1711 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1712 if (bound_cost.bound < span_lower_bound) {
1713 const int64 violation =
CapSub(span_lower_bound, bound_cost.bound);
1720 if (
CapAdd(total_transit, min_total_slack) >
1721 vehicle_span_upper_bounds_[vehicle]) {
1725 if (FilterCumulSoftLowerBounds() && filter_vehicle_costs) {
1728 GetPathCumulSoftLowerBoundCost(delta_path_transits_, path));
1730 if (FilterPrecedences()) {
1731 StoreMinMaxCumulOfNodesOnPath(path, min_path_cumuls,
true);
1733 if (!filter_vehicle_costs) {
1736 cumul_cost_delta = 0;
1737 delta_path_transits_.ClearPath(path);
1739 if (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1740 FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1741 FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1742 delta_paths_.insert(GetPath(path_start));
1743 delta_path_cumul_cost_values_[vehicle] = cumul_cost_delta;
1745 CapSub(cumul_cost_delta, current_cumul_cost_values_[path_start]);
1746 if (filter_vehicle_costs) {
1747 delta_max_end_cumul_ =
std::max(delta_max_end_cumul_, min_end);
1750 cumul_cost_delta_ =
CapAdd(cumul_cost_delta_, cumul_cost_delta);
1754 bool PathCumulFilter::FinalizeAcceptPath(
const Assignment*
delta,
1755 int64 objective_min,
1756 int64 objective_max) {
1757 if ((!FilterSpanCost() && !FilterCumulSoftBounds() && !FilterSlackCost() &&
1758 !FilterCumulSoftLowerBounds() && !FilterCumulPiecewiseLinearCosts() &&
1759 !FilterPrecedences() && !FilterSoftSpanCost() &&
1760 !FilterSoftSpanQuadraticCost()) ||
1764 if (FilterPrecedences()) {
1765 for (
int64 node : delta_nodes_with_precedences_and_changed_cumul_
1766 .PositionsSetAtLeastOnce()) {
1767 const std::pair<int64, int64> node_min_max_cumul_in_delta =
1772 DCHECK(node_min_max_cumul_in_delta.first >= 0 &&
1773 node_min_max_cumul_in_delta.second >= 0);
1774 for (
const RoutingDimension::NodePrecedence& precedence :
1775 node_index_to_precedences_[node]) {
1776 const bool node_is_first = (precedence.first_node == node);
1777 const int64 other_node =
1778 node_is_first ? precedence.second_node : precedence.first_node;
1780 GetNext(other_node) == other_node) {
1787 const std::pair<int64, int64>& other_min_max_cumul_in_delta =
1790 current_min_max_node_cumuls_[other_node]);
1792 const int64 first_min_cumul = node_is_first
1793 ? node_min_max_cumul_in_delta.first
1794 : other_min_max_cumul_in_delta.first;
1795 const int64 second_max_cumul = node_is_first
1796 ? other_min_max_cumul_in_delta.second
1797 : node_min_max_cumul_in_delta.second;
1799 if (second_max_cumul < first_min_cumul + precedence.offset) {
1805 int64 new_max_end = delta_max_end_cumul_;
1807 if (FilterSpanCost()) {
1808 if (new_max_end < current_max_end_.cumul_value) {
1813 current_max_end_.cumul_value_support)) {
1814 new_max_end = current_max_end_.cumul_value;
1816 for (
int i = 0; i < current_max_end_.path_values.size(); ++i) {
1817 if (current_max_end_.path_values[i] > new_max_end &&
1819 new_max_end = current_max_end_.path_values[i];
1827 for (
int r = 0; r < delta_path_transits_.NumPaths(); ++r) {
1829 std::min(ComputePathMaxStartFromEndCumul(delta_path_transits_, r,
1830 Start(r), new_max_end),
1833 if (new_max_end != current_max_end_.cumul_value) {
1834 for (
int r = 0; r < NumPaths(); ++r) {
1838 new_min_start =
std::min(new_min_start, ComputePathMaxStartFromEndCumul(
1839 current_path_transits_, r,
1840 Start(r), new_max_end));
1842 }
else if (new_min_start > current_min_start_.cumul_value) {
1847 current_min_start_.cumul_value_support)) {
1848 new_min_start = current_min_start_.cumul_value;
1850 for (
int i = 0; i < current_min_start_.path_values.size(); ++i) {
1851 if (current_min_start_.path_values[i] < new_min_start &&
1853 new_min_start = current_min_start_.path_values[i];
1861 accepted_objective_value_ =
1862 CapAdd(cumul_cost_delta_,
CapProd(global_span_cost_coefficient_,
1863 CapSub(new_max_end, new_min_start)));
1865 if (can_use_lp_ && optimizer_ !=
nullptr &&
1866 accepted_objective_value_ <= objective_max) {
1867 const size_t num_touched_paths = GetTouchedPathStarts().size();
1868 std::vector<int64> path_delta_cost_values(num_touched_paths, 0);
1869 std::vector<bool> requires_mp(num_touched_paths,
false);
1870 for (
int i = 0; i < num_touched_paths; ++i) {
1871 const int64 start = GetTouchedPathStarts()[i];
1872 const int vehicle = start_to_vehicle_[start];
1873 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1876 int64 path_delta_cost_with_lp = 0;
1878 optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1879 vehicle, [
this](
int64 node) {
return GetNext(node); },
1880 &path_delta_cost_with_lp);
1886 path_delta_cost_with_lp, delta_path_cumul_cost_values_[vehicle]);
1887 if (path_cost_diff_with_lp > 0) {
1888 path_delta_cost_values[i] = path_delta_cost_with_lp;
1889 accepted_objective_value_ =
1890 CapAdd(accepted_objective_value_, path_cost_diff_with_lp);
1891 if (accepted_objective_value_ > objective_max) {
1895 path_delta_cost_values[i] = delta_path_cumul_cost_values_[vehicle];
1897 if (mp_optimizer_ !=
nullptr) {
1899 FilterBreakCost(vehicle) ||
1900 (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY);
1903 if (mp_optimizer_ ==
nullptr) {
1904 return accepted_objective_value_ <= objective_max;
1906 for (
int i = 0; i < num_touched_paths; ++i) {
1907 if (!requires_mp[i]) {
1910 const int64 start = GetTouchedPathStarts()[i];
1911 const int vehicle = start_to_vehicle_[start];
1912 int64 path_delta_cost_with_mp = 0;
1913 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1914 vehicle, [
this](
int64 node) { return GetNext(node); },
1915 &path_delta_cost_with_mp) ==
1920 const int64 path_cost_diff_with_mp =
1921 CapSub(path_delta_cost_with_mp, path_delta_cost_values[i]);
1922 if (path_cost_diff_with_mp > 0) {
1923 accepted_objective_value_ =
1924 CapAdd(accepted_objective_value_, path_cost_diff_with_mp);
1925 if (accepted_objective_value_ > objective_max) {
1932 return accepted_objective_value_ <= objective_max;
1935 void PathCumulFilter::InitializeSupportedPathCumul(
1936 SupportedPathCumul* supported_cumul,
int64 default_value) {
1937 supported_cumul->cumul_value = default_value;
1938 supported_cumul->cumul_value_support = -1;
1939 supported_cumul->path_values.resize(NumPaths(), default_value);
1942 bool PathCumulFilter::PickupToDeliveryLimitsRespected(
1943 const PathTransits& path_transits,
int path,
1944 const std::vector<int64>& min_path_cumuls)
const {
1945 if (!dimension_.HasPickupToDeliveryLimits()) {
1948 const int num_pairs = routing_model_.GetPickupAndDeliveryPairs().size();
1949 DCHECK_GT(num_pairs, 0);
1950 std::vector<std::pair<int, int64>> visited_delivery_and_min_cumul_per_pair(
1951 num_pairs, {-1, -1});
1953 const int path_size = path_transits.PathSize(path);
1954 CHECK_EQ(min_path_cumuls.size(), path_size);
1956 int64 max_cumul = min_path_cumuls.back();
1957 for (
int i = path_transits.PathSize(path) - 2; i >= 0; i--) {
1958 const int node_index = path_transits.Node(path, i);
1959 max_cumul =
CapSub(max_cumul, path_transits.Transit(path, i));
1962 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1963 routing_model_.GetPickupIndexPairs(node_index);
1964 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1965 routing_model_.GetDeliveryIndexPairs(node_index);
1966 if (!pickup_index_pairs.empty()) {
1970 DCHECK(delivery_index_pairs.empty());
1971 DCHECK_EQ(pickup_index_pairs.size(), 1);
1972 const int pair_index = pickup_index_pairs[0].first;
1974 const int delivery_index =
1975 visited_delivery_and_min_cumul_per_pair[pair_index].first;
1976 if (delivery_index < 0) {
1980 const int64 cumul_diff_limit = dimension_.GetPickupToDeliveryLimitForPair(
1981 pair_index, pickup_index_pairs[0].second, delivery_index);
1982 if (
CapSub(visited_delivery_and_min_cumul_per_pair[pair_index].second,
1983 max_cumul) > cumul_diff_limit) {
1987 if (!delivery_index_pairs.empty()) {
1990 DCHECK(pickup_index_pairs.empty());
1991 DCHECK_EQ(delivery_index_pairs.size(), 1);
1992 const int pair_index = delivery_index_pairs[0].first;
1993 std::pair<int, int64>& delivery_index_and_cumul =
1994 visited_delivery_and_min_cumul_per_pair[pair_index];
1995 int& delivery_index = delivery_index_and_cumul.first;
1996 DCHECK_EQ(delivery_index, -1);
1997 delivery_index = delivery_index_pairs[0].second;
1998 delivery_index_and_cumul.second = min_path_cumuls[i];
2004 void PathCumulFilter::StoreMinMaxCumulOfNodesOnPath(
2005 int path,
const std::vector<int64>& min_path_cumuls,
bool is_delta) {
2006 const PathTransits& path_transits =
2007 is_delta ? delta_path_transits_ : current_path_transits_;
2009 const int path_size = path_transits.PathSize(path);
2010 DCHECK_EQ(min_path_cumuls.size(), path_size);
2012 int64 max_cumul =
cumuls_[path_transits.Node(path, path_size - 1)]->Max();
2013 for (
int i = path_size - 1; i >= 0; i--) {
2014 const int node_index = path_transits.Node(path, i);
2016 if (i < path_size - 1) {
2017 max_cumul =
CapSub(max_cumul, path_transits.Transit(path, i));
2021 if (is_delta && node_index_to_precedences_[node_index].empty()) {
2026 std::pair<int64, int64>& min_max_cumuls =
2027 is_delta ? node_with_precedence_to_delta_min_max_cumuls_[node_index]
2028 : current_min_max_node_cumuls_[node_index];
2029 min_max_cumuls.first = min_path_cumuls[i];
2030 min_max_cumuls.second = max_cumul;
2032 if (is_delta && !routing_model_.IsEnd(node_index) &&
2033 (min_max_cumuls.first !=
2034 current_min_max_node_cumuls_[node_index].first ||
2035 max_cumul != current_min_max_node_cumuls_[node_index].second)) {
2036 delta_nodes_with_precedences_and_changed_cumul_.
Set(node_index);
2041 int64 PathCumulFilter::ComputePathMaxStartFromEndCumul(
2042 const PathTransits& path_transits,
int path,
int64 path_start,
2043 int64 min_end_cumul)
const {
2044 int64 cumul_from_min_end = min_end_cumul;
2045 int64 cumul_from_max_end =
2046 cumuls_[routing_model_.End(start_to_vehicle_[path_start])]->Max();
2047 for (
int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
2048 const int64 transit = path_transits.Transit(path, i);
2049 const int64 node = path_transits.Node(path, i);
2050 cumul_from_min_end =
2052 cumul_from_max_end = dimension_.GetLastPossibleLessOrEqualValueForNode(
2053 node,
CapSub(cumul_from_max_end, transit));
2055 return std::min(cumul_from_min_end, cumul_from_max_end);
2063 bool propagate_own_objective_value,
bool filter_objective_cost,
2066 return model.solver()->RevAlloc(
new PathCumulFilter(
2068 filter_objective_cost, can_use_lp));
2073 bool DimensionHasCumulCost(
const RoutingDimension& dimension) {
2074 if (dimension.global_span_cost_coefficient() != 0)
return true;
2075 if (dimension.HasSoftSpanUpperBounds())
return true;
2076 if (dimension.HasQuadraticCostSoftSpanUpperBounds())
return true;
2077 for (
const int64 coefficient : dimension.vehicle_span_cost_coefficients()) {
2080 for (
int i = 0; i < dimension.cumuls().size(); ++i) {
2081 if (dimension.HasCumulVarSoftUpperBound(i))
return true;
2082 if (dimension.HasCumulVarSoftLowerBound(i))
return true;
2083 if (dimension.HasCumulVarPiecewiseLinearCost(i))
return true;
2088 bool DimensionHasCumulConstraint(
const RoutingDimension& dimension) {
2089 if (dimension.HasBreakConstraints())
return true;
2090 if (dimension.HasPickupToDeliveryLimits())
return true;
2091 if (!dimension.GetNodePrecedences().empty())
return true;
2092 for (
const int64 upper_bound : dimension.vehicle_span_upper_bounds()) {
2093 if (upper_bound !=
kint64max)
return true;
2095 for (
const IntVar*
const slack : dimension.slacks()) {
2096 if (slack->Min() > 0)
return true;
2098 const std::vector<IntVar*>& cumuls = dimension.cumuls();
2099 for (
int i = 0; i < cumuls.size(); ++i) {
2100 IntVar*
const cumul_var = cumuls[i];
2101 if (cumul_var->Min() > 0 && cumul_var->Max() <
kint64max &&
2102 !dimension.model()->IsEnd(i)) {
2105 if (dimension.forbidden_intervals()[i].NumIntervals() > 0)
return true;
2113 const std::vector<RoutingDimension*>& dimensions,
2114 const RoutingSearchParameters&
parameters,
bool filter_objective_cost,
2115 std::vector<LocalSearchFilter*>* filters) {
2123 const int num_dimensions = dimensions.size();
2125 std::vector<bool> use_path_cumul_filter(num_dimensions);
2126 std::vector<bool> use_cumul_bounds_propagator_filter(num_dimensions);
2127 std::vector<bool> use_global_lp_filter(num_dimensions);
2128 std::vector<int> filtering_difficulty(num_dimensions);
2129 for (
int d = 0; d < num_dimensions; d++) {
2131 const bool has_cumul_cost = DimensionHasCumulCost(dimension);
2132 use_path_cumul_filter[d] =
2133 has_cumul_cost || DimensionHasCumulConstraint(dimension);
2135 const bool can_use_cumul_bounds_propagator_filter =
2137 (!filter_objective_cost || !has_cumul_cost);
2139 use_global_lp_filter[d] =
2140 (has_precedences && !can_use_cumul_bounds_propagator_filter) ||
2143 use_cumul_bounds_propagator_filter[d] =
2144 has_precedences && !use_global_lp_filter[d];
2146 filtering_difficulty[d] = 4 * use_global_lp_filter[d] +
2147 2 * use_cumul_bounds_propagator_filter[d] +
2148 use_path_cumul_filter[d];
2151 std::vector<int> sorted_dimension_indices(num_dimensions);
2152 std::iota(sorted_dimension_indices.begin(), sorted_dimension_indices.end(),
2154 std::sort(sorted_dimension_indices.begin(), sorted_dimension_indices.end(),
2155 [&filtering_difficulty](
int d1,
int d2) {
2156 return filtering_difficulty[d1] < filtering_difficulty[d2];
2159 for (
const int d : sorted_dimension_indices) {
2166 const bool use_global_lp = use_global_lp_filter[d];
2167 if (use_path_cumul_filter[d]) {
2169 dimension,
parameters, !use_global_lp, filter_objective_cost));
2172 model.solver()->RevAlloc(
new ChainCumulFilter(
model, dimension)));
2175 if (use_global_lp) {
2176 DCHECK(
model.GetMutableGlobalCumulOptimizer(dimension) !=
nullptr);
2178 model.GetMutableGlobalCumulOptimizer(dimension),
2179 filter_objective_cost));
2180 }
else if (use_cumul_bounds_propagator_filter[d]) {
2189 class PickupDeliveryFilter :
public BasePathFilter {
2191 PickupDeliveryFilter(
const std::vector<IntVar*>& nexts,
int next_domain_size,
2192 const RoutingModel::IndexPairs& pairs,
2193 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2195 ~PickupDeliveryFilter()
override {}
2196 bool AcceptPath(
int64 path_start,
int64 chain_start,
2197 int64 chain_end)
override;
2198 std::string DebugString()
const override {
return "PickupDeliveryFilter"; }
2201 bool AcceptPathDefault(
int64 path_start);
2202 template <
bool lifo>
2203 bool AcceptPathOrdered(
int64 path_start);
2205 std::vector<int> pair_firsts_;
2206 std::vector<int> pair_seconds_;
2207 const RoutingModel::IndexPairs pairs_;
2208 SparseBitset<> visited_;
2209 std::deque<int> visited_deque_;
2210 const std::vector<RoutingModel::PickupAndDeliveryPolicy> vehicle_policies_;
2213 PickupDeliveryFilter::PickupDeliveryFilter(
2214 const std::vector<IntVar*>& nexts,
int next_domain_size,
2215 const RoutingModel::IndexPairs& pairs,
2216 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
2217 : BasePathFilter(nexts, next_domain_size),
2222 vehicle_policies_(vehicle_policies) {
2223 for (
int i = 0; i < pairs.size(); ++i) {
2224 const auto& index_pair = pairs[i];
2225 for (
int first : index_pair.first) {
2226 pair_firsts_[first] = i;
2228 for (
int second : index_pair.second) {
2229 pair_seconds_[second] = i;
2234 bool PickupDeliveryFilter::AcceptPath(
int64 path_start,
int64 chain_start,
2236 switch (vehicle_policies_[GetPath(path_start)]) {
2237 case RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER:
2238 return AcceptPathDefault(path_start);
2239 case RoutingModel::PICKUP_AND_DELIVERY_LIFO:
2240 return AcceptPathOrdered<true>(path_start);
2241 case RoutingModel::PICKUP_AND_DELIVERY_FIFO:
2242 return AcceptPathOrdered<false>(path_start);
2248 bool PickupDeliveryFilter::AcceptPathDefault(
int64 path_start) {
2249 visited_.ClearAll();
2250 int64 node = path_start;
2251 int64 path_length = 1;
2252 while (node < Size()) {
2254 if (path_length > Size()) {
2261 for (
int second : pairs_[pair_firsts_[node]].second) {
2262 if (visited_[second]) {
2268 bool found_first =
false;
2269 bool some_synced =
false;
2270 for (
int first : pairs_[pair_seconds_[node]].first) {
2271 if (visited_[first]) {
2275 if (IsVarSynced(first)) {
2279 if (!found_first && some_synced) {
2292 for (
const int64 node : visited_.PositionsSetAtLeastOnce()) {
2294 bool found_second =
false;
2295 bool some_synced =
false;
2296 for (
int second : pairs_[pair_firsts_[node]].second) {
2297 if (visited_[second]) {
2298 found_second =
true;
2301 if (IsVarSynced(second)) {
2305 if (!found_second && some_synced) {
2313 template <
bool lifo>
2314 bool PickupDeliveryFilter::AcceptPathOrdered(
int64 path_start) {
2315 visited_deque_.clear();
2316 int64 node = path_start;
2317 int64 path_length = 1;
2318 while (node < Size()) {
2320 if (path_length > Size()) {
2325 visited_deque_.push_back(node);
2327 visited_deque_.push_front(node);
2331 bool found_first =
false;
2332 bool some_synced =
false;
2333 for (
int first : pairs_[pair_seconds_[node]].first) {
2334 if (!visited_deque_.empty() && visited_deque_.back() == first) {
2338 if (IsVarSynced(first)) {
2342 if (!found_first && some_synced) {
2344 }
else if (!visited_deque_.empty()) {
2345 visited_deque_.pop_back();
2356 while (!visited_deque_.empty()) {
2357 for (
int second : pairs_[pair_firsts_[visited_deque_.back()]].second) {
2358 if (IsVarSynced(second)) {
2362 visited_deque_.pop_back();
2371 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2373 return routing_model.
solver()->RevAlloc(
new PickupDeliveryFilter(
2375 pairs, vehicle_policies));
2381 class VehicleVarFilter :
public BasePathFilter {
2383 explicit VehicleVarFilter(
const RoutingModel& routing_model);
2384 ~VehicleVarFilter()
override {}
2385 bool AcceptPath(
int64 path_start,
int64 chain_start,
2386 int64 chain_end)
override;
2387 std::string DebugString()
const override {
return "VehicleVariableFilter"; }
2390 bool DisableFiltering()
const override;
2391 bool IsVehicleVariableConstrained(
int index)
const;
2393 std::vector<int64> start_to_vehicle_;
2394 std::vector<IntVar*> vehicle_vars_;
2395 const int64 unconstrained_vehicle_var_domain_size_;
2398 VehicleVarFilter::VehicleVarFilter(
const RoutingModel& routing_model)
2399 : BasePathFilter(routing_model.Nexts(),
2400 routing_model.Size() + routing_model.vehicles()),
2401 vehicle_vars_(routing_model.VehicleVars()),
2402 unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) {
2403 start_to_vehicle_.resize(Size(), -1);
2404 for (
int i = 0; i < routing_model.vehicles(); ++i) {
2405 start_to_vehicle_[routing_model.Start(i)] = i;
2409 bool VehicleVarFilter::AcceptPath(
int64 path_start,
int64 chain_start,
2411 const int64 vehicle = start_to_vehicle_[path_start];
2412 int64 node = chain_start;
2413 while (node != chain_end) {
2414 if (!vehicle_vars_[node]->Contains(vehicle)) {
2417 node = GetNext(node);
2419 return vehicle_vars_[node]->Contains(vehicle);
2422 bool VehicleVarFilter::DisableFiltering()
const {
2423 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
2424 if (IsVehicleVariableConstrained(i))
return false;
2429 bool VehicleVarFilter::IsVehicleVariableConstrained(
int index)
const {
2430 const IntVar*
const vehicle_var = vehicle_vars_[
index];
2434 const int adjusted_unconstrained_vehicle_var_domain_size =
2435 vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_
2436 : unconstrained_vehicle_var_domain_size_ + 1;
2437 return vehicle_var->Size() != adjusted_unconstrained_vehicle_var_domain_size;
2444 return routing_model.
solver()->RevAlloc(
new VehicleVarFilter(routing_model));
2449 class CumulBoundsPropagatorFilter :
public IntVarLocalSearchFilter {
2451 explicit CumulBoundsPropagatorFilter(
const RoutingDimension& dimension);
2452 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2453 int64 objective_min,
int64 objective_max)
override;
2454 std::string DebugString()
const override {
2455 return "CumulBoundsPropagatorFilter(" + propagator_.dimension().name() +
2460 CumulBoundsPropagator propagator_;
2461 const int64 cumul_offset_;
2462 SparseBitset<int64> delta_touched_;
2463 std::vector<int64> delta_nexts_;
2466 CumulBoundsPropagatorFilter::CumulBoundsPropagatorFilter(
2467 const RoutingDimension& dimension)
2468 : IntVarLocalSearchFilter(dimension.
model()->Nexts()),
2469 propagator_(&dimension),
2470 cumul_offset_(dimension.GetGlobalOptimizerOffset()),
2471 delta_touched_(Size()),
2472 delta_nexts_(Size()) {}
2474 bool CumulBoundsPropagatorFilter::Accept(
const Assignment*
delta,
2475 const Assignment* deltadelta,
2476 int64 objective_min,
2477 int64 objective_max) {
2479 for (
const IntVarElement& delta_element :
2480 delta->IntVarContainer().elements()) {
2482 if (FindIndex(delta_element.Var(), &
index)) {
2483 if (!delta_element.Bound()) {
2488 delta_nexts_[
index] = delta_element.Value();
2491 const auto& next_accessor = [
this](
int64 index) {
2495 return propagator_.PropagateCumulBounds(next_accessor, cumul_offset_);
2503 new CumulBoundsPropagatorFilter(dimension));
2508 class LPCumulFilter :
public IntVarLocalSearchFilter {
2510 LPCumulFilter(
const std::vector<IntVar*>& nexts,
2511 GlobalDimensionCumulOptimizer* optimizer,
2512 bool filter_objective_cost);
2513 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2514 int64 objective_min,
int64 objective_max)
override;
2515 int64 GetAcceptedObjectiveValue()
const override;
2516 void OnSynchronize(
const Assignment*
delta)
override;
2517 int64 GetSynchronizedObjectiveValue()
const override;
2518 std::string DebugString()
const override {
2519 return "LPCumulFilter(" + optimizer_.dimension()->name() +
")";
2523 GlobalDimensionCumulOptimizer& optimizer_;
2524 const bool filter_objective_cost_;
2525 int64 synchronized_cost_without_transit_;
2526 int64 delta_cost_without_transit_;
2527 SparseBitset<int64> delta_touched_;
2528 std::vector<int64> delta_nexts_;
2531 LPCumulFilter::LPCumulFilter(
const std::vector<IntVar*>& nexts,
2532 GlobalDimensionCumulOptimizer* optimizer,
2533 bool filter_objective_cost)
2534 : IntVarLocalSearchFilter(nexts),
2535 optimizer_(*optimizer),
2536 filter_objective_cost_(filter_objective_cost),
2537 synchronized_cost_without_transit_(-1),
2538 delta_cost_without_transit_(-1),
2539 delta_touched_(Size()),
2540 delta_nexts_(Size()) {}
2542 bool LPCumulFilter::Accept(
const Assignment*
delta,
2543 const Assignment* deltadelta,
int64 objective_min,
2544 int64 objective_max) {
2546 for (
const IntVarElement& delta_element :
2547 delta->IntVarContainer().elements()) {
2549 if (FindIndex(delta_element.Var(), &
index)) {
2550 if (!delta_element.Bound()) {
2555 delta_nexts_[
index] = delta_element.Value();
2558 const auto& next_accessor = [
this](
int64 index) {
2562 if (!filter_objective_cost_) {
2564 delta_cost_without_transit_ = 0;
2565 return optimizer_.IsFeasible(next_accessor);
2568 if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
2569 next_accessor, &delta_cost_without_transit_)) {
2571 delta_cost_without_transit_ =
kint64max;
2574 return delta_cost_without_transit_ <= objective_max;
2577 int64 LPCumulFilter::GetAcceptedObjectiveValue()
const {
2578 return delta_cost_without_transit_;
2581 void LPCumulFilter::OnSynchronize(
const Assignment*
delta) {
2584 if (!optimizer_.ComputeCumulCostWithoutFixedTransits(
2585 [
this](
int64 index) { return Value(index); },
2586 &synchronized_cost_without_transit_)) {
2589 synchronized_cost_without_transit_ = 0;
2593 int64 LPCumulFilter::GetSynchronizedObjectiveValue()
const {
2594 return synchronized_cost_without_transit_;
2602 return model.solver()->RevAlloc(
2603 new LPCumulFilter(
model.Nexts(), optimizer, filter_objective_cost));
2608 CPFeasibilityFilter::CPFeasibilityFilter(
const RoutingModel* routing_model)
2610 model_(routing_model),
2611 solver_(routing_model->solver()),
2612 assignment_(solver_->MakeAssignment()),
2613 temp_assignment_(solver_->MakeAssignment()),
2614 restore_(solver_->MakeRestoreAssignment(temp_assignment_)) {
2615 assignment_->Add(routing_model->
Nexts());
2619 const Assignment* deltadelta,
2621 temp_assignment_->Copy(assignment_);
2622 AddDeltaToAssignment(
delta, temp_assignment_);
2623 return solver_->Solve(restore_);
2627 AddDeltaToAssignment(
delta, assignment_);
2630 void CPFeasibilityFilter::AddDeltaToAssignment(
const Assignment*
delta,
2631 Assignment* assignment) {
2632 if (
delta ==
nullptr) {
2635 Assignment::IntContainer*
const container =
2636 assignment->MutableIntVarContainer();
2637 const Assignment::IntContainer& delta_container =
delta->IntVarContainer();
2638 const int delta_size = delta_container.Size();
2640 for (
int i = 0; i < delta_size; i++) {
2641 const IntVarElement& delta_element = delta_container.Element(i);
2642 IntVar*
const var = delta_element.Var();
2652 container->MutableElement(
index)->Deactivate();
2655 container->MutableElement(
index)->Activate();
2663 return routing_model->
solver()->RevAlloc(
2673 int type, std::function<
bool(
int)> vehicle_is_compatible) {
2674 std::set<VehicleTypeCurator::VehicleClassEntry>& sorted_classes =
2675 sorted_vehicle_classes_per_type_[type];
2676 auto vehicle_class_it = sorted_classes.begin();
2678 while (vehicle_class_it != sorted_classes.end()) {
2679 const int vehicle_class = vehicle_class_it->vehicle_class;
2680 std::vector<int>& vehicles = vehicles_per_vehicle_class_[vehicle_class];
2681 DCHECK(!vehicles.empty());
2683 for (
auto vehicle_it = vehicles.begin(); vehicle_it != vehicles.end();
2685 const int vehicle = *vehicle_it;
2686 if (vehicle_is_compatible(vehicle)) {
2687 vehicles.erase(vehicle_it);
2688 if (vehicles.empty()) {
2689 sorted_classes.erase(vehicle_class_it);
2707 std::unique_ptr<IntVarFilteredHeuristic> heuristic)
2708 : heuristic_(std::move(heuristic)) {}
2711 Assignment*
const assignment = heuristic_->BuildSolution();
2712 if (assignment !=
nullptr) {
2713 VLOG(2) <<
"Number of decisions: " << heuristic_->number_of_decisions();
2714 VLOG(2) <<
"Number of rejected decisions: "
2715 << heuristic_->number_of_rejects();
2716 assignment->Restore();
2724 return heuristic_->number_of_decisions();
2728 return heuristic_->number_of_rejects();
2732 return absl::StrCat(
"IntVarFilteredDecisionBuilder(",
2733 heuristic_->DebugString(),
")");
2741 Solver* solver,
const std::vector<IntVar*>& vars,
2743 : assignment_(solver->MakeAssignment()),
2746 delta_(solver->MakeAssignment()),
2747 is_in_delta_(
vars_.size(), false),
2748 empty_(solver->MakeAssignment()),
2749 filter_manager_(filter_manager),
2750 number_of_decisions_(0),
2751 number_of_rejects_(0) {
2752 assignment_->MutableIntVarContainer()->Resize(vars_.size());
2753 delta_indices_.reserve(vars_.size());
2757 number_of_decisions_ = 0;
2758 number_of_rejects_ = 0;
2761 assignment_->MutableIntVarContainer()->Resize(vars_.size());
2762 delta_->MutableIntVarContainer()->Clear();
2779 const std::function<
int64(
int64)>& next_accessor) {
2784 start_chain_ends_.resize(
model()->vehicles());
2785 end_chain_starts_.resize(
model()->vehicles());
2787 for (
int v = 0; v < model_->
vehicles(); v++) {
2789 while (!model_->
IsEnd(node)) {
2791 DCHECK_NE(
next, node);
2797 start_chain_ends_[v] =
model()->
End(v);
2811 ++number_of_decisions_;
2812 const bool accept = FilterAccept();
2814 const Assignment::IntContainer& delta_container = delta_->IntVarContainer();
2815 const int delta_size = delta_container.Size();
2816 Assignment::IntContainer*
const container =
2818 for (
int i = 0; i < delta_size; ++i) {
2819 const IntVarElement& delta_element = delta_container.Element(i);
2820 IntVar*
const var = delta_element.Var();
2821 DCHECK_EQ(
var, vars_[delta_indices_[i]]);
2822 container->AddAtPosition(
var, delta_indices_[i])
2823 ->SetValue(delta_element.Value());
2827 ++number_of_rejects_;
2830 for (
const int delta_index : delta_indices_) {
2831 is_in_delta_[delta_index] =
false;
2834 delta_indices_.clear();
2842 bool IntVarFilteredHeuristic::FilterAccept() {
2843 if (!filter_manager_)
return true;
2855 bool RoutingFilteredHeuristic::InitializeSolution() {
2865 start_chain_ends_.clear();
2866 start_chain_ends_.resize(
model()->vehicles(), -1);
2867 end_chain_starts_.clear();
2868 end_chain_starts_.resize(
model()->vehicles(), -1);
2871 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2873 while (!
model()->IsEnd(node) &&
Var(node)->Bound()) {
2879 start_chain_ends_[vehicle] = node;
2882 std::vector<int64> starts(
Size() +
model()->vehicles(), -1);
2883 std::vector<int64> ends(
Size() +
model()->vehicles(), -1);
2886 starts[node] = node;
2889 std::vector<bool> touched(
Size(),
false);
2890 for (
int node = 0; node <
Size(); ++node) {
2892 while (!
model()->
IsEnd(current) && !touched[current]) {
2893 touched[current] =
true;
2894 IntVar*
const next_var =
Var(current);
2895 if (next_var->Bound()) {
2896 current = next_var->Value();
2901 starts[ends[current]] = starts[node];
2902 ends[starts[node]] = ends[current];
2907 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
2908 end_chain_starts_[vehicle] = starts[
model()->
End(vehicle)];
2909 int64 node = start_chain_ends_[vehicle];
2910 if (!
model()->IsEnd(node)) {
2915 while (!
model()->IsEnd(node)) {
2933 node, 1, [
this, node](
int alternate) {
2934 if (node != alternate && !
Contains(alternate)) {
2952 std::function<
int64(
int64)> penalty_evaluator,
2956 penalty_evaluator_(std::move(penalty_evaluator)) {}
2958 std::vector<std::vector<CheapestInsertionFilteredHeuristic::StartEndValue>>
2960 const std::vector<int>& vehicles) {
2961 std::vector<std::vector<StartEndValue>> start_end_distances_per_node(
2964 for (
int node = 0; node <
model()->
Size(); node++) {
2966 std::vector<StartEndValue>& start_end_distances =
2967 start_end_distances_per_node[node];
2969 for (
const int vehicle : vehicles) {
2974 const int64 distance =
2975 CapAdd(
model()->GetArcCostForVehicle(start, node, vehicle),
2976 model()->GetArcCostForVehicle(node, end, vehicle));
2977 start_end_distances.push_back({distance, vehicle});
2981 std::sort(start_end_distances.begin(), start_end_distances.end(),
2983 return second < first;
2986 return start_end_distances_per_node;
2989 template <
class Queue>
2991 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
2992 Queue* priority_queue) {
2994 DCHECK_EQ(start_end_distances_per_node->size(), num_nodes);
2996 for (
int node = 0; node < num_nodes; node++) {
2998 std::vector<StartEndValue>& start_end_distances =
2999 (*start_end_distances_per_node)[node];
3000 if (start_end_distances.empty()) {
3004 const StartEndValue& start_end_value = start_end_distances.back();
3005 priority_queue->push(std::make_pair(start_end_value, node));
3006 start_end_distances.pop_back();
3020 std::vector<ValuedPosition>* valued_positions) {
3021 CHECK(valued_positions !=
nullptr);
3022 int64 insert_after = start;
3023 while (!
model()->IsEnd(insert_after)) {
3024 const int64 insert_before =
3025 (insert_after == start) ? next_after_start :
Value(insert_after);
3026 valued_positions->push_back(std::make_pair(
3028 insert_before, vehicle),
3030 insert_after = insert_before;
3036 int vehicle)
const {
3038 evaluator_(node_to_insert, insert_before, vehicle)),
3039 evaluator_(insert_after, insert_before, vehicle));
3043 int64 node_to_insert)
const {
3052 void SortAndExtractPairSeconds(std::vector<std::pair<int64, T>>* pairs,
3053 std::vector<T>* sorted_seconds) {
3054 CHECK(pairs !=
nullptr);
3055 CHECK(sorted_seconds !=
nullptr);
3056 std::sort(pairs->begin(), pairs->end());
3057 sorted_seconds->reserve(pairs->size());
3058 for (
const std::pair<int64, T>& p : *pairs) {
3059 sorted_seconds->push_back(p.second);
3083 if (value_ != other.value_) {
3084 return value_ > other.value_;
3086 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
3087 return vehicle_ == -1;
3089 return std::tie(pickup_insert_after_, pickup_to_insert_,
3090 delivery_insert_after_, delivery_to_insert_, vehicle_) >
3091 std::tie(other.pickup_insert_after_, other.pickup_to_insert_,
3092 other.delivery_insert_after_, other.delivery_to_insert_,
3108 const int pickup_to_insert_;
3109 const int pickup_insert_after_;
3110 const int delivery_to_insert_;
3111 const int delivery_insert_after_;
3126 if (value_ != other.value_) {
3127 return value_ > other.value_;
3129 if ((vehicle_ == -1) ^ (other.vehicle_ == -1)) {
3130 return vehicle_ == -1;
3132 return std::tie(insert_after_, node_to_insert_, vehicle_) >
3133 std::tie(other.insert_after_, other.node_to_insert_, other.vehicle_);
3146 const int node_to_insert_;
3147 const int insert_after_;
3157 std::function<
int64(
int64)> penalty_evaluator,
3161 std::move(penalty_evaluator),
3164 node_index_to_vehicle_(
model->Size(), -1) {
3171 if (num_neighbors >= size - 1) {
3179 for (
int64 node = 0; node < size; node++) {
3181 pickup_nodes_.push_back(node);
3183 delivery_nodes_.push_back(node);
3185 single_nodes_.push_back(node);
3191 void GlobalCheapestInsertionFilteredHeuristic::ComputeNeighborhoods() {
3196 if (!node_index_to_single_neighbors_by_cost_class_.empty()) {
3198 DCHECK(!node_index_to_pickup_neighbors_by_cost_class_.empty());
3199 DCHECK(!node_index_to_delivery_neighbors_by_cost_class_.empty());
3204 const RoutingModel& routing_model = *
model();
3205 const int64 size = routing_model.Size();
3209 DCHECK_LT(num_neighbors, size - 1);
3211 node_index_to_single_neighbors_by_cost_class_.resize(size);
3212 node_index_to_pickup_neighbors_by_cost_class_.resize(size);
3213 node_index_to_delivery_neighbors_by_cost_class_.resize(size);
3214 const int num_cost_classes = routing_model.GetCostClassesCount();
3215 for (
int64 node_index = 0; node_index < size; node_index++) {
3216 node_index_to_single_neighbors_by_cost_class_[node_index].resize(
3218 node_index_to_pickup_neighbors_by_cost_class_[node_index].resize(
3220 node_index_to_delivery_neighbors_by_cost_class_[node_index].resize(
3222 for (
int cc = 0; cc < num_cost_classes; cc++) {
3223 node_index_to_single_neighbors_by_cost_class_[node_index][cc] =
3224 absl::make_unique<SparseBitset<int64>>(size);
3225 node_index_to_pickup_neighbors_by_cost_class_[node_index][cc] =
3226 absl::make_unique<SparseBitset<int64>>(size);
3227 node_index_to_delivery_neighbors_by_cost_class_[node_index][cc] =
3228 absl::make_unique<SparseBitset<int64>>(size);
3232 for (
int64 node_index = 0; node_index < size; ++node_index) {
3233 DCHECK(!routing_model.IsEnd(node_index));
3234 const bool node_is_pickup =
3235 !routing_model.GetPickupIndexPairs(node_index).empty();
3236 const bool node_is_delivery =
3237 !routing_model.GetDeliveryIndexPairs(node_index).empty();
3240 for (
int cost_class = 0; cost_class < num_cost_classes; cost_class++) {
3241 if (!routing_model.HasVehicleWithCostClassIndex(
3242 RoutingCostClassIndex(cost_class))) {
3246 std::vector<std::pair<
int64,
int64>> costed_after_nodes;
3247 costed_after_nodes.reserve(size);
3248 for (
int after_node = 0; after_node < size; ++after_node) {
3249 if (after_node != node_index) {
3250 costed_after_nodes.push_back(
3251 std::make_pair(routing_model.GetArcCostForClass(
3252 node_index, after_node, cost_class),
3256 std::nth_element(costed_after_nodes.begin(),
3257 costed_after_nodes.begin() + num_neighbors - 1,
3258 costed_after_nodes.end());
3259 costed_after_nodes.resize(num_neighbors);
3261 for (
const auto& costed_neighbor : costed_after_nodes) {
3262 const int64 neighbor = costed_neighbor.second;
3263 AddNeighborForCostClass(
3264 cost_class, node_index, neighbor,
3265 !routing_model.GetPickupIndexPairs(neighbor).empty(),
3266 !routing_model.GetDeliveryIndexPairs(neighbor).empty());
3269 DCHECK(!routing_model.IsEnd(neighbor));
3270 AddNeighborForCostClass(cost_class, neighbor, node_index,
3271 node_is_pickup, node_is_delivery);
3277 void GlobalCheapestInsertionFilteredHeuristic::AddNeighborForCostClass(
3278 int cost_class,
int64 node_index,
int64 neighbor_index,
3279 bool neighbor_is_pickup,
bool neighbor_is_delivery) {
3280 if (neighbor_is_pickup) {
3281 node_index_to_pickup_neighbors_by_cost_class_[node_index][cost_class]->Set(
3284 if (neighbor_is_delivery) {
3285 node_index_to_delivery_neighbors_by_cost_class_[node_index][cost_class]
3286 ->Set(neighbor_index);
3288 if (!neighbor_is_pickup && !neighbor_is_delivery) {
3289 node_index_to_single_neighbors_by_cost_class_[node_index][cost_class]->Set(
3294 bool GlobalCheapestInsertionFilteredHeuristic::IsNeighborForCostClass(
3295 int cost_class,
int64 node_index,
int64 neighbor_index)
const {
3299 const SparseBitset<int64>* neighbors;
3300 if (!
model()->GetPickupIndexPairs(neighbor_index).empty()) {
3302 node_index_to_pickup_neighbors_by_cost_class_[node_index][cost_class]
3304 }
else if (!
model()->GetDeliveryIndexPairs(neighbor_index).empty()) {
3306 node_index_to_delivery_neighbors_by_cost_class_[node_index][cost_class]
3310 node_index_to_single_neighbors_by_cost_class_[node_index][cost_class]
3313 return (*neighbors)[neighbor_index];
3316 bool GlobalCheapestInsertionFilteredHeuristic::CheckVehicleIndices()
const {
3317 std::vector<bool> node_is_visited(
model()->
Size(), -1);
3320 node =
Value(node)) {
3321 if (node_index_to_vehicle_[node] != v) {
3324 node_is_visited[node] =
true;
3328 for (
int node = 0; node <
model()->
Size(); node++) {
3329 if (!node_is_visited[node] && node_index_to_vehicle_[node] != -1) {
3338 ComputeNeighborhoods();
3340 absl::flat_hash_map<int, std::vector<int>> vehicle_to_pair_nodes;
3342 model()->GetPickupAndDeliveryPairs()) {
3343 int pickup_vehicle = -1;
3344 for (
int64 pickup : index_pair.first) {
3346 pickup_vehicle = node_index_to_vehicle_[pickup];
3350 int delivery_vehicle = -1;
3351 for (
int64 delivery : index_pair.second) {
3353 delivery_vehicle = node_index_to_vehicle_[delivery];
3357 if (pickup_vehicle >= 0 && delivery_vehicle < 0) {
3358 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[pickup_vehicle];
3359 for (
int64 delivery : index_pair.second) {
3360 pair_nodes.push_back(delivery);
3363 if (pickup_vehicle < 0 && delivery_vehicle >= 0) {
3364 std::vector<int>& pair_nodes = vehicle_to_pair_nodes[delivery_vehicle];
3365 for (
int64 pickup : index_pair.first) {
3366 pair_nodes.push_back(pickup);
3370 for (
const auto& vehicle_and_nodes : vehicle_to_pair_nodes) {
3371 InsertNodesOnRoutes(vehicle_and_nodes.second, {vehicle_and_nodes.first});
3374 InsertNodesByRequirementTopologicalOrder();
3379 std::vector<int> nodes;
3380 for (
int node = 0; node <
model()->
Size(); ++node) {
3382 nodes.push_back(node);
3385 InsertFarthestNodesAsSeeds();
3387 SequentialInsertNodes(nodes);
3389 InsertNodesOnRoutes(nodes, {});
3392 DCHECK(CheckVehicleIndices());
3396 void GlobalCheapestInsertionFilteredHeuristic::
3397 InsertNodesByRequirementTopologicalOrder() {
3398 for (
int type :
model()->GetTopologicallySortedVisitTypes()) {
3399 InsertNodesOnRoutes(
model()->GetSingleNodesOfType(type), {});
3403 void GlobalCheapestInsertionFilteredHeuristic::InsertPairs() {
3405 std::vector<PairEntries> pickup_to_entries;
3406 std::vector<PairEntries> delivery_to_entries;
3407 InitializePairPositions(&priority_queue, &pickup_to_entries,
3408 &delivery_to_entries);
3409 while (!priority_queue.
IsEmpty()) {
3411 for (PairEntry*
const entry : *priority_queue.
Raw()) {
3416 PairEntry*
const entry = priority_queue.
Top();
3417 if (
Contains(entry->pickup_to_insert()) ||
3418 Contains(entry->delivery_to_insert())) {
3419 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3420 &delivery_to_entries);
3424 if (entry->vehicle() == -1) {
3426 SetValue(entry->pickup_to_insert(), entry->pickup_to_insert());
3427 SetValue(entry->delivery_to_insert(), entry->delivery_to_insert());
3429 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3430 &delivery_to_entries);
3436 const int64 pickup_insert_before =
Value(entry->pickup_insert_after());
3437 InsertBetween(entry->pickup_to_insert(), entry->pickup_insert_after(),
3438 pickup_insert_before);
3439 const int64 delivery_insert_before =
3440 (entry->pickup_to_insert() == entry->delivery_insert_after())
3441 ? pickup_insert_before
3442 :
Value(entry->delivery_insert_after());
3443 InsertBetween(entry->delivery_to_insert(), entry->delivery_insert_after(),
3444 delivery_insert_before);
3446 const int64 pickup_after = entry->pickup_insert_after();
3447 const int64 pickup = entry->pickup_to_insert();
3448 const int64 delivery_after = entry->delivery_insert_after();
3449 const int64 delivery = entry->delivery_to_insert();
3450 const int vehicle = entry->vehicle();
3451 UpdatePairPositions(vehicle, pickup_after, &priority_queue,
3452 &pickup_to_entries, &delivery_to_entries);
3453 UpdatePairPositions(vehicle, pickup, &priority_queue, &pickup_to_entries,
3454 &delivery_to_entries);
3455 UpdatePairPositions(vehicle, delivery, &priority_queue,
3456 &pickup_to_entries, &delivery_to_entries);
3457 if (pickup != delivery_after) {
3458 UpdatePairPositions(vehicle, delivery_after, &priority_queue,
3459 &pickup_to_entries, &delivery_to_entries);
3461 SetVehicleIndex(pickup, vehicle);
3462 SetVehicleIndex(delivery, vehicle);
3464 DeletePairEntry(entry, &priority_queue, &pickup_to_entries,
3465 &delivery_to_entries);
3470 void GlobalCheapestInsertionFilteredHeuristic::InsertNodesOnRoutes(
3471 const std::vector<int>& nodes,
const absl::flat_hash_set<int>& vehicles) {
3473 std::vector<NodeEntries> position_to_node_entries;
3474 InitializePositions(nodes, &priority_queue, &position_to_node_entries,
3476 const bool all_routes =
3479 while (!priority_queue.
IsEmpty()) {
3480 NodeEntry*
const node_entry = priority_queue.
Top();
3482 for (NodeEntry*
const entry : *priority_queue.
Raw()) {
3489 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3493 if (node_entry->vehicle() == -1) {
3497 SetValue(node_to_insert, node_to_insert);
3499 DeleteNodeEntry(node_entry, &priority_queue,
3500 &position_to_node_entries);
3503 DCHECK_EQ(node_entry->value(), 0);
3511 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3512 while (!priority_queue.
IsEmpty()) {
3513 NodeEntry*
const to_delete = priority_queue.
Top();
3514 DeleteNodeEntry(to_delete, &priority_queue,
3515 &position_to_node_entries);
3522 const int64 insert_after = node_entry->insert_after();
3525 const int vehicle = node_entry->vehicle();
3526 UpdatePositions(nodes, vehicle, node_to_insert, &priority_queue,
3527 &position_to_node_entries);
3528 UpdatePositions(nodes, vehicle, insert_after, &priority_queue,
3529 &position_to_node_entries);
3530 SetVehicleIndex(node_to_insert, vehicle);
3532 DeleteNodeEntry(node_entry, &priority_queue, &position_to_node_entries);
3537 void GlobalCheapestInsertionFilteredHeuristic::SequentialInsertNodes(
3538 const std::vector<int>& nodes) {
3539 std::vector<bool> is_vehicle_used;
3540 absl::flat_hash_set<int> used_vehicles;
3541 std::vector<int> unused_vehicles;
3543 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
3544 if (!used_vehicles.empty()) {
3545 InsertNodesOnRoutes(nodes, used_vehicles);
3548 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
3550 std::priority_queue<Seed, std::vector<Seed>, std::greater<Seed>>
3554 int vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
3557 while (vehicle >= 0) {
3558 InsertNodesOnRoutes(nodes, {vehicle});
3559 vehicle = InsertSeedNode(&start_end_distances_per_node, &first_node_queue,
3564 void GlobalCheapestInsertionFilteredHeuristic::DetectUsedVehicles(
3565 std::vector<bool>* is_vehicle_used, std::vector<int>* unused_vehicles,
3566 absl::flat_hash_set<int>* used_vehicles) {
3567 is_vehicle_used->clear();
3568 is_vehicle_used->resize(
model()->vehicles());
3570 used_vehicles->clear();
3571 used_vehicles->reserve(
model()->vehicles());
3573 unused_vehicles->clear();
3574 unused_vehicles->reserve(
model()->vehicles());
3576 for (
int vehicle = 0; vehicle <
model()->
vehicles(); vehicle++) {
3578 (*is_vehicle_used)[vehicle] =
true;
3579 used_vehicles->insert(vehicle);
3581 (*is_vehicle_used)[vehicle] =
false;
3582 unused_vehicles->push_back(vehicle);
3587 void GlobalCheapestInsertionFilteredHeuristic::InsertFarthestNodesAsSeeds() {
3590 const int num_seeds =
static_cast<int>(
3593 std::vector<bool> is_vehicle_used;
3594 absl::flat_hash_set<int> used_vehicles;
3595 std::vector<int> unused_vehicles;
3596 DetectUsedVehicles(&is_vehicle_used, &unused_vehicles, &used_vehicles);
3597 std::vector<std::vector<StartEndValue>> start_end_distances_per_node =
3602 std::priority_queue<Seed> farthest_node_queue;
3605 int inserted_seeds = 0;
3606 while (!farthest_node_queue.empty() && inserted_seeds < num_seeds) {
3607 InsertSeedNode(&start_end_distances_per_node, &farthest_node_queue,
3613 template <
class Queue>
3614 int GlobalCheapestInsertionFilteredHeuristic::InsertSeedNode(
3615 std::vector<std::vector<StartEndValue>>* start_end_distances_per_node,
3616 Queue* priority_queue, std::vector<bool>* is_vehicle_used) {
3617 while (!priority_queue->empty()) {
3619 const Seed& seed = priority_queue->top();
3621 const int seed_node = seed.second;
3622 const int seed_vehicle = seed.first.vehicle;
3624 std::vector<StartEndValue>& other_start_end_values =
3625 (*start_end_distances_per_node)[seed_node];
3630 priority_queue->pop();
3631 other_start_end_values.clear();
3634 if (!(*is_vehicle_used)[seed_vehicle]) {
3638 DCHECK_EQ(
Value(start), end);
3641 priority_queue->pop();
3642 (*is_vehicle_used)[seed_vehicle] =
true;
3643 other_start_end_values.clear();
3644 SetVehicleIndex(seed_node, seed_vehicle);
3645 return seed_vehicle;
3652 priority_queue->pop();
3653 if (!other_start_end_values.empty()) {
3654 const StartEndValue& next_seed_value = other_start_end_values.back();
3655 priority_queue->push(std::make_pair(next_seed_value, seed_node));
3656 other_start_end_values.pop_back();
3663 void GlobalCheapestInsertionFilteredHeuristic::InitializePairPositions(
3665 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3666 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3668 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3669 delivery_to_entries) {
3670 priority_queue->Clear();
3671 pickup_to_entries->clear();
3672 pickup_to_entries->resize(
model()->
Size());
3673 delivery_to_entries->clear();
3674 delivery_to_entries->resize(
model()->
Size());
3676 model()->GetPickupAndDeliveryPairs()) {
3677 for (
int64 pickup : index_pair.first) {
3678 for (
int64 delivery : index_pair.second) {
3683 FLAGS_routing_shift_insertion_cost_by_penalty ?
kint64max : 0;
3688 if (index_pair.first.size() == 1 && index_pair.second.size() == 1) {
3692 PairEntry*
const entry =
3693 new PairEntry(pickup, -1, delivery, -1, -1);
3694 if (FLAGS_routing_shift_insertion_cost_by_penalty) {
3695 entry->set_value(0);
3696 penalty =
CapAdd(pickup_penalty, delivery_penalty);
3698 entry->set_value(
CapAdd(pickup_penalty, delivery_penalty));
3701 priority_queue->Add(entry);
3705 InitializeInsertionEntriesPerformingPair(
3706 pickup, delivery, penalty, priority_queue, pickup_to_entries,
3707 delivery_to_entries);
3713 void GlobalCheapestInsertionFilteredHeuristic::
3714 InitializeInsertionEntriesPerformingPair(
3717 GlobalCheapestInsertionFilteredHeuristic::PairEntry>*
3719 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3721 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3722 delivery_to_entries) {
3724 std::vector<std::pair<std::pair<int64, int>, std::pair<int64, int64>>>
3726 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
3727 std::vector<ValuedPosition> valued_pickup_positions;
3730 &valued_pickup_positions);
3732 valued_pickup_positions) {
3733 const int64 pickup_position = valued_pickup_position.second;
3734 CHECK(!
model()->IsEnd(pickup_position));
3735 std::vector<ValuedPosition> valued_delivery_positions;
3737 vehicle, &valued_delivery_positions);
3739 valued_delivery_positions) {
3740 valued_positions.push_back(std::make_pair(
3741 std::make_pair(
CapAdd(valued_pickup_position.first,
3742 valued_delivery_position.first),
3744 std::make_pair(pickup_position,
3745 valued_delivery_position.second)));
3749 for (
const std::pair<std::pair<int64, int>, std::pair<int64, int64>>&
3750 valued_position : valued_positions) {
3751 PairEntry*
const entry =
new PairEntry(
3752 pickup, valued_position.second.first, delivery,
3753 valued_position.second.second, valued_position.first.second);
3754 entry->set_value(
CapSub(valued_position.first.first, penalty));
3755 pickup_to_entries->at(valued_position.second.first).insert(entry);
3756 DCHECK_NE(valued_position.second.first, valued_position.second.second);
3757 delivery_to_entries->at(valued_position.second.second).insert(entry);
3758 priority_queue->Add(entry);
3767 absl::flat_hash_set<std::pair<int64, int64>> existing_insertion_positions;
3769 for (
const std::vector<int64>*
const neighbors :
3770 GetNeighborsOfNodeForCostClass(cost_class, pickup)) {
3771 for (
const int64 pickup_insert_after : *neighbors) {
3772 if (!
Contains(pickup_insert_after)) {
3775 const int vehicle = node_index_to_vehicle_[pickup_insert_after];
3782 pickup, pickup_insert_after,
Value(pickup_insert_after), vehicle);
3783 int64 delivery_insert_after = pickup;
3784 while (!
model()->IsEnd(delivery_insert_after)) {
3785 const std::pair<int64, int64> insertion_position = {
3786 pickup_insert_after, delivery_insert_after};
3788 insertion_position));
3789 existing_insertion_positions.insert(insertion_position);
3790 PairEntry*
const entry =
3791 new PairEntry(pickup, pickup_insert_after, delivery,
3792 delivery_insert_after, vehicle);
3793 pickup_to_entries->at(pickup_insert_after).insert(entry);
3794 delivery_to_entries->at(delivery_insert_after).insert(entry);
3796 const int64 delivery_insert_before =
3797 (delivery_insert_after == pickup) ?
Value(pickup_insert_after)
3798 :
Value(delivery_insert_after);
3800 delivery, delivery_insert_after, delivery_insert_before, vehicle);
3802 CapSub(
CapAdd(pickup_value, delivery_value), penalty));
3803 priority_queue->Add(entry);
3804 delivery_insert_after = delivery_insert_before;
3810 for (
const std::vector<int64>*
const neighbors :
3811 GetNeighborsOfNodeForCostClass(cost_class, delivery)) {
3812 for (
const int64 delivery_insert_after : *neighbors) {
3813 if (!
Contains(delivery_insert_after)) {
3816 const int vehicle = node_index_to_vehicle_[delivery_insert_after];
3817 if (
model()->GetCostClassIndexOfVehicle(vehicle).
value() !=
3823 delivery, delivery_insert_after,
Value(delivery_insert_after),
3826 while (pickup_insert_after != delivery_insert_after) {
3827 const int64 pickup_insert_before =
Value(pickup_insert_after);
3829 existing_insertion_positions,
3830 std::make_pair(pickup_insert_after, delivery_insert_after))) {
3831 pickup_insert_after = pickup_insert_before;
3834 PairEntry*
const entry =
3835 new PairEntry(pickup, pickup_insert_after, delivery,
3836 delivery_insert_after, vehicle);
3837 pickup_to_entries->at(pickup_insert_after).insert(entry);
3838 delivery_to_entries->at(delivery_insert_after).insert(entry);
3840 pickup, pickup_insert_after, pickup_insert_before, vehicle);
3842 CapSub(
CapAdd(pickup_value, delivery_value), penalty));
3843 priority_queue->Add(entry);
3844 pickup_insert_after = pickup_insert_before;
3851 void GlobalCheapestInsertionFilteredHeuristic::UpdatePickupPositions(
3852 int vehicle,
int64 pickup_insert_after,
3854 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3855 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3857 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3858 delivery_to_entries) {
3861 using Pair = std::pair<int64, int64>;
3862 using Insertion = std::pair<Pair,
int64>;
3863 absl::flat_hash_set<Insertion> existing_insertions;
3864 std::vector<PairEntry*> to_remove;
3865 for (PairEntry*
const pair_entry :
3866 pickup_to_entries->at(pickup_insert_after)) {
3867 DCHECK(priority_queue->Contains(pair_entry));
3868 DCHECK_EQ(pair_entry->pickup_insert_after(), pickup_insert_after);
3869 if (
Contains(pair_entry->pickup_to_insert()) ||
3870 Contains(pair_entry->delivery_to_insert())) {
3871 to_remove.push_back(pair_entry);
3873 existing_insertions.insert(
3874 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
3875 pair_entry->delivery_insert_after()});
3878 for (PairEntry*
const pair_entry : to_remove) {
3879 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
3880 delivery_to_entries);
3885 const int64 pickup_insert_before =
Value(pickup_insert_after);
3887 GetPickupNeighborsOfNodeForCostClass(cost_class, pickup_insert_after)) {
3891 for (
const auto& pickup_index_pair :
model()->GetPickupIndexPairs(pickup)) {
3894 for (
int64 delivery : index_pair.second) {
3898 int64 delivery_insert_after = pickup;
3899 while (!
model()->IsEnd(delivery_insert_after)) {
3900 const std::pair<Pair, int64> insertion = {{pickup, delivery},
3901 delivery_insert_after};
3903 PairEntry*
const entry =
3904 new PairEntry(pickup, pickup_insert_after, delivery,
3905 delivery_insert_after, vehicle);
3906 pickup_to_entries->at(pickup_insert_after).insert(entry);
3907 delivery_to_entries->at(delivery_insert_after).insert(entry);
3909 if (delivery_insert_after == pickup) {
3910 delivery_insert_after = pickup_insert_before;
3912 delivery_insert_after =
Value(delivery_insert_after);
3921 for (PairEntry*
const pair_entry :
3922 pickup_to_entries->at(pickup_insert_after)) {
3923 const int64 pickup = pair_entry->pickup_to_insert();
3924 const int64 delivery = pair_entry->delivery_to_insert();
3925 DCHECK_EQ(pickup_insert_after, pair_entry->pickup_insert_after());
3927 pickup, pickup_insert_after, pickup_insert_before, vehicle);
3928 const int64 delivery_insert_after = pair_entry->delivery_insert_after();
3929 const int64 delivery_insert_before = (delivery_insert_after == pickup)
3930 ? pickup_insert_before
3931 :
Value(delivery_insert_after);
3933 delivery, delivery_insert_after, delivery_insert_before, vehicle);
3934 const int64 penalty =
3935 FLAGS_routing_shift_insertion_cost_by_penalty
3938 pair_entry->set_value(
3939 CapSub(
CapAdd(pickup_value, delivery_value), penalty));
3940 if (priority_queue->Contains(pair_entry)) {
3941 priority_queue->NoteChangedPriority(pair_entry);
3943 priority_queue->Add(pair_entry);
3948 void GlobalCheapestInsertionFilteredHeuristic::UpdateDeliveryPositions(
3949 int vehicle,
int64 delivery_insert_after,
3951 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
3952 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3954 std::vector<GlobalCheapestInsertionFilteredHeuristic::PairEntries>*
3955 delivery_to_entries) {
3958 using Pair = std::pair<int64, int64>;
3959 using Insertion = std::pair<Pair,
int64>;
3960 absl::flat_hash_set<Insertion> existing_insertions;
3961 std::vector<PairEntry*> to_remove;
3962 for (PairEntry*
const pair_entry :
3963 delivery_to_entries->at(delivery_insert_after)) {
3964 DCHECK(priority_queue->Contains(pair_entry));
3965 DCHECK_EQ(pair_entry->delivery_insert_after(), delivery_insert_after);
3966 if (
Contains(pair_entry->pickup_to_insert()) ||
3967 Contains(pair_entry->delivery_to_insert())) {
3968 to_remove.push_back(pair_entry);
3970 existing_insertions.insert(
3971 {{pair_entry->pickup_to_insert(), pair_entry->delivery_to_insert()},
3972 pair_entry->pickup_insert_after()});
3975 for (PairEntry*
const pair_entry : to_remove) {
3976 DeletePairEntry(pair_entry, priority_queue, pickup_to_entries,
3977 delivery_to_entries);
3982 const int64 delivery_insert_before =
Value(delivery_insert_after);
3983 for (
int64 delivery : GetDeliveryNeighborsOfNodeForCostClass(
3984 cost_class, delivery_insert_after)) {
3988 for (
const auto& delivery_index_pair :
3989 model()->GetDeliveryIndexPairs(delivery)) {
3992 for (
int64 pickup : index_pair.first) {
3997 while (pickup_insert_after != delivery_insert_after) {
3998 std::pair<Pair, int64> insertion = {{pickup, delivery},
3999 pickup_insert_after};
4001 PairEntry*
const entry =
4002 new PairEntry(pickup, pickup_insert_after, delivery,
4003 delivery_insert_after, vehicle);
4004 pickup_to_entries->at(pickup_insert_after).insert(entry);
4005 delivery_to_entries->at(delivery_insert_after).insert(entry);
4007 pickup_insert_after =
Value(pickup_insert_after);
4015 for (PairEntry*
const pair_entry :
4016 delivery_to_entries->at(delivery_insert_after)) {
4017 const int64 pickup = pair_entry->pickup_to_insert();
4018 const int64 delivery = pair_entry->delivery_to_insert();
4019 DCHECK_EQ(delivery_insert_after, pair_entry->delivery_insert_after());
4020 const int64 pickup_insert_after = pair_entry->pickup_insert_after();
4022 pickup, pickup_insert_after,
Value(pickup_insert_after), vehicle);
4024 delivery, delivery_insert_after, delivery_insert_before, vehicle);
4025 const int64 penalty =
4026 FLAGS_routing_shift_insertion_cost_by_penalty
4029 pair_entry->set_value(
4030 CapSub(
CapAdd(pickup_value, delivery_value), penalty));
4031 if (priority_queue->Contains(pair_entry)) {
4032 priority_queue->NoteChangedPriority(pair_entry);
4034 priority_queue->Add(pair_entry);
4039 void GlobalCheapestInsertionFilteredHeuristic::DeletePairEntry(
4040 GlobalCheapestInsertionFilteredHeuristic::PairEntry* entry,
4042 GlobalCheapestInsertionFilteredHeuristic::PairEntry>* priority_queue,
4043 std::vector<PairEntries>* pickup_to_entries,
4044 std::vector<PairEntries>* delivery_to_entries) {
4045 priority_queue->Remove(entry);
4046 if (entry->pickup_insert_after() != -1) {
4047 pickup_to_entries->at(entry->pickup_insert_after()).erase(entry);
4049 if (entry->delivery_insert_after() != -1) {
4050 delivery_to_entries->at(entry->delivery_insert_after()).erase(entry);
4055 void GlobalCheapestInsertionFilteredHeuristic::InitializePositions(
4056 const std::vector<int>& nodes,
4058 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4059 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4060 position_to_node_entries,
4061 const absl::flat_hash_set<int>& vehicles) {
4062 priority_queue->Clear();
4063 position_to_node_entries->clear();
4064 position_to_node_entries->resize(
model()->
Size());
4066 const int num_vehicles =
4069 for (
int node : nodes) {
4075 FLAGS_routing_shift_insertion_cost_by_penalty ?
kint64max : 0;
4078 NodeEntry*
const node_entry =
new NodeEntry(node, -1, -1);
4079 if (FLAGS_routing_shift_insertion_cost_by_penalty ||
4080 num_vehicles <
model()->vehicles()) {
4083 node_entry->set_value(0);
4084 penalty = node_penalty;
4086 node_entry->set_value(node_penalty);
4089 priority_queue->Add(node_entry);
4092 InitializeInsertionEntriesPerformingNode(
4093 node, penalty, vehicles, priority_queue, position_to_node_entries);
4097 void GlobalCheapestInsertionFilteredHeuristic::
4098 InitializeInsertionEntriesPerformingNode(
4099 int64 node,
int64 penalty,
const absl::flat_hash_set<int>& vehicles,
4101 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>*
4103 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4104 position_to_node_entries) {
4105 const int num_vehicles =
4108 auto vehicles_it = vehicles.begin();
4109 for (
int v = 0; v < num_vehicles; v++) {
4110 const int vehicle = vehicles.empty() ? v : *vehicles_it++;
4112 std::vector<ValuedPosition> valued_positions;
4116 for (
const std::pair<int64, int64>& valued_position : valued_positions) {
4117 NodeEntry*
const node_entry =
4118 new NodeEntry(node, valued_position.second, vehicle);
4119 node_entry->set_value(
CapSub(valued_position.first, penalty));
4120 position_to_node_entries->at(valued_position.second).insert(node_entry);
4121 priority_queue->Add(node_entry);
4129 absl::flat_hash_set<int> vehicles_to_consider;
4130 const bool all_vehicles = (num_vehicles ==
model()->
vehicles());
4131 const auto insert_on_vehicle_for_cost_class = [
this, &vehicles, all_vehicles](
4132 int v,
int cost_class) {
4134 (all_vehicles || vehicles.contains(v));
4138 for (
const std::vector<int64>*
const neighbors :
4139 GetNeighborsOfNodeForCostClass(cost_class, node)) {
4140 for (
const int64 insert_after : *neighbors) {
4144 const int vehicle = node_index_to_vehicle_[insert_after];
4145 if (!insert_on_vehicle_for_cost_class(vehicle, cost_class)) {
4148 NodeEntry*
const node_entry =
4149 new NodeEntry(node, insert_after, vehicle);
4150 node_entry->set_value(
4152 node, insert_after,
Value(insert_after), vehicle),
4154 position_to_node_entries->at(insert_after).insert(node_entry);
4155 priority_queue->Add(node_entry);
4161 void GlobalCheapestInsertionFilteredHeuristic::UpdatePositions(
4162 const std::vector<int>& nodes,
int vehicle,
int64 insert_after,
4164 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4165 std::vector<GlobalCheapestInsertionFilteredHeuristic::NodeEntries>*
4170 if (node_entries->at(insert_after).empty()) {
4173 for (
int node_to_insert : nodes) {
4175 IsNeighborForCostClass(cost_class, insert_after, node_to_insert)) {
4176 NodeEntry*
const node_entry =
4177 new NodeEntry(node_to_insert, insert_after, vehicle);
4178 node_entries->at(insert_after).insert(node_entry);
4182 std::vector<NodeEntry*> to_remove;
4183 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
4184 if (priority_queue->Contains(node_entry)) {
4185 DCHECK_EQ(node_entry->insert_after(), insert_after);
4186 if (
Contains(node_entry->node_to_insert())) {
4187 to_remove.push_back(node_entry);
4191 for (NodeEntry*
const node_entry : to_remove) {
4192 DeleteNodeEntry(node_entry, priority_queue, node_entries);
4198 DCHECK_GE(
model()->
Size(), node_entries->at(insert_after).size());
4199 const int64 insert_before =
Value(insert_after);
4200 for (NodeEntry*
const node_entry : node_entries->at(insert_after)) {
4201 DCHECK_EQ(node_entry->insert_after(), insert_after);
4203 node_entry->node_to_insert(), insert_after, insert_before, vehicle);
4204 const int64 penalty =
4205 FLAGS_routing_shift_insertion_cost_by_penalty
4210 priority_queue->NoteChangedPriority(node_entry);
4212 priority_queue->Add(node_entry);
4217 void GlobalCheapestInsertionFilteredHeuristic::DeleteNodeEntry(
4218 GlobalCheapestInsertionFilteredHeuristic::NodeEntry* entry,
4220 GlobalCheapestInsertionFilteredHeuristic::NodeEntry>* priority_queue,
4221 std::vector<NodeEntries>* node_entries) {
4222 priority_queue->Remove(entry);
4223 if (entry->insert_after() != -1) {
4224 node_entries->at(entry->insert_after()).erase(entry);
4239 std::iota(std::begin(all_vehicles), std::end(all_vehicles), 0);
4241 start_end_distances_per_node_ =
4247 std::vector<bool> visited(
model()->
Size(),
false);
4249 std::vector<int64> insertion_positions;
4252 std::vector<int64> delivery_insertion_positions;
4256 for (
const auto& index_pair : index_pairs) {
4257 for (
int64 pickup : index_pair.first) {
4261 for (
int64 delivery : index_pair.second) {
4268 visited[pickup] =
true;
4269 visited[delivery] =
true;
4270 ComputeEvaluatorSortedPositions(pickup, &insertion_positions);
4271 for (
const int64 pickup_insertion : insertion_positions) {
4272 const int pickup_insertion_next =
Value(pickup_insertion);
4273 ComputeEvaluatorSortedPositionsOnRouteAfter(
4274 delivery, pickup, pickup_insertion_next,
4275 &delivery_insertion_positions);
4277 for (
const int64 delivery_insertion : delivery_insertion_positions) {
4278 InsertBetween(pickup, pickup_insertion, pickup_insertion_next);
4279 const int64 delivery_insertion_next =
4280 (delivery_insertion == pickup_insertion) ? pickup
4281 : (delivery_insertion == pickup) ? pickup_insertion_next
4282 :
Value(delivery_insertion);
4284 delivery_insertion_next);
4298 std::priority_queue<Seed> node_queue;
4301 while (!node_queue.empty()) {
4302 const int node = node_queue.top().second;
4304 if (
Contains(node) || visited[node])
continue;
4305 ComputeEvaluatorSortedPositions(node, &insertion_positions);
4306 for (
const int64 insertion : insertion_positions) {
4318 void LocalCheapestInsertionFilteredHeuristic::ComputeEvaluatorSortedPositions(
4319 int64 node, std::vector<int64>* sorted_positions) {
4320 CHECK(sorted_positions !=
nullptr);
4322 sorted_positions->clear();
4325 std::vector<std::pair<int64, int64>> valued_positions;
4326 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
4331 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
4335 void LocalCheapestInsertionFilteredHeuristic::
4336 ComputeEvaluatorSortedPositionsOnRouteAfter(
4338 std::vector<int64>* sorted_positions) {
4339 CHECK(sorted_positions !=
nullptr);
4341 sorted_positions->clear();
4345 std::vector<std::pair<int64, int64>> valued_positions;
4348 SortAndExtractPairSeconds(&valued_positions, sorted_positions);
4361 std::vector<std::vector<int64>> deliveries(
Size());
4362 std::vector<std::vector<int64>> pickups(
Size());
4364 for (
int first : pair.first) {
4365 for (
int second : pair.second) {
4366 deliveries[first].push_back(second);
4367 pickups[second].push_back(first);
4374 std::vector<int> sorted_vehicles(
model()->vehicles(), 0);
4375 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
4376 sorted_vehicles[vehicle] = vehicle;
4378 std::sort(sorted_vehicles.begin(), sorted_vehicles.end(),
4379 PartialRoutesAndLargeVehicleIndicesFirst(*
this));
4381 for (
const int vehicle : sorted_vehicles) {
4383 bool extend_route =
true;
4388 while (extend_route) {
4389 extend_route =
false;
4400 std::vector<int64> neighbors;
4402 std::unique_ptr<IntVarIterator> it(
4403 model()->Nexts()[
index]->MakeDomainIterator(
false));
4404 auto next_values = InitAndGetValues(it.get());
4405 neighbors = GetPossibleNextsFromIterator(
index, next_values.begin(),
4408 for (
int i = 0; !found && i < neighbors.size(); ++i) {
4412 next = FindTopSuccessor(
index, neighbors);
4415 SortSuccessors(
index, &neighbors);
4416 ABSL_FALLTHROUGH_INTENDED;
4418 next = neighbors[i];
4425 bool contains_pickups =
false;
4428 contains_pickups =
true;
4432 if (!contains_pickups) {
4436 std::vector<int64> next_deliveries;
4437 if (
next < deliveries.size()) {
4438 next_deliveries = GetPossibleNextsFromIterator(
4439 next, deliveries[
next].begin(), deliveries[
next].end());
4441 if (next_deliveries.empty()) next_deliveries = {
kUnassigned};
4442 for (
int j = 0; !found && j < next_deliveries.size(); ++j) {
4447 delivery = FindTopSuccessor(
next, next_deliveries);
4450 SortSuccessors(
next, &next_deliveries);
4451 ABSL_FALLTHROUGH_INTENDED;
4453 delivery = next_deliveries[j];
4471 if (
model()->IsEnd(end) && last_node != delivery) {
4472 last_node = delivery;
4473 extend_route =
true;
4488 bool CheapestAdditionFilteredHeuristic::
4489 PartialRoutesAndLargeVehicleIndicesFirst::operator()(
int vehicle1,
4490 int vehicle2)
const {
4491 const bool has_partial_route1 = (builder_.model()->Start(vehicle1) !=
4492 builder_.GetStartChainEnd(vehicle1));
4493 const bool has_partial_route2 = (builder_.model()->Start(vehicle2) !=
4494 builder_.GetStartChainEnd(vehicle2));
4495 if (has_partial_route1 == has_partial_route2) {
4496 return vehicle2 < vehicle1;
4498 return has_partial_route2 < has_partial_route1;
4511 int64 EvaluatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
4512 int64 node,
const std::vector<int64>& successors) {
4514 int64 best_successor = -1;
4515 for (
int64 successor : successors) {
4516 const int64 evaluation =
4517 (successor >= 0) ? evaluator_(node, successor) :
kint64max;
4518 if (evaluation < best_evaluation ||
4519 (evaluation == best_evaluation && successor > best_successor)) {
4520 best_evaluation = evaluation;
4521 best_successor = successor;
4524 return best_successor;
4527 void EvaluatorCheapestAdditionFilteredHeuristic::SortSuccessors(
4528 int64 node, std::vector<int64>* successors) {
4529 std::vector<std::pair<int64, int64>> values;
4530 values.reserve(successors->size());
4531 for (
int64 successor : *successors) {
4534 values.push_back({evaluator_(node, successor), -successor});
4536 std::sort(values.begin(), values.end());
4537 successors->clear();
4538 for (
auto value : values) {
4539 successors->push_back(-
value.second);
4550 comparator_(std::move(comparator)) {}
4552 int64 ComparatorCheapestAdditionFilteredHeuristic::FindTopSuccessor(
4553 int64 node,
const std::vector<int64>& successors) {
4554 return *std::min_element(successors.begin(), successors.end(),
4555 [
this, node](
int successor1,
int successor2) {
4556 return comparator_(node, successor1, successor2);
4560 void ComparatorCheapestAdditionFilteredHeuristic::SortSuccessors(
4561 int64 node, std::vector<int64>* successors) {
4562 std::sort(successors->begin(), successors->end(),
4563 [
this, node](
int successor1,
int successor2) {
4564 return comparator_(node, successor1, successor2);
4616 template <
typename Saving>
4617 class SavingsFilteredHeuristic::SavingsContainer {
4621 : savings_db_(savings_db),
4622 vehicle_types_(vehicle_types),
4623 index_in_sorted_savings_(0),
4624 single_vehicle_type_(vehicle_types == 1),
4625 using_incoming_reinjected_saving_(false),
4630 sorted_savings_per_vehicle_type_.clear();
4631 sorted_savings_per_vehicle_type_.resize(vehicle_types_);
4632 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
4633 savings.reserve(size * saving_neighbors);
4636 sorted_savings_.clear();
4637 costs_and_savings_per_arc_.clear();
4638 arc_indices_per_before_node_.clear();
4640 if (!single_vehicle_type_) {
4641 costs_and_savings_per_arc_.reserve(size * saving_neighbors);
4642 arc_indices_per_before_node_.resize(size);
4643 for (
int before_node = 0; before_node < size; before_node++) {
4644 arc_indices_per_before_node_[before_node].reserve(saving_neighbors);
4647 skipped_savings_starting_at_.clear();
4648 skipped_savings_starting_at_.resize(size);
4649 skipped_savings_ending_at_.clear();
4650 skipped_savings_ending_at_.resize(size);
4651 incoming_reinjected_savings_ =
nullptr;
4652 outgoing_reinjected_savings_ =
nullptr;
4653 incoming_new_reinjected_savings_ =
nullptr;
4654 outgoing_new_reinjected_savings_ =
nullptr;
4658 int64 after_node,
int vehicle_type) {
4659 CHECK(!sorted_savings_per_vehicle_type_.empty())
4660 <<
"Container not initialized!";
4661 sorted_savings_per_vehicle_type_[vehicle_type].push_back(saving);
4662 UpdateArcIndicesCostsAndSavings(before_node, after_node,
4663 {total_cost, saving});
4667 CHECK(!sorted_) <<
"Container already sorted!";
4669 for (std::vector<Saving>& savings : sorted_savings_per_vehicle_type_) {
4670 std::sort(savings.begin(), savings.end());
4673 if (single_vehicle_type_) {
4674 const auto& savings = sorted_savings_per_vehicle_type_[0];
4675 sorted_savings_.resize(savings.size());
4676 std::transform(savings.begin(), savings.end(), sorted_savings_.begin(),
4677 [](
const Saving& saving) {
4678 return SavingAndArc({saving, -1});
4684 sorted_savings_.reserve(vehicle_types_ *
4685 costs_and_savings_per_arc_.size());
4687 for (
int arc_index = 0; arc_index < costs_and_savings_per_arc_.size();
4689 std::vector<std::pair<int64, Saving>>& costs_and_savings =
4690 costs_and_savings_per_arc_[arc_index];
4691 DCHECK(!costs_and_savings.empty());
4694 costs_and_savings.begin(), costs_and_savings.end(),
4695 [](
const std::pair<int64, Saving>& cs1,
4696 const std::pair<int64, Saving>& cs2) { return cs1 > cs2; });
4701 const int64 cost = costs_and_savings.back().first;
4702 while (!costs_and_savings.empty() &&
4703 costs_and_savings.back().first ==
cost) {
4704 sorted_savings_.push_back(
4705 {costs_and_savings.back().second, arc_index});
4706 costs_and_savings.pop_back();
4709 std::sort(sorted_savings_.begin(), sorted_savings_.end());
4710 next_saving_type_and_index_for_arc_.clear();
4711 next_saving_type_and_index_for_arc_.resize(
4712 costs_and_savings_per_arc_.size(), {-1, -1});
4715 index_in_sorted_savings_ = 0;
4720 return index_in_sorted_savings_ < sorted_savings_.size() ||
4721 HasReinjectedSavings();
4725 CHECK(sorted_) <<
"Calling GetSaving() before Sort() !";
4727 <<
"Update() should be called between two calls to GetSaving() !";
4731 if (HasReinjectedSavings()) {
4732 if (incoming_reinjected_savings_ !=
nullptr &&
4733 outgoing_reinjected_savings_ !=
nullptr) {
4735 SavingAndArc& incoming_saving = incoming_reinjected_savings_->front();
4736 SavingAndArc& outgoing_saving = outgoing_reinjected_savings_->front();
4737 if (incoming_saving < outgoing_saving) {
4738 current_saving_ = incoming_saving;
4739 using_incoming_reinjected_saving_ =
true;
4741 current_saving_ = outgoing_saving;
4742 using_incoming_reinjected_saving_ =
false;
4745 if (incoming_reinjected_savings_ !=
nullptr) {
4746 current_saving_ = incoming_reinjected_savings_->front();
4747 using_incoming_reinjected_saving_ =
true;
4749 if (outgoing_reinjected_savings_ !=
nullptr) {
4750 current_saving_ = outgoing_reinjected_savings_->front();
4751 using_incoming_reinjected_saving_ =
false;
4755 current_saving_ = sorted_savings_[index_in_sorted_savings_];
4757 return current_saving_.saving;
4760 void Update(
bool update_best_saving,
int type = -1) {
4761 CHECK(to_update_) <<
"Container already up to date!";
4762 if (update_best_saving) {
4763 const int64 arc_index = current_saving_.arc_index;
4764 UpdateNextAndSkippedSavingsForArcWithType(arc_index, type);
4766 if (!HasReinjectedSavings()) {
4767 index_in_sorted_savings_++;
4769 if (index_in_sorted_savings_ == sorted_savings_.size()) {
4770 sorted_savings_.swap(next_savings_);
4772 index_in_sorted_savings_ = 0;
4774 std::sort(sorted_savings_.begin(), sorted_savings_.end());
4775 next_saving_type_and_index_for_arc_.clear();
4776 next_saving_type_and_index_for_arc_.resize(
4777 costs_and_savings_per_arc_.size(), {-1, -1});
4780 UpdateReinjectedSavings();
4785 CHECK(!single_vehicle_type_);
4786 Update(
true, type);
4790 CHECK(sorted_) <<
"Savings not sorted yet!";
4791 CHECK_LT(type, vehicle_types_);
4792 return sorted_savings_per_vehicle_type_[type];
4796 CHECK(outgoing_new_reinjected_savings_ ==
nullptr);
4797 outgoing_new_reinjected_savings_ = &(skipped_savings_starting_at_[node]);
4801 CHECK(incoming_new_reinjected_savings_ ==
nullptr);
4802 incoming_new_reinjected_savings_ = &(skipped_savings_ending_at_[node]);
4806 struct SavingAndArc {
4810 bool operator<(
const SavingAndArc& other)
const {
4811 return std::tie(saving, arc_index) <
4812 std::tie(other.saving, other.arc_index);
4818 void SkipSavingForArc(
const SavingAndArc& saving_and_arc) {
4819 const Saving& saving = saving_and_arc.saving;
4820 const int64 before_node = savings_db_->GetBeforeNodeFromSaving(saving);
4821 const int64 after_node = savings_db_->GetAfterNodeFromSaving(saving);
4822 if (!savings_db_->Contains(before_node)) {
4823 skipped_savings_starting_at_[before_node].push_back(saving_and_arc);
4825 if (!savings_db_->Contains(after_node)) {
4826 skipped_savings_ending_at_[after_node].push_back(saving_and_arc);
4840 void UpdateNextAndSkippedSavingsForArcWithType(
int64 arc_index,
int type) {
4841 if (single_vehicle_type_) {
4844 SkipSavingForArc(current_saving_);
4847 CHECK_GE(arc_index, 0);
4848 auto& type_and_index = next_saving_type_and_index_for_arc_[arc_index];
4849 const int previous_index = type_and_index.second;
4850 const int previous_type = type_and_index.first;
4851 bool next_saving_added =
false;
4854 if (previous_index >= 0) {
4856 DCHECK_GE(previous_type, 0);
4857 if (type == -1 || previous_type == type) {
4860 next_saving_added =
true;
4861 next_saving = next_savings_[previous_index].saving;
4865 if (!next_saving_added &&
4866 GetNextSavingForArcWithType(arc_index, type, &next_saving)) {
4867 type_and_index.first = savings_db_->GetVehicleTypeFromSaving(next_saving);
4868 if (previous_index >= 0) {
4870 next_savings_[previous_index] = {next_saving, arc_index};
4873 type_and_index.second = next_savings_.size();
4874 next_savings_.push_back({next_saving, arc_index});
4876 next_saving_added =
true;
4882 SkipSavingForArc(current_saving_);
4886 if (next_saving_added) {
4887 SkipSavingForArc({next_saving, arc_index});
4892 void UpdateReinjectedSavings() {
4893 UpdateGivenReinjectedSavings(incoming_new_reinjected_savings_,
4894 &incoming_reinjected_savings_,
4895 using_incoming_reinjected_saving_);
4896 UpdateGivenReinjectedSavings(outgoing_new_reinjected_savings_,
4897 &outgoing_reinjected_savings_,
4898 !using_incoming_reinjected_saving_);
4899 incoming_new_reinjected_savings_ =
nullptr;
4900 outgoing_new_reinjected_savings_ =
nullptr;
4903 void UpdateGivenReinjectedSavings(
4904 std::deque<SavingAndArc>* new_reinjected_savings,
4905 std::deque<SavingAndArc>** reinjected_savings,
4906 bool using_reinjected_savings) {
4907 if (new_reinjected_savings ==
nullptr) {
4909 if (*reinjected_savings !=
nullptr && using_reinjected_savings) {
4910 CHECK(!(*reinjected_savings)->empty());
4911 (*reinjected_savings)->pop_front();
4912 if ((*reinjected_savings)->empty()) {
4913 *reinjected_savings =
nullptr;
4922 if (*reinjected_savings !=
nullptr) {
4923 (*reinjected_savings)->clear();
4925 *reinjected_savings =
nullptr;
4926 if (!new_reinjected_savings->empty()) {
4927 *reinjected_savings = new_reinjected_savings;
4931 bool HasReinjectedSavings() {
4932 return outgoing_reinjected_savings_ !=
nullptr ||
4933 incoming_reinjected_savings_ !=
nullptr;
4936 void UpdateArcIndicesCostsAndSavings(
4938 const std::pair<int64, Saving>& cost_and_saving) {
4939 if (single_vehicle_type_) {
4942 absl::flat_hash_map<int, int>& arc_indices =
4943 arc_indices_per_before_node_[before_node];
4944 const auto& arc_inserted = arc_indices.insert(
4945 std::make_pair(after_node, costs_and_savings_per_arc_.size()));
4946 const int index = arc_inserted.first->second;
4947 if (arc_inserted.second) {
4948 costs_and_savings_per_arc_.push_back({cost_and_saving});
4950 DCHECK_LT(
index, costs_and_savings_per_arc_.size());
4951 costs_and_savings_per_arc_[
index].push_back(cost_and_saving);
4955 bool GetNextSavingForArcWithType(
int64 arc_index,
int type,
4956 Saving* next_saving) {
4957 std::vector<std::pair<int64, Saving>>& costs_and_savings =
4958 costs_and_savings_per_arc_[arc_index];
4960 bool found_saving =
false;
4961 while (!costs_and_savings.empty() && !found_saving) {
4962 const Saving& saving = costs_and_savings.back().second;
4963 if (type == -1 || savings_db_->GetVehicleTypeFromSaving(saving) == type) {
4964 *next_saving = saving;
4965 found_saving =
true;
4967 costs_and_savings.pop_back();
4969 return found_saving;
4972 const SavingsFilteredHeuristic*
const savings_db_;
4973 const int vehicle_types_;
4974 int64 index_in_sorted_savings_;
4975 std::vector<std::vector<Saving>> sorted_savings_per_vehicle_type_;
4976 std::vector<SavingAndArc> sorted_savings_;
4977 std::vector<SavingAndArc> next_savings_;
4978 std::vector<std::pair< int,
int>>
4979 next_saving_type_and_index_for_arc_;
4980 SavingAndArc current_saving_;
4981 const bool single_vehicle_type_;
4982 std::vector<std::vector<std::pair<
int64, Saving>>>
4983 costs_and_savings_per_arc_;
4984 std::vector<absl::flat_hash_map< int,
int>>
4985 arc_indices_per_before_node_;
4986 std::vector<std::deque<SavingAndArc>> skipped_savings_starting_at_;
4987 std::vector<std::deque<SavingAndArc>> skipped_savings_ending_at_;
4988 std::deque<SavingAndArc>* outgoing_reinjected_savings_;
4989 std::deque<SavingAndArc>* incoming_reinjected_savings_;
4990 bool using_incoming_reinjected_saving_;
4991 std::deque<SavingAndArc>* outgoing_new_reinjected_savings_;
4992 std::deque<SavingAndArc>* incoming_new_reinjected_savings_;
4999 SavingsFilteredHeuristic::SavingsFilteredHeuristic(
5003 vehicle_type_curator_(nullptr),
5011 size_squared_ = size * size;
5019 model()->GetVehicleTypeContainer());
5031 int type,
int64 before_node,
int64 after_node) {
5032 auto vehicle_is_compatible = [
this, before_node, after_node](
int vehicle) {
5047 type, vehicle_is_compatible);
5051 void SavingsFilteredHeuristic::AddSymmetricArcsToAdjacencyLists(
5052 std::vector<std::vector<int64>>* adjacency_lists) {
5053 for (
int64 node = 0; node < adjacency_lists->size(); node++) {
5054 for (
int64 neighbor : (*adjacency_lists)[node]) {
5055 if (
model()->IsStart(neighbor) ||
model()->IsEnd(neighbor)) {
5058 (*adjacency_lists)[neighbor].push_back(node);
5061 std::transform(adjacency_lists->begin(), adjacency_lists->end(),
5062 adjacency_lists->begin(), [](std::vector<int64> vec) {
5063 std::sort(vec.begin(), vec.end());
5064 vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
5080 void SavingsFilteredHeuristic::ComputeSavings() {
5084 std::vector<int64> uncontained_non_start_end_nodes;
5085 uncontained_non_start_end_nodes.reserve(size);
5086 for (
int node = 0; node < size; node++) {
5088 uncontained_non_start_end_nodes.push_back(node);
5092 const int64 saving_neighbors =
5093 std::min(MaxNumNeighborsPerNode(num_vehicle_types),
5094 static_cast<int64>(uncontained_non_start_end_nodes.size()));
5097 absl::make_unique<SavingsContainer<Saving>>(
this, num_vehicle_types);
5100 std::vector<std::vector<int64>> adjacency_lists(size);
5102 for (
int type = 0; type < num_vehicle_types; ++type) {
5108 const int64 cost_class =
5116 for (
int before_node : uncontained_non_start_end_nodes) {
5117 std::vector<std::pair<
int64,
int64>> costed_after_nodes;
5118 costed_after_nodes.reserve(uncontained_non_start_end_nodes.size());
5119 for (
int after_node : uncontained_non_start_end_nodes) {
5120 if (after_node != before_node) {
5121 costed_after_nodes.push_back(std::make_pair(
5122 model()->GetArcCostForClass(before_node, after_node, cost_class),
5126 if (saving_neighbors < costed_after_nodes.size()) {
5127 std::nth_element(costed_after_nodes.begin(),
5128 costed_after_nodes.begin() + saving_neighbors,
5129 costed_after_nodes.end());
5130 costed_after_nodes.resize(saving_neighbors);
5132 adjacency_lists[before_node].resize(costed_after_nodes.size());
5133 std::transform(costed_after_nodes.begin(), costed_after_nodes.end(),
5134 adjacency_lists[before_node].begin(),
5135 [](std::pair<int64, int64> cost_and_node) {
5136 return cost_and_node.second;
5140 AddSymmetricArcsToAdjacencyLists(&adjacency_lists);
5144 for (
int before_node : uncontained_non_start_end_nodes) {
5145 const int64 before_to_end_cost =
5147 const int64 start_to_before_cost =
5148 CapSub(
model()->GetArcCostForClass(start, before_node, cost_class),
5150 for (
int64 after_node : adjacency_lists[before_node]) {
5151 if (
model()->IsStart(after_node) ||
model()->IsEnd(after_node) ||
5152 before_node == after_node ||
Contains(after_node)) {
5155 const int64 arc_cost =
5157 const int64 start_to_after_cost =
5158 CapSub(
model()->GetArcCostForClass(start, after_node, cost_class),
5160 const int64 after_to_end_cost =
5163 const double weighted_arc_cost_fp =
5165 const int64 weighted_arc_cost =
5167 ?
static_cast<int64>(weighted_arc_cost_fp)
5170 CapAdd(before_to_end_cost, start_to_after_cost), weighted_arc_cost);
5173 BuildSaving(-saving_value, type, before_node, after_node);
5175 const int64 total_cost =
5176 CapAdd(
CapAdd(start_to_before_cost, arc_cost), after_to_end_cost);
5186 int64 SavingsFilteredHeuristic::MaxNumNeighborsPerNode(
5187 int num_vehicle_types)
const {
5190 const int64 num_neighbors_with_ratio =
5197 const double max_memory_usage_in_savings_unit =
5215 if (num_vehicle_types > 1) {
5216 multiplicative_factor += 1.5;
5218 const double num_savings =
5219 max_memory_usage_in_savings_unit / multiplicative_factor;
5220 const int64 num_neighbors_with_memory_restriction =
5221 std::max(1.0, num_savings / (num_vehicle_types * size));
5223 return std::min(num_neighbors_with_ratio,
5224 num_neighbors_with_memory_restriction);
5229 void SequentialSavingsFilteredHeuristic::BuildRoutesFromSavings() {
5231 DCHECK_GT(vehicle_types, 0);
5235 std::vector<std::vector<const Saving*>> in_savings_ptr(size * vehicle_types);
5236 std::vector<std::vector<const Saving*>> out_savings_ptr(size * vehicle_types);
5237 for (
int type = 0; type < vehicle_types; type++) {
5238 const int vehicle_type_offset = type * size;
5239 const std::vector<Saving>& sorted_savings_for_type =
5241 for (
const Saving& saving : sorted_savings_for_type) {
5244 in_savings_ptr[vehicle_type_offset + before_node].push_back(&saving);
5246 out_savings_ptr[vehicle_type_offset + after_node].push_back(&saving);
5256 const bool nodes_not_contained =
5259 bool committed =
false;
5261 if (nodes_not_contained) {
5274 const int saving_offset = type * size;
5276 while (in_index < in_savings_ptr[saving_offset + after_node].size() ||
5278 out_savings_ptr[saving_offset + before_node].size()) {
5281 int before_before_node = -1;
5282 int after_after_node = -1;
5283 if (in_index < in_savings_ptr[saving_offset + after_node].size()) {
5284 const Saving& in_saving =
5285 *(in_savings_ptr[saving_offset + after_node][in_index]);
5287 out_savings_ptr[saving_offset + before_node].size()) {
5288 const Saving& out_saving =
5289 *(out_savings_ptr[saving_offset + before_node][out_index]);
5304 *(out_savings_ptr[saving_offset + before_node][out_index]));
5307 if (after_after_node != -1) {
5308 DCHECK_EQ(before_before_node, -1);
5311 SetValue(after_node, after_after_node);
5315 after_node = after_after_node;
5324 CHECK_GE(before_before_node, 0);
5325 if (!
Contains(before_before_node)) {
5326 SetValue(start, before_before_node);
5327 SetValue(before_before_node, before_node);
5330 before_node = before_before_node;
5347 void ParallelSavingsFilteredHeuristic::BuildRoutesFromSavings() {
5353 first_node_on_route_.resize(vehicles, -1);
5354 last_node_on_route_.resize(vehicles, -1);
5355 vehicle_of_first_or_last_node_.resize(size, -1);
5357 for (
int vehicle = 0; vehicle < vehicles; vehicle++) {
5365 vehicle_of_first_or_last_node_[node] = vehicle;
5366 first_node_on_route_[vehicle] = node;
5369 while (
next != end) {
5373 vehicle_of_first_or_last_node_[node] = vehicle;
5374 last_node_on_route_[vehicle] = node;
5387 bool committed =
false;
5395 vehicle_of_first_or_last_node_[before_node] = vehicle;
5396 vehicle_of_first_or_last_node_[after_node] = vehicle;
5397 first_node_on_route_[vehicle] = before_node;
5398 last_node_on_route_[vehicle] = after_node;
5410 const int v1 = vehicle_of_first_or_last_node_[before_node];
5411 const int64 last_node = v1 == -1 ? -1 : last_node_on_route_[v1];
5413 const int v2 = vehicle_of_first_or_last_node_[after_node];
5414 const int64 first_node = v2 == -1 ? -1 : first_node_on_route_[v2];
5416 if (before_node == last_node && after_node == first_node && v1 != v2 &&
5418 CHECK_EQ(
Value(before_node),
model()->End(v1));
5419 CHECK_EQ(
Value(
model()->Start(v2)), after_node);
5425 MergeRoutes(v1, v2, before_node, after_node);
5430 const int vehicle = vehicle_of_first_or_last_node_[before_node];
5431 const int64 last_node = vehicle == -1 ? -1 : last_node_on_route_[vehicle];
5433 if (before_node == last_node) {
5435 CHECK_EQ(
Value(before_node), end);
5438 if (type != route_type) {
5449 if (first_node_on_route_[vehicle] != before_node) {
5451 DCHECK_NE(
Value(
model()->Start(vehicle)), before_node);
5452 vehicle_of_first_or_last_node_[before_node] = -1;
5454 vehicle_of_first_or_last_node_[after_node] = vehicle;
5455 last_node_on_route_[vehicle] = after_node;
5462 const int vehicle = vehicle_of_first_or_last_node_[after_node];
5463 const int64 first_node =
5464 vehicle == -1 ? -1 : first_node_on_route_[vehicle];
5466 if (after_node == first_node) {
5468 CHECK_EQ(
Value(start), after_node);
5471 if (type != route_type) {
5482 if (last_node_on_route_[vehicle] != after_node) {
5484 DCHECK_NE(
Value(after_node),
model()->End(vehicle));
5485 vehicle_of_first_or_last_node_[after_node] = -1;
5487 vehicle_of_first_or_last_node_[before_node] = vehicle;
5488 first_node_on_route_[vehicle] = before_node;
5497 void ParallelSavingsFilteredHeuristic::MergeRoutes(
int first_vehicle,
5502 const int64 new_first_node = first_node_on_route_[first_vehicle];
5503 DCHECK_EQ(vehicle_of_first_or_last_node_[new_first_node], first_vehicle);
5504 CHECK_EQ(
Value(
model()->Start(first_vehicle)), new_first_node);
5505 const int64 new_last_node = last_node_on_route_[second_vehicle];
5506 DCHECK_EQ(vehicle_of_first_or_last_node_[new_last_node], second_vehicle);
5507 CHECK_EQ(
Value(new_last_node),
model()->End(second_vehicle));
5510 int used_vehicle = first_vehicle;
5511 int unused_vehicle = second_vehicle;
5512 if (
model()->GetFixedCostOfVehicle(first_vehicle) >
5513 model()->GetFixedCostOfVehicle(second_vehicle)) {
5514 used_vehicle = second_vehicle;
5515 unused_vehicle = first_vehicle;
5520 if (used_vehicle == first_vehicle) {
5525 bool committed =
Commit();
5527 model()->GetVehicleClassIndexOfVehicle(first_vehicle).
value() !=
5528 model()->GetVehicleClassIndexOfVehicle(second_vehicle).
value()) {
5530 std::swap(used_vehicle, unused_vehicle);
5533 if (used_vehicle == first_vehicle) {
5544 model()->GetVehicleClassIndexOfVehicle(unused_vehicle).
value(),
5545 model()->GetFixedCostOfVehicle(unused_vehicle));
5548 first_node_on_route_[unused_vehicle] = -1;
5549 last_node_on_route_[unused_vehicle] = -1;
5550 vehicle_of_first_or_last_node_[before_node] = -1;
5551 vehicle_of_first_or_last_node_[after_node] = -1;
5552 first_node_on_route_[used_vehicle] = new_first_node;
5553 last_node_on_route_[used_vehicle] = new_last_node;
5554 vehicle_of_first_or_last_node_[new_last_node] = used_vehicle;
5555 vehicle_of_first_or_last_node_[new_first_node] = used_vehicle;
5562 bool use_minimum_matching)
5564 use_minimum_matching_(use_minimum_matching) {}
5574 std::vector<int> indices(1, 0);
5575 for (
int i = 1; i < size; ++i) {
5576 if (!
model()->IsStart(i) && !
model()->IsEnd(i)) {
5577 indices.push_back(i);
5581 std::vector<std::vector<int>> path_per_cost_class(num_cost_classes);
5582 std::vector<bool> class_covered(num_cost_classes,
false);
5583 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
5584 const int64 cost_class =
5586 if (!class_covered[cost_class]) {
5587 class_covered[cost_class] =
true;
5590 auto cost = [
this, &indices, start, end, cost_class](
int from,
int to) {
5591 DCHECK_LT(from, indices.size());
5592 DCHECK_LT(to, indices.size());
5593 const int from_index = (from == 0) ? start : indices[from];
5594 const int to_index = (to == 0) ? end : indices[to];
5603 using Cost = decltype(
cost);
5605 indices.size(),
cost);
5606 if (use_minimum_matching_) {
5609 MINIMUM_WEIGHT_MATCHING);
5611 path_per_cost_class[cost_class] =
5616 for (
int vehicle = 0; vehicle <
model()->
vehicles(); ++vehicle) {
5617 const int64 cost_class =
5619 const std::vector<int>& path = path_per_cost_class[cost_class];
5620 DCHECK_EQ(0, path[0]);
5621 DCHECK_EQ(0, path.back());
5624 const int end =
model()->
End(vehicle);
5625 for (
int i = 1; i < path.size() - 1 && prev != end; ++i) {
5627 int next = indices[path[i]];
5643 class GuidedSlackFinalizer :
public DecisionBuilder {
5647 Decision* Next(Solver* solver)
override;
5651 int64 ChooseVariable();
5655 const std::function<
int64(
int64)> initializer_;
5656 RevArray<bool> is_initialized_;
5657 std::vector<int64> initial_values_;
5658 Rev<int64> current_index_;
5659 Rev<int64> current_route_;
5660 RevArray<int64> last_delta_used_;
5665 GuidedSlackFinalizer::GuidedSlackFinalizer(
5666 const RoutingDimension* dimension, RoutingModel*
model,
5670 initializer_(std::move(initializer)),
5671 is_initialized_(dimension->slacks().size(), false),
5672 initial_values_(dimension->slacks().size(),
kint64min),
5673 current_index_(model_->Start(0)),
5675 last_delta_used_(dimension->slacks().size(), 0) {}
5677 Decision* GuidedSlackFinalizer::Next(Solver* solver) {
5678 CHECK_EQ(solver, model_->
solver());
5679 const int node_idx = ChooseVariable();
5680 CHECK(node_idx == -1 ||
5681 (node_idx >= 0 && node_idx < dimension_->slacks().size()));
5682 if (node_idx != -1) {
5683 if (!is_initialized_[node_idx]) {
5684 initial_values_[node_idx] = initializer_(node_idx);
5685 is_initialized_.SetValue(solver, node_idx,
true);
5688 IntVar*
const slack_variable = dimension_->
SlackVar(node_idx);
5689 return solver->MakeAssignVariableValue(slack_variable,
value);
5695 const IntVar*
const slack_variable = dimension_->
SlackVar(
index);
5697 const int64 max_delta =
5698 std::max(center - slack_variable->Min(), slack_variable->Max() - center) +
5704 while (std::abs(
delta) < max_delta &&
5705 !slack_variable->Contains(center +
delta)) {
5713 return center +
delta;
5716 int64 GuidedSlackFinalizer::ChooseVariable() {
5717 int64 int_current_node = current_index_.Value();
5718 int64 int_current_route = current_route_.Value();
5720 while (int_current_route < model_->vehicles()) {
5721 while (!model_->
IsEnd(int_current_node) &&
5722 dimension_->
SlackVar(int_current_node)->Bound()) {
5723 int_current_node = model_->
NextVar(int_current_node)->Value();
5725 if (!model_->
IsEnd(int_current_node)) {
5728 int_current_route += 1;
5729 if (int_current_route < model_->vehicles()) {
5730 int_current_node = model_->
Start(int_current_route);
5734 CHECK(int_current_route == model_->
vehicles() ||
5735 !dimension_->
SlackVar(int_current_node)->Bound());
5736 current_index_.SetValue(model_->
solver(), int_current_node);
5737 current_route_.SetValue(model_->
solver(), int_current_route);
5738 if (int_current_route < model_->vehicles()) {
5739 return int_current_node;
5749 return solver_->RevAlloc(
5750 new GuidedSlackFinalizer(dimension,
this, std::move(initializer)));
5754 CHECK_EQ(base_dimension_,
this);
5755 CHECK(!model_->
IsEnd(node));
5768 state_dependent_class_evaluators_
5769 [state_dependent_vehicle_to_class_[serving_vehicle]])(
next,
5774 const int64 optimal_next_cumul =
5776 next_cumul_min, next_cumul_max + 1);
5778 DCHECK_LE(next_cumul_min, optimal_next_cumul);
5779 DCHECK_LE(optimal_next_cumul, next_cumul_max);
5786 class_evaluators_[vehicle_to_class_[serving_vehicle]])(node,
next);
5787 const int64 current_state_dependent_transit =
5790 state_dependent_class_evaluators_
5791 [state_dependent_vehicle_to_class_[serving_vehicle]])(node,
5793 .transit->Query(current_cumul);
5794 const int64 optimal_slack = optimal_next_cumul - current_cumul -
5795 current_state_independent_transit -
5796 current_state_dependent_transit;
5797 CHECK_LE(
SlackVar(node)->Min(), optimal_slack);
5798 CHECK_LE(optimal_slack,
SlackVar(node)->Max());
5799 return optimal_slack;
5805 explicit GreedyDescentLSOperator(std::vector<IntVar*> variables);
5807 bool MakeNextNeighbor(Assignment*
delta, Assignment* deltadelta)
override;
5808 void Start(
const Assignment* assignment)
override;
5811 int64 FindMaxDistanceToDomain(
const Assignment* assignment);
5813 const std::vector<IntVar*> variables_;
5814 const Assignment* center_;
5815 int64 current_step_;
5822 int64 current_direction_;
5827 GreedyDescentLSOperator::GreedyDescentLSOperator(std::vector<IntVar*> variables)
5828 : variables_(std::move(variables)),
5831 current_direction_(0) {}
5833 bool GreedyDescentLSOperator::MakeNextNeighbor(Assignment*
delta,
5835 static const int64 sings[] = {1, -1};
5836 for (; 1 <= current_step_; current_step_ /= 2) {
5837 for (; current_direction_ < 2 * variables_.size();) {
5838 const int64 variable_idx = current_direction_ / 2;
5839 IntVar*
const variable = variables_[variable_idx];
5840 const int64 sign_index = current_direction_ % 2;
5841 const int64 sign = sings[sign_index];
5842 const int64 offset = sign * current_step_;
5843 const int64 new_value = center_->Value(variable) + offset;
5844 ++current_direction_;
5845 if (variable->Contains(new_value)) {
5846 delta->Add(variable);
5847 delta->SetValue(variable, new_value);
5851 current_direction_ = 0;
5856 void GreedyDescentLSOperator::Start(
const Assignment* assignment) {
5857 CHECK(assignment !=
nullptr);
5858 current_step_ = FindMaxDistanceToDomain(assignment);
5859 center_ = assignment;
5862 int64 GreedyDescentLSOperator::FindMaxDistanceToDomain(
5863 const Assignment* assignment) {
5865 for (
const IntVar*
const var : variables_) {
5866 result =
std::max(result, std::abs(
var->Max() - assignment->Value(
var)));
5867 result =
std::max(result, std::abs(
var->Min() - assignment->Value(
var)));
5874 std::vector<IntVar*> variables) {
5875 return std::unique_ptr<LocalSearchOperator>(
5876 new GreedyDescentLSOperator(std::move(variables)));
5881 CHECK(dimension !=
nullptr);
5886 DecisionBuilder*
const guided_finalizer =
5888 DecisionBuilder*
const slacks_finalizer =
5889 solver_->MakeSolveOnce(guided_finalizer);
5890 std::vector<IntVar*> start_cumuls(vehicles_,
nullptr);
5891 for (
int64 vehicle_idx = 0; vehicle_idx < vehicles_; ++vehicle_idx) {
5892 start_cumuls[vehicle_idx] = dimension->
CumulVar(starts_[vehicle_idx]);
5895 solver_->RevAlloc(
new GreedyDescentLSOperator(start_cumuls));
5897 solver_->MakeLocalSearchPhaseParameters(
CostVar(), hill_climber,
5899 Assignment*
const first_solution = solver_->MakeAssignment();
5900 first_solution->Add(start_cumuls);
5901 for (IntVar*
const cumul : start_cumuls) {
5902 first_solution->SetValue(cumul, cumul->Min());
5904 DecisionBuilder*
const finalizer =
5905 solver_->MakeLocalSearchPhase(first_solution,
parameters);