34 #include "absl/container/btree_set.h"
35 #include "absl/container/flat_hash_map.h"
36 #include "absl/container/flat_hash_set.h"
37 #include "absl/flags/flag.h"
38 #include "absl/strings/string_view.h"
47 #include "ortools/constraint_solver/routing_parameters.pb.h"
54 "Run stronger checks in debug; these stronger tests might change "
55 "the complexity of the code in particular.");
63 class MaxActiveVehiclesFilter :
public IntVarLocalSearchFilter {
65 explicit MaxActiveVehiclesFilter(
const RoutingModel& routing_model)
66 : IntVarLocalSearchFilter(routing_model.Nexts()),
67 routing_model_(routing_model),
68 is_active_(routing_model.vehicles(), false),
69 active_vehicles_(0) {}
70 bool Accept(
const Assignment*
delta,
const Assignment* ,
71 int64_t , int64_t )
override {
74 const int delta_size = container.Size();
75 int current_active_vehicles = active_vehicles_;
76 for (
int i = 0; i < delta_size; ++i) {
77 const IntVarElement& new_element = container.Element(i);
78 IntVar*
const var = new_element.Var();
80 if (FindIndex(
var, &
index) && routing_model_.IsStart(
index)) {
81 if (new_element.Min() != new_element.Max()) {
85 const int vehicle = routing_model_.VehicleIndex(
index);
86 const bool is_active =
87 (new_element.Min() != routing_model_.End(vehicle));
88 if (is_active && !is_active_[vehicle]) {
89 ++current_active_vehicles;
90 }
else if (!is_active && is_active_[vehicle]) {
91 --current_active_vehicles;
95 return current_active_vehicles <=
96 routing_model_.GetMaximumNumberOfActiveVehicles();
100 void OnSynchronize(
const Assignment* )
override {
101 active_vehicles_ = 0;
102 for (
int i = 0; i < routing_model_.vehicles(); ++i) {
103 const int index = routing_model_.Start(i);
105 is_active_[i] =
true;
108 is_active_[i] =
false;
113 const RoutingModel& routing_model_;
114 std::vector<bool> is_active_;
115 int active_vehicles_;
122 new MaxActiveVehiclesFilter(routing_model));
128 class NodeDisjunctionFilter :
public IntVarLocalSearchFilter {
130 explicit NodeDisjunctionFilter(
const RoutingModel& routing_model,
132 : IntVarLocalSearchFilter(routing_model.Nexts()),
133 routing_model_(routing_model),
134 active_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
135 inactive_per_disjunction_(routing_model.GetNumberOfDisjunctions(), 0),
136 synchronized_objective_value_(std::numeric_limits<int64_t>::
min()),
137 accepted_objective_value_(std::numeric_limits<int64_t>::
min()),
138 filter_cost_(filter_cost),
139 has_mandatory_disjunctions_(routing_model.HasMandatoryDisjunctions()) {}
141 bool Accept(
const Assignment*
delta,
const Assignment* ,
142 int64_t , int64_t objective_max)
override {
145 const int delta_size = container.Size();
147 disjunction_active_deltas;
149 disjunction_inactive_deltas;
150 bool lns_detected =
false;
152 for (
int i = 0; i < delta_size; ++i) {
153 const IntVarElement& new_element = container.Element(i);
154 IntVar*
const var = new_element.Var();
157 const bool is_inactive =
158 (new_element.Min() <=
index && new_element.Max() >=
index);
159 if (new_element.Min() != new_element.Max()) {
163 routing_model_.GetDisjunctionIndices(
index)) {
164 const bool is_var_synced = IsVarSynced(
index);
167 : &disjunction_active_deltas,
168 disjunction_index, 0);
171 : &disjunction_inactive_deltas,
172 disjunction_index, 0);
179 for (
const auto [disjunction_index, active_nodes] :
180 disjunction_active_deltas) {
182 if (active_per_disjunction_[disjunction_index] + active_nodes >
183 routing_model_.GetDisjunctionMaxCardinality(disjunction_index)) {
187 if (lns_detected || (!filter_cost_ && !has_mandatory_disjunctions_)) {
188 accepted_objective_value_ = 0;
192 accepted_objective_value_ = synchronized_objective_value_;
193 for (
const auto [disjunction_index, inactive_nodes] :
194 disjunction_inactive_deltas) {
195 const int64_t penalty =
196 routing_model_.GetDisjunctionPenalty(disjunction_index);
197 if (penalty == 0)
continue;
198 const int current_inactive_nodes =
199 inactive_per_disjunction_[disjunction_index];
200 const int max_inactive_cardinality =
201 routing_model_.GetDisjunctionNodeIndices(disjunction_index).size() -
202 routing_model_.GetDisjunctionMaxCardinality(disjunction_index);
204 if (current_inactive_nodes + inactive_nodes > max_inactive_cardinality) {
209 }
else if (current_inactive_nodes <= max_inactive_cardinality) {
212 accepted_objective_value_ =
213 CapAdd(accepted_objective_value_, penalty);
215 }
else if (current_inactive_nodes > max_inactive_cardinality) {
218 accepted_objective_value_ =
CapSub(accepted_objective_value_, penalty);
222 return accepted_objective_value_ <= objective_max;
224 std::string DebugString()
const override {
return "NodeDisjunctionFilter"; }
225 int64_t GetSynchronizedObjectiveValue()
const override {
226 return synchronized_objective_value_;
228 int64_t GetAcceptedObjectiveValue()
const override {
229 return accepted_objective_value_;
233 void OnSynchronize(
const Assignment* )
override {
234 synchronized_objective_value_ = 0;
236 i < active_per_disjunction_.size(); ++i) {
237 active_per_disjunction_[i] = 0;
238 inactive_per_disjunction_[i] = 0;
239 const std::vector<int64_t>& disjunction_indices =
240 routing_model_.GetDisjunctionNodeIndices(i);
241 for (
const int64_t
index : disjunction_indices) {
242 if (IsVarSynced(
index)) {
244 ++active_per_disjunction_[i];
246 ++inactive_per_disjunction_[i];
250 if (!filter_cost_)
continue;
251 const int64_t penalty = routing_model_.GetDisjunctionPenalty(i);
252 const int max_cardinality =
253 routing_model_.GetDisjunctionMaxCardinality(i);
254 if (inactive_per_disjunction_[i] >
255 disjunction_indices.size() - max_cardinality &&
257 synchronized_objective_value_ =
258 CapAdd(synchronized_objective_value_, penalty);
263 const RoutingModel& routing_model_;
266 active_per_disjunction_;
268 inactive_per_disjunction_;
269 int64_t synchronized_objective_value_;
270 int64_t accepted_objective_value_;
271 const bool filter_cost_;
272 const bool has_mandatory_disjunctions_;
279 new NodeDisjunctionFilter(routing_model, filter_cost));
285 int next_domain_size)
288 paths_(nexts.size(), -1),
289 new_synchronized_unperformed_nodes_(nexts.size()),
291 touched_paths_(nexts.size()),
293 ranks_(next_domain_size, -1),
294 status_(BasePathFilter::UNKNOWN),
295 lns_detected_(
false) {}
299 int64_t objective_min, int64_t objective_max) {
301 lns_detected_ =
false;
302 for (
const int touched : delta_touched_) {
305 delta_touched_.clear();
307 const int delta_size = container.
Size();
308 delta_touched_.reserve(delta_size);
320 const auto update_touched_path_chain_start_end = [
this](int64_t
index) {
321 const int64_t
start = node_path_starts_[
index];
325 int64_t& chain_start = touched_path_chain_start_ends_[
start].first;
330 int64_t& chain_end = touched_path_chain_start_ends_[
start].second;
336 for (
int i = 0; i < delta_size; ++i) {
341 if (!new_element.
Bound()) {
343 lns_detected_ =
true;
347 delta_touched_.push_back(
index);
348 update_touched_path_chain_start_end(
index);
349 update_touched_path_chain_start_end(new_nexts_[
index]);
353 if (!InitializeAcceptPath())
return false;
355 const std::pair<int64_t, int64_t> start_end =
356 touched_path_chain_start_ends_[touched_start];
357 if (!AcceptPath(touched_start, start_end.first, start_end.second)) {
363 return FinalizeAcceptPath(objective_min, objective_max);
366 void BasePathFilter::ComputePathStarts(std::vector<int64_t>* path_starts,
367 std::vector<int>* index_to_path) {
368 path_starts->clear();
369 const int nexts_size =
Size();
372 for (
int i = 0; i < nexts_size; ++i) {
377 if (
next < nexts_size) {
382 for (
int i = 0; i < nexts_size; ++i) {
384 (*index_to_path)[i] = path_starts->size();
385 path_starts->push_back(i);
390 bool BasePathFilter::HavePathsChanged() {
391 std::vector<int64_t> path_starts;
393 ComputePathStarts(&path_starts, &index_to_path);
394 if (path_starts.size() != starts_.size()) {
397 for (
int i = 0; i < path_starts.size(); ++i) {
398 if (path_starts[i] != starts_[i]) {
402 for (
int i = 0; i <
Size(); ++i) {
403 if (index_to_path[i] != paths_[i]) {
410 void BasePathFilter::SynchronizeFullAssignment() {
414 ComputePathStarts(&starts_, &paths_);
419 new_synchronized_unperformed_nodes_.
Set(
index);
423 node_path_starts_.assign(node_path_starts_.size(),
kUnassigned);
425 const int nexts_size =
Size();
426 for (
const int64_t
start : starts_) {
428 node_path_starts_[node] =
start;
431 while (
next < nexts_size) {
433 node_path_starts_[node] =
start;
439 OnBeforeSynchronizePaths();
441 OnAfterSynchronizePaths();
445 if (status_ == BasePathFilter::UNKNOWN) {
447 DisableFiltering() ? BasePathFilter::DISABLED : BasePathFilter::ENABLED;
450 new_synchronized_unperformed_nodes_.
ClearAll();
451 if (
delta ==
nullptr ||
delta->Empty() || starts_.empty()) {
452 SynchronizeFullAssignment();
459 DCHECK(!absl::GetFlag(FLAGS_routing_strong_debug_checks) ||
460 !HavePathsChanged());
463 for (
int i = 0; i < container.
Size(); ++i) {
467 const int64_t
start = node_path_starts_[
index];
472 DCHECK_LT(
index, new_nexts_.size());
473 new_synchronized_unperformed_nodes_.
Set(
index);
479 OnBeforeSynchronizePaths();
481 int64_t node = touched_start;
482 while (node <
Size()) {
483 node_path_starts_[node] = touched_start;
486 node_path_starts_[node] = touched_start;
487 UpdatePathRanksFromStart(touched_start);
488 OnSynchronizePathFromStart(touched_start);
490 OnAfterSynchronizePaths();
493 void BasePathFilter::UpdateAllRanks() {
494 for (
int i = 0; i < ranks_.size(); ++i) {
497 for (
int r = 0; r <
NumPaths(); ++r) {
498 UpdatePathRanksFromStart(
Start(r));
499 OnSynchronizePathFromStart(
Start(r));
503 void BasePathFilter::UpdatePathRanksFromStart(
int start) {
505 int64_t node =
start;
506 while (node <
Size()) {
516 class VehicleAmortizedCostFilter :
public BasePathFilter {
518 explicit VehicleAmortizedCostFilter(
const RoutingModel& routing_model);
519 ~VehicleAmortizedCostFilter()
override {}
520 std::string DebugString()
const override {
521 return "VehicleAmortizedCostFilter";
523 int64_t GetSynchronizedObjectiveValue()
const override {
524 return current_vehicle_cost_;
526 int64_t GetAcceptedObjectiveValue()
const override {
527 return lns_detected() ? 0 : delta_vehicle_cost_;
531 void OnSynchronizePathFromStart(int64_t
start)
override;
532 void OnAfterSynchronizePaths()
override;
533 bool InitializeAcceptPath()
override;
534 bool AcceptPath(int64_t path_start, int64_t chain_start,
535 int64_t chain_end)
override;
536 bool FinalizeAcceptPath(int64_t objective_min,
537 int64_t objective_max)
override;
539 int64_t current_vehicle_cost_;
540 int64_t delta_vehicle_cost_;
541 std::vector<int> current_route_lengths_;
542 std::vector<int64_t> start_to_end_;
543 std::vector<int> start_to_vehicle_;
544 std::vector<int64_t> vehicle_to_start_;
545 const std::vector<int64_t>& linear_cost_factor_of_vehicle_;
546 const std::vector<int64_t>& quadratic_cost_factor_of_vehicle_;
549 VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
550 const RoutingModel& routing_model)
551 : BasePathFilter(routing_model.Nexts(),
552 routing_model.Size() + routing_model.vehicles()),
553 current_vehicle_cost_(0),
554 delta_vehicle_cost_(0),
555 current_route_lengths_(Size(), -1),
556 linear_cost_factor_of_vehicle_(
557 routing_model.GetAmortizedLinearCostFactorOfVehicles()),
558 quadratic_cost_factor_of_vehicle_(
559 routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) {
560 start_to_end_.resize(
Size(), -1);
561 start_to_vehicle_.resize(
Size(), -1);
562 vehicle_to_start_.resize(routing_model.vehicles());
563 for (
int v = 0; v < routing_model.vehicles(); v++) {
564 const int64_t
start = routing_model.Start(v);
565 start_to_vehicle_[
start] = v;
566 start_to_end_[
start] = routing_model.End(v);
567 vehicle_to_start_[v] =
start;
571 void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(int64_t
start) {
572 const int64_t
end = start_to_end_[
start];
574 const int route_length = Rank(
end) - 1;
575 CHECK_GE(route_length, 0);
576 current_route_lengths_[
start] = route_length;
579 void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
580 current_vehicle_cost_ = 0;
581 for (
int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
582 const int64_t
start = vehicle_to_start_[vehicle];
583 DCHECK_EQ(vehicle, start_to_vehicle_[
start]);
585 const int route_length = current_route_lengths_[
start];
586 DCHECK_GE(route_length, 0);
588 if (route_length == 0) {
593 const int64_t linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle];
594 const int64_t route_length_cost =
595 CapProd(quadratic_cost_factor_of_vehicle_[vehicle],
596 route_length * route_length);
598 current_vehicle_cost_ =
CapAdd(
599 current_vehicle_cost_,
CapSub(linear_cost_factor, route_length_cost));
603 bool VehicleAmortizedCostFilter::InitializeAcceptPath() {
604 delta_vehicle_cost_ = current_vehicle_cost_;
608 bool VehicleAmortizedCostFilter::AcceptPath(int64_t path_start,
612 const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start);
613 CHECK_GE(previous_chain_nodes, 0);
614 int new_chain_nodes = 0;
615 int64_t node = GetNext(chain_start);
616 while (node != chain_end) {
618 node = GetNext(node);
621 const int previous_route_length = current_route_lengths_[path_start];
622 CHECK_GE(previous_route_length, 0);
623 const int new_route_length =
624 previous_route_length - previous_chain_nodes + new_chain_nodes;
626 const int vehicle = start_to_vehicle_[path_start];
627 CHECK_GE(vehicle, 0);
628 DCHECK_EQ(path_start, vehicle_to_start_[vehicle]);
632 if (previous_route_length == 0) {
634 CHECK_GT(new_route_length, 0);
635 delta_vehicle_cost_ =
636 CapAdd(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
637 }
else if (new_route_length == 0) {
639 delta_vehicle_cost_ =
640 CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
644 const int64_t quadratic_cost_factor =
645 quadratic_cost_factor_of_vehicle_[vehicle];
646 delta_vehicle_cost_ =
647 CapAdd(delta_vehicle_cost_,
649 previous_route_length * previous_route_length));
650 delta_vehicle_cost_ =
CapSub(
652 CapProd(quadratic_cost_factor, new_route_length * new_route_length));
657 bool VehicleAmortizedCostFilter::FinalizeAcceptPath(int64_t ,
658 int64_t objective_max) {
659 return delta_vehicle_cost_ <= objective_max;
667 new VehicleAmortizedCostFilter(routing_model));
672 class TypeRegulationsFilter :
public BasePathFilter {
674 explicit TypeRegulationsFilter(
const RoutingModel&
model);
675 ~TypeRegulationsFilter()
override {}
676 std::string DebugString()
const override {
return "TypeRegulationsFilter"; }
679 void OnSynchronizePathFromStart(int64_t
start)
override;
680 bool AcceptPath(int64_t path_start, int64_t chain_start,
681 int64_t chain_end)
override;
683 bool HardIncompatibilitiesRespected(
int vehicle, int64_t chain_start,
686 const RoutingModel& routing_model_;
687 std::vector<int> start_to_vehicle_;
690 std::vector<std::vector<int>> hard_incompatibility_type_counts_per_vehicle_;
692 TypeIncompatibilityChecker temporal_incompatibility_checker_;
693 TypeRequirementChecker requirement_checker_;
696 TypeRegulationsFilter::TypeRegulationsFilter(
const RoutingModel&
model)
698 routing_model_(
model),
699 start_to_vehicle_(
model.Size(), -1),
700 temporal_incompatibility_checker_(
model,
702 requirement_checker_(
model) {
703 const int num_vehicles =
model.vehicles();
704 const bool has_hard_type_incompatibilities =
705 model.HasHardTypeIncompatibilities();
706 if (has_hard_type_incompatibilities) {
707 hard_incompatibility_type_counts_per_vehicle_.resize(num_vehicles);
709 const int num_visit_types =
model.GetNumberOfVisitTypes();
710 for (
int vehicle = 0; vehicle < num_vehicles; vehicle++) {
712 start_to_vehicle_[
start] = vehicle;
713 if (has_hard_type_incompatibilities) {
714 hard_incompatibility_type_counts_per_vehicle_[vehicle].resize(
720 void TypeRegulationsFilter::OnSynchronizePathFromStart(int64_t
start) {
721 if (!routing_model_.HasHardTypeIncompatibilities())
return;
723 const int vehicle = start_to_vehicle_[
start];
724 CHECK_GE(vehicle, 0);
725 std::vector<int>& type_counts =
726 hard_incompatibility_type_counts_per_vehicle_[vehicle];
727 std::fill(type_counts.begin(), type_counts.end(), 0);
728 const int num_types = type_counts.size();
730 int64_t node =
start;
731 while (node < Size()) {
732 DCHECK(IsVarSynced(node));
733 const int type = routing_model_.GetVisitType(node);
734 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
735 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
736 CHECK_LT(type, num_types);
743 bool TypeRegulationsFilter::HardIncompatibilitiesRespected(
int vehicle,
746 if (!routing_model_.HasHardTypeIncompatibilities())
return true;
748 const std::vector<int>& previous_type_counts =
749 hard_incompatibility_type_counts_per_vehicle_[vehicle];
751 absl::flat_hash_map< int,
int> new_type_counts;
752 absl::flat_hash_set<int> types_to_check;
755 int64_t node = GetNext(chain_start);
756 while (node != chain_end) {
757 const int type = routing_model_.GetVisitType(node);
758 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
759 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
760 DCHECK_LT(type, previous_type_counts.size());
762 previous_type_counts[type]);
763 if (type_count++ == 0) {
765 types_to_check.insert(type);
768 node = GetNext(node);
773 node =
Value(chain_start);
774 while (node != chain_end) {
775 const int type = routing_model_.GetVisitType(node);
776 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
777 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
778 DCHECK_LT(type, previous_type_counts.size());
780 previous_type_counts[type]);
781 CHECK_GE(type_count, 1);
788 for (
int type : types_to_check) {
789 for (
int incompatible_type :
790 routing_model_.GetHardTypeIncompatibilitiesOfType(type)) {
792 previous_type_counts[incompatible_type]) > 0) {
800 bool TypeRegulationsFilter::AcceptPath(int64_t path_start, int64_t chain_start,
802 const int vehicle = start_to_vehicle_[path_start];
803 CHECK_GE(vehicle, 0);
804 const auto next_accessor = [
this](int64_t node) {
return GetNext(node); };
805 return HardIncompatibilitiesRespected(vehicle, chain_start, chain_end) &&
806 temporal_incompatibility_checker_.CheckVehicle(vehicle,
808 requirement_checker_.CheckVehicle(vehicle, next_accessor);
816 new TypeRegulationsFilter(routing_model));
826 class ChainCumulFilter :
public BasePathFilter {
828 ChainCumulFilter(
const RoutingModel& routing_model,
829 const RoutingDimension& dimension);
830 ~ChainCumulFilter()
override {}
831 std::string DebugString()
const override {
832 return "ChainCumulFilter(" + name_ +
")";
836 void OnSynchronizePathFromStart(int64_t
start)
override;
837 bool AcceptPath(int64_t path_start, int64_t chain_start,
838 int64_t chain_end)
override;
840 const std::vector<IntVar*>
cumuls_;
841 std::vector<int64_t> start_to_vehicle_;
842 std::vector<int64_t> start_to_end_;
843 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
844 const std::vector<int64_t> vehicle_capacities_;
845 std::vector<int64_t> current_path_cumul_mins_;
846 std::vector<int64_t> current_max_of_path_end_cumul_mins_;
847 std::vector<int64_t> old_nexts_;
848 std::vector<int> old_vehicles_;
849 std::vector<int64_t> current_transits_;
850 const std::string name_;
853 ChainCumulFilter::ChainCumulFilter(
const RoutingModel& routing_model,
854 const RoutingDimension& dimension)
855 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
857 evaluators_(routing_model.vehicles(), nullptr),
858 vehicle_capacities_(dimension.vehicle_capacities()),
859 current_path_cumul_mins_(dimension.cumuls().size(), 0),
860 current_max_of_path_end_cumul_mins_(dimension.cumuls().size(), 0),
861 old_nexts_(routing_model.Size(), kUnassigned),
862 old_vehicles_(routing_model.Size(), kUnassigned),
863 current_transits_(routing_model.Size(), 0),
864 name_(dimension.
name()) {
865 start_to_vehicle_.resize(Size(), -1);
866 start_to_end_.resize(Size(), -1);
867 for (
int i = 0; i < routing_model.vehicles(); ++i) {
868 start_to_vehicle_[routing_model.Start(i)] = i;
869 start_to_end_[routing_model.Start(i)] = routing_model.End(i);
870 evaluators_[i] = &dimension.transit_evaluator(i);
877 void ChainCumulFilter::OnSynchronizePathFromStart(int64_t
start) {
878 const int vehicle = start_to_vehicle_[
start];
879 std::vector<int64_t> path_nodes;
880 int64_t node =
start;
881 int64_t cumul =
cumuls_[node]->Min();
882 while (node < Size()) {
883 path_nodes.push_back(node);
884 current_path_cumul_mins_[node] = cumul;
886 if (
next != old_nexts_[node] || vehicle != old_vehicles_[node]) {
887 old_nexts_[node] =
next;
888 old_vehicles_[node] = vehicle;
889 current_transits_[node] = (*evaluators_[vehicle])(node,
next);
891 cumul =
CapAdd(cumul, current_transits_[node]);
895 path_nodes.push_back(node);
896 current_path_cumul_mins_[node] = cumul;
897 int64_t max_cumuls = cumul;
898 for (
int i = path_nodes.size() - 1; i >= 0; --i) {
899 const int64_t node = path_nodes[i];
900 max_cumuls =
std::max(max_cumuls, current_path_cumul_mins_[node]);
901 current_max_of_path_end_cumul_mins_[node] = max_cumuls;
906 bool ChainCumulFilter::AcceptPath(int64_t path_start, int64_t chain_start,
908 const int vehicle = start_to_vehicle_[path_start];
909 const int64_t
capacity = vehicle_capacities_[vehicle];
910 int64_t node = chain_start;
911 int64_t cumul = current_path_cumul_mins_[node];
912 while (node != chain_end) {
913 const int64_t
next = GetNext(node);
914 if (IsVarSynced(node) &&
next ==
Value(node) &&
915 vehicle == old_vehicles_[node]) {
916 cumul =
CapAdd(cumul, current_transits_[node]);
918 cumul =
CapAdd(cumul, (*evaluators_[vehicle])(node,
next));
924 const int64_t
end = start_to_end_[path_start];
925 const int64_t end_cumul_delta =
926 CapSub(current_path_cumul_mins_[
end], current_path_cumul_mins_[node]);
927 const int64_t after_chain_cumul_delta =
928 CapSub(current_max_of_path_end_cumul_mins_[node],
929 current_path_cumul_mins_[node]);
936 class PathCumulFilter :
public BasePathFilter {
938 PathCumulFilter(
const RoutingModel& routing_model,
939 const RoutingDimension& dimension,
940 bool propagate_own_objective_value,
941 bool filter_objective_cost,
bool can_use_lp);
942 ~PathCumulFilter()
override {}
943 std::string DebugString()
const override {
944 return "PathCumulFilter(" + name_ +
")";
946 int64_t GetSynchronizedObjectiveValue()
const override {
947 return propagate_own_objective_value_ ? synchronized_objective_value_ : 0;
949 int64_t GetAcceptedObjectiveValue()
const override {
950 return lns_detected() || !propagate_own_objective_value_
952 : accepted_objective_value_;
959 struct SupportedPathCumul {
980 void ClearPath(
int path) {
981 paths_[path].clear();
982 transits_[path].clear();
984 int AddPaths(
int num_paths) {
985 const int first_path = paths_.size();
986 paths_.resize(first_path + num_paths);
987 transits_.resize(first_path + num_paths);
990 void ReserveTransits(
int path,
int number_of_route_arcs) {
991 transits_[path].reserve(number_of_route_arcs);
992 paths_[path].reserve(number_of_route_arcs + 1);
996 void PushTransit(
int path,
int node,
int next, int64_t transit) {
997 transits_[path].push_back(transit);
998 if (paths_[path].empty()) {
999 paths_[path].push_back(node);
1001 DCHECK_EQ(paths_[path].back(), node);
1002 paths_[path].push_back(
next);
1004 int NumPaths()
const {
return paths_.size(); }
1005 int PathSize(
int path)
const {
return paths_[path].size(); }
1006 int Node(
int path,
int position)
const {
return paths_[path][position]; }
1007 int64_t Transit(
int path,
int position)
const {
1008 return transits_[path][position];
1013 std::vector<std::vector<int64_t>> paths_;
1016 std::vector<std::vector<int64_t>> transits_;
1019 bool InitializeAcceptPath()
override {
1020 cumul_cost_delta_ = total_current_cumul_cost_value_;
1021 node_with_precedence_to_delta_min_max_cumuls_.clear();
1024 delta_paths_.clear();
1025 delta_path_transits_.Clear();
1026 delta_nodes_with_precedences_and_changed_cumul_.
ClearAll();
1029 bool AcceptPath(int64_t path_start, int64_t chain_start,
1030 int64_t chain_end)
override;
1031 bool FinalizeAcceptPath(int64_t objective_min,
1032 int64_t objective_max)
override;
1033 void OnBeforeSynchronizePaths()
override;
1035 bool FilterSpanCost()
const {
return global_span_cost_coefficient_ != 0; }
1037 bool FilterSlackCost()
const {
1038 return has_nonzero_vehicle_span_cost_coefficients_ ||
1039 has_vehicle_span_upper_bounds_;
1042 bool FilterBreakCost(
int vehicle)
const {
1043 return dimension_.HasBreakConstraints() &&
1044 !dimension_.GetBreakIntervalsOfVehicle(vehicle).empty();
1047 bool FilterCumulSoftBounds()
const {
return !cumul_soft_bounds_.empty(); }
1049 int64_t GetCumulSoftCost(int64_t node, int64_t
cumul_value)
const;
1051 bool FilterCumulPiecewiseLinearCosts()
const {
1052 return !cumul_piecewise_linear_costs_.empty();
1055 bool FilterWithDimensionCumulOptimizerForVehicle(
int vehicle)
const {
1056 if (!can_use_lp_ || FilterCumulPiecewiseLinearCosts()) {
1060 int num_linear_constraints = 0;
1061 if (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0)
1062 ++num_linear_constraints;
1063 if (FilterSoftSpanCost(vehicle)) ++num_linear_constraints;
1064 if (FilterCumulSoftLowerBounds()) ++num_linear_constraints;
1065 if (FilterCumulSoftBounds()) ++num_linear_constraints;
1066 if (vehicle_span_upper_bounds_[vehicle] <
1068 ++num_linear_constraints;
1070 const bool has_breaks = FilterBreakCost(vehicle);
1071 if (has_breaks) ++num_linear_constraints;
1080 return num_linear_constraints >= 2 &&
1081 (has_breaks || filter_objective_cost_);
1084 bool FilterDimensionForbiddenIntervals()
const {
1085 for (
const SortedDisjointIntervalList& intervals :
1086 dimension_.forbidden_intervals()) {
1089 if (intervals.NumIntervals() > 0) {
1096 int64_t GetCumulPiecewiseLinearCost(int64_t node, int64_t
cumul_value)
const;
1098 bool FilterCumulSoftLowerBounds()
const {
1099 return !cumul_soft_lower_bounds_.empty();
1102 bool FilterPrecedences()
const {
return !node_index_to_precedences_.empty(); }
1104 bool FilterSoftSpanCost()
const {
1105 return dimension_.HasSoftSpanUpperBounds();
1107 bool FilterSoftSpanCost(
int vehicle)
const {
1108 return dimension_.HasSoftSpanUpperBounds() &&
1109 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle).cost > 0;
1111 bool FilterSoftSpanQuadraticCost()
const {
1112 return dimension_.HasQuadraticCostSoftSpanUpperBounds();
1114 bool FilterSoftSpanQuadraticCost(
int vehicle)
const {
1115 return dimension_.HasQuadraticCostSoftSpanUpperBounds() &&
1116 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle)
1120 int64_t GetCumulSoftLowerBoundCost(int64_t node, int64_t
cumul_value)
const;
1122 int64_t GetPathCumulSoftLowerBoundCost(
const PathTransits& path_transits,
1125 void InitializeSupportedPathCumul(SupportedPathCumul* supported_cumul,
1126 int64_t default_value);
1136 bool PickupToDeliveryLimitsRespected(
1137 const PathTransits& path_transits,
int path,
1138 const std::vector<int64_t>& min_path_cumuls)
const;
1148 void StoreMinMaxCumulOfNodesOnPath(
1149 int path,
const std::vector<int64_t>& min_path_cumuls,
bool is_delta);
1158 int64_t ComputePathMaxStartFromEndCumul(
const PathTransits& path_transits,
1159 int path, int64_t path_start,
1160 int64_t min_end_cumul)
const;
1162 const RoutingModel& routing_model_;
1163 const RoutingDimension& dimension_;
1164 const std::vector<IntVar*>
cumuls_;
1165 const std::vector<IntVar*> slacks_;
1166 std::vector<int64_t> start_to_vehicle_;
1167 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1168 std::vector<int64_t> vehicle_span_upper_bounds_;
1169 bool has_vehicle_span_upper_bounds_;
1170 int64_t total_current_cumul_cost_value_;
1171 int64_t synchronized_objective_value_;
1172 int64_t accepted_objective_value_;
1175 absl::flat_hash_map<int64_t, int64_t> current_cumul_cost_values_;
1176 int64_t cumul_cost_delta_;
1178 std::vector<int64_t> delta_path_cumul_cost_values_;
1179 const int64_t global_span_cost_coefficient_;
1180 std::vector<SoftBound> cumul_soft_bounds_;
1181 std::vector<SoftBound> cumul_soft_lower_bounds_;
1182 std::vector<const PiecewiseLinearFunction*> cumul_piecewise_linear_costs_;
1183 std::vector<int64_t> vehicle_span_cost_coefficients_;
1184 bool has_nonzero_vehicle_span_cost_coefficients_;
1185 const std::vector<int64_t> vehicle_capacities_;
1189 std::vector<std::vector<RoutingDimension::NodePrecedence>>
1190 node_index_to_precedences_;
1193 SupportedPathCumul current_min_start_;
1194 SupportedPathCumul current_max_end_;
1195 PathTransits current_path_transits_;
1197 std::vector<std::pair<int64_t, int64_t>> current_min_max_node_cumuls_;
1200 PathTransits delta_path_transits_;
1201 int64_t delta_max_end_cumul_;
1202 SparseBitset<int64_t> delta_nodes_with_precedences_and_changed_cumul_;
1203 absl::flat_hash_map<int64_t, std::pair<int64_t, int64_t>>
1204 node_with_precedence_to_delta_min_max_cumuls_;
1205 absl::btree_set<int> delta_paths_;
1206 const std::string name_;
1208 LocalDimensionCumulOptimizer* optimizer_;
1209 LocalDimensionCumulOptimizer* mp_optimizer_;
1210 const bool filter_objective_cost_;
1213 const bool can_use_lp_;
1214 const bool propagate_own_objective_value_;
1216 std::vector<int64_t> min_path_cumuls_;
1219 PathCumulFilter::PathCumulFilter(
const RoutingModel& routing_model,
1220 const RoutingDimension& dimension,
1221 bool propagate_own_objective_value,
1222 bool filter_objective_cost,
bool can_use_lp)
1223 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size()),
1224 routing_model_(routing_model),
1225 dimension_(dimension),
1227 slacks_(dimension.slacks()),
1228 evaluators_(routing_model.vehicles(), nullptr),
1229 vehicle_span_upper_bounds_(dimension.vehicle_span_upper_bounds()),
1230 has_vehicle_span_upper_bounds_(false),
1231 total_current_cumul_cost_value_(0),
1232 synchronized_objective_value_(0),
1233 accepted_objective_value_(0),
1234 current_cumul_cost_values_(),
1235 cumul_cost_delta_(0),
1236 delta_path_cumul_cost_values_(routing_model.vehicles(),
1237 std::numeric_limits<int64_t>::
min()),
1238 global_span_cost_coefficient_(dimension.global_span_cost_coefficient()),
1239 vehicle_span_cost_coefficients_(
1240 dimension.vehicle_span_cost_coefficients()),
1241 has_nonzero_vehicle_span_cost_coefficients_(false),
1242 vehicle_capacities_(dimension.vehicle_capacities()),
1243 delta_max_end_cumul_(0),
1244 delta_nodes_with_precedences_and_changed_cumul_(routing_model.Size()),
1245 name_(dimension.
name()),
1246 optimizer_(routing_model.GetMutableLocalCumulLPOptimizer(dimension)),
1247 mp_optimizer_(routing_model.GetMutableLocalCumulMPOptimizer(dimension)),
1248 filter_objective_cost_(filter_objective_cost),
1249 can_use_lp_(can_use_lp),
1250 propagate_own_objective_value_(propagate_own_objective_value) {
1251 for (
const int64_t
upper_bound : vehicle_span_upper_bounds_) {
1253 has_vehicle_span_upper_bounds_ =
true;
1257 for (
const int64_t
coefficient : vehicle_span_cost_coefficients_) {
1259 has_nonzero_vehicle_span_cost_coefficients_ =
true;
1263 cumul_soft_bounds_.resize(
cumuls_.size());
1264 cumul_soft_lower_bounds_.resize(
cumuls_.size());
1265 cumul_piecewise_linear_costs_.resize(
cumuls_.size());
1266 bool has_cumul_soft_bounds =
false;
1267 bool has_cumul_soft_lower_bounds =
false;
1268 bool has_cumul_piecewise_linear_costs =
false;
1269 bool has_cumul_hard_bounds =
false;
1270 for (
const IntVar*
const slack : slacks_) {
1271 if (slack->Min() > 0) {
1272 has_cumul_hard_bounds =
true;
1276 for (
int i = 0; i <
cumuls_.size(); ++i) {
1277 if (dimension.HasCumulVarSoftUpperBound(i)) {
1278 has_cumul_soft_bounds =
true;
1279 cumul_soft_bounds_[i].bound = dimension.GetCumulVarSoftUpperBound(i);
1280 cumul_soft_bounds_[i].coefficient =
1281 dimension.GetCumulVarSoftUpperBoundCoefficient(i);
1283 if (dimension.HasCumulVarSoftLowerBound(i)) {
1284 has_cumul_soft_lower_bounds =
true;
1285 cumul_soft_lower_bounds_[i].bound =
1286 dimension.GetCumulVarSoftLowerBound(i);
1287 cumul_soft_lower_bounds_[i].coefficient =
1288 dimension.GetCumulVarSoftLowerBoundCoefficient(i);
1290 if (dimension.HasCumulVarPiecewiseLinearCost(i)) {
1291 has_cumul_piecewise_linear_costs =
true;
1292 cumul_piecewise_linear_costs_[i] =
1293 dimension.GetCumulVarPiecewiseLinearCost(i);
1295 IntVar*
const cumul_var =
cumuls_[i];
1296 if (cumul_var->Min() > 0 ||
1298 has_cumul_hard_bounds =
true;
1301 if (!has_cumul_soft_bounds) {
1302 cumul_soft_bounds_.clear();
1304 if (!has_cumul_soft_lower_bounds) {
1305 cumul_soft_lower_bounds_.clear();
1307 if (!has_cumul_piecewise_linear_costs) {
1308 cumul_piecewise_linear_costs_.clear();
1310 if (!has_cumul_hard_bounds) {
1315 vehicle_span_cost_coefficients_.assign(routing_model.vehicles(), 0);
1316 has_nonzero_vehicle_span_cost_coefficients_ =
false;
1318 start_to_vehicle_.resize(Size(), -1);
1319 for (
int i = 0; i < routing_model.vehicles(); ++i) {
1320 start_to_vehicle_[routing_model.Start(i)] = i;
1321 evaluators_[i] = &dimension.transit_evaluator(i);
1324 const std::vector<RoutingDimension::NodePrecedence>& node_precedences =
1325 dimension.GetNodePrecedences();
1326 if (!node_precedences.empty()) {
1327 current_min_max_node_cumuls_.resize(
cumuls_.size(), {-1, -1});
1328 node_index_to_precedences_.resize(
cumuls_.size());
1329 for (
const auto& node_precedence : node_precedences) {
1330 node_index_to_precedences_[node_precedence.first_node].push_back(
1332 node_index_to_precedences_[node_precedence.second_node].push_back(
1338 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
1339 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1340 DCHECK_NE(optimizer_,
nullptr);
1341 DCHECK_NE(mp_optimizer_,
nullptr);
1347 int64_t PathCumulFilter::GetCumulSoftCost(int64_t node,
1349 if (node < cumul_soft_bounds_.size()) {
1350 const int64_t
bound = cumul_soft_bounds_[node].bound;
1351 const int64_t
coefficient = cumul_soft_bounds_[node].coefficient;
1359 int64_t PathCumulFilter::GetCumulPiecewiseLinearCost(
1361 if (node < cumul_piecewise_linear_costs_.size()) {
1362 const PiecewiseLinearFunction*
cost = cumul_piecewise_linear_costs_[node];
1363 if (
cost !=
nullptr) {
1370 int64_t PathCumulFilter::GetCumulSoftLowerBoundCost(int64_t node,
1372 if (node < cumul_soft_lower_bounds_.size()) {
1373 const int64_t
bound = cumul_soft_lower_bounds_[node].bound;
1374 const int64_t
coefficient = cumul_soft_lower_bounds_[node].coefficient;
1382 int64_t PathCumulFilter::GetPathCumulSoftLowerBoundCost(
1383 const PathTransits& path_transits,
int path)
const {
1384 int64_t node = path_transits.Node(path, path_transits.PathSize(path) - 1);
1385 int64_t cumul =
cumuls_[node]->Max();
1386 int64_t current_cumul_cost_value = GetCumulSoftLowerBoundCost(node, cumul);
1387 for (
int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
1388 node = path_transits.Node(path, i);
1389 cumul =
CapSub(cumul, path_transits.Transit(path, i));
1391 current_cumul_cost_value =
CapAdd(current_cumul_cost_value,
1392 GetCumulSoftLowerBoundCost(node, cumul));
1394 return current_cumul_cost_value;
1397 void PathCumulFilter::OnBeforeSynchronizePaths() {
1398 total_current_cumul_cost_value_ = 0;
1399 cumul_cost_delta_ = 0;
1400 current_cumul_cost_values_.clear();
1401 if (NumPaths() > 0 &&
1402 (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1403 FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1404 FilterPrecedences() || FilterSoftSpanCost() ||
1405 FilterSoftSpanQuadraticCost())) {
1406 InitializeSupportedPathCumul(¤t_min_start_,
1408 InitializeSupportedPathCumul(¤t_max_end_,
1410 current_path_transits_.Clear();
1411 current_path_transits_.AddPaths(NumPaths());
1413 for (
int r = 0; r < NumPaths(); ++r) {
1414 int64_t node = Start(r);
1415 const int vehicle = start_to_vehicle_[Start(r)];
1418 int number_of_route_arcs = 0;
1419 while (node < Size()) {
1420 ++number_of_route_arcs;
1423 current_path_transits_.ReserveTransits(r, number_of_route_arcs);
1426 int64_t cumul =
cumuls_[node]->Min();
1427 min_path_cumuls_.clear();
1428 min_path_cumuls_.push_back(cumul);
1430 int64_t current_cumul_cost_value = GetCumulSoftCost(node, cumul);
1431 current_cumul_cost_value =
CapAdd(
1432 current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1434 int64_t total_transit = 0;
1435 while (node < Size()) {
1437 const int64_t transit = (*evaluators_[vehicle])(node,
next);
1438 total_transit =
CapAdd(total_transit, transit);
1439 const int64_t transit_slack =
CapAdd(transit, slacks_[node]->Min());
1440 current_path_transits_.PushTransit(r, node,
next, transit_slack);
1441 cumul =
CapAdd(cumul, transit_slack);
1443 dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
next, cumul);
1445 min_path_cumuls_.push_back(cumul);
1447 current_cumul_cost_value =
1448 CapAdd(current_cumul_cost_value, GetCumulSoftCost(node, cumul));
1449 current_cumul_cost_value =
CapAdd(
1450 current_cumul_cost_value, GetCumulPiecewiseLinearCost(node, cumul));
1452 if (FilterPrecedences()) {
1453 StoreMinMaxCumulOfNodesOnPath(r, min_path_cumuls_,
1456 if (number_of_route_arcs == 1 &&
1457 !routing_model_.IsVehicleUsedWhenEmpty(vehicle)) {
1460 current_cumul_cost_values_[Start(r)] = 0;
1461 current_path_transits_.ClearPath(r);
1464 if (FilterSlackCost() || FilterSoftSpanCost() ||
1465 FilterSoftSpanQuadraticCost()) {
1466 const int64_t
start = ComputePathMaxStartFromEndCumul(
1467 current_path_transits_, r, Start(r), cumul);
1468 const int64_t span_lower_bound =
CapSub(cumul,
start);
1469 if (FilterSlackCost()) {
1470 current_cumul_cost_value =
1471 CapAdd(current_cumul_cost_value,
1472 CapProd(vehicle_span_cost_coefficients_[vehicle],
1473 CapSub(span_lower_bound, total_transit)));
1475 if (FilterSoftSpanCost()) {
1476 const BoundCost bound_cost =
1477 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1478 if (bound_cost.bound < span_lower_bound) {
1479 const int64_t violation =
1480 CapSub(span_lower_bound, bound_cost.bound);
1481 current_cumul_cost_value =
CapAdd(
1482 current_cumul_cost_value,
CapProd(bound_cost.cost, violation));
1485 if (FilterSoftSpanQuadraticCost()) {
1486 const BoundCost bound_cost =
1487 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1488 if (bound_cost.bound < span_lower_bound) {
1489 const int64_t violation =
1490 CapSub(span_lower_bound, bound_cost.bound);
1491 current_cumul_cost_value =
1492 CapAdd(current_cumul_cost_value,
1497 if (FilterCumulSoftLowerBounds()) {
1498 current_cumul_cost_value =
1499 CapAdd(current_cumul_cost_value,
1500 GetPathCumulSoftLowerBoundCost(current_path_transits_, r));
1502 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1505 int64_t lp_cumul_cost_value = 0;
1506 LocalDimensionCumulOptimizer*
const optimizer =
1507 FilterBreakCost(vehicle) ? mp_optimizer_ : optimizer_;
1508 DCHECK(optimizer !=
nullptr);
1510 optimizer->ComputeRouteCumulCostWithoutFixedTransits(
1511 vehicle, [
this](int64_t node) {
return Value(node); },
1512 &lp_cumul_cost_value);
1515 lp_cumul_cost_value = 0;
1517 case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
1518 DCHECK(mp_optimizer_ !=
nullptr);
1519 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1520 vehicle, [
this](int64_t node) { return Value(node); },
1521 &lp_cumul_cost_value) ==
1523 lp_cumul_cost_value = 0;
1529 current_cumul_cost_value =
1530 std::max(current_cumul_cost_value, lp_cumul_cost_value);
1532 current_cumul_cost_values_[Start(r)] = current_cumul_cost_value;
1533 current_max_end_.path_values[r] = cumul;
1534 if (current_max_end_.cumul_value < cumul) {
1535 current_max_end_.cumul_value = cumul;
1536 current_max_end_.cumul_value_support = r;
1538 total_current_cumul_cost_value_ =
1539 CapAdd(total_current_cumul_cost_value_, current_cumul_cost_value);
1541 if (FilterPrecedences()) {
1543 for (int64_t node : GetNewSynchronizedUnperformedNodes()) {
1544 current_min_max_node_cumuls_[node] = {-1, -1};
1549 for (
int r = 0; r < NumPaths(); ++r) {
1550 const int64_t
start = ComputePathMaxStartFromEndCumul(
1551 current_path_transits_, r, Start(r), current_max_end_.cumul_value);
1552 current_min_start_.path_values[r] =
start;
1553 if (current_min_start_.cumul_value >
start) {
1554 current_min_start_.cumul_value =
start;
1555 current_min_start_.cumul_value_support = r;
1562 DCHECK(global_span_cost_coefficient_ == 0 ||
1563 current_min_start_.cumul_value <= current_max_end_.cumul_value);
1564 synchronized_objective_value_ =
1565 CapAdd(total_current_cumul_cost_value_,
1566 CapProd(global_span_cost_coefficient_,
1567 CapSub(current_max_end_.cumul_value,
1568 current_min_start_.cumul_value)));
1571 bool PathCumulFilter::AcceptPath(int64_t path_start, int64_t ,
1573 int64_t node = path_start;
1574 int64_t cumul =
cumuls_[node]->Min();
1575 int64_t cumul_cost_delta = 0;
1576 int64_t total_transit = 0;
1577 const int path = delta_path_transits_.AddPaths(1);
1578 const int vehicle = start_to_vehicle_[path_start];
1579 const int64_t
capacity = vehicle_capacities_[vehicle];
1580 const bool filter_vehicle_costs =
1581 !routing_model_.IsEnd(GetNext(node)) ||
1582 routing_model_.IsVehicleUsedWhenEmpty(vehicle);
1583 if (filter_vehicle_costs) {
1584 cumul_cost_delta =
CapAdd(GetCumulSoftCost(node, cumul),
1585 GetCumulPiecewiseLinearCost(node, cumul));
1588 int number_of_route_arcs = 0;
1589 while (node < Size()) {
1590 ++number_of_route_arcs;
1591 node = GetNext(node);
1592 DCHECK_NE(node, kUnassigned);
1594 delta_path_transits_.ReserveTransits(path, number_of_route_arcs);
1595 min_path_cumuls_.clear();
1596 min_path_cumuls_.push_back(cumul);
1601 while (node < Size()) {
1602 const int64_t
next = GetNext(node);
1603 const int64_t transit = (*evaluators_[vehicle])(node,
next);
1604 total_transit =
CapAdd(total_transit, transit);
1605 const int64_t transit_slack =
CapAdd(transit, slacks_[node]->Min());
1606 delta_path_transits_.PushTransit(path, node,
next, transit_slack);
1607 cumul =
CapAdd(cumul, transit_slack);
1608 cumul = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
next, cumul);
1613 min_path_cumuls_.push_back(cumul);
1615 if (filter_vehicle_costs) {
1617 CapAdd(cumul_cost_delta, GetCumulSoftCost(node, cumul));
1619 CapAdd(cumul_cost_delta, GetCumulPiecewiseLinearCost(node, cumul));
1622 const int64_t min_end = cumul;
1624 if (!PickupToDeliveryLimitsRespected(delta_path_transits_, path,
1625 min_path_cumuls_)) {
1628 if (FilterSlackCost() || FilterBreakCost(vehicle) ||
1629 FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1631 if (vehicle_span_upper_bounds_[vehicle] <
1633 const int64_t span_max = vehicle_span_upper_bounds_[vehicle];
1634 slack_max =
std::min(slack_max,
CapSub(span_max, total_transit));
1636 const int64_t max_start_from_min_end = ComputePathMaxStartFromEndCumul(
1637 delta_path_transits_, path, path_start, min_end);
1638 const int64_t span_lb =
CapSub(min_end, max_start_from_min_end);
1639 int64_t min_total_slack =
CapSub(span_lb, total_transit);
1640 if (min_total_slack > slack_max)
return false;
1642 if (dimension_.HasBreakConstraints()) {
1643 for (
const auto [limit, min_break_duration] :
1644 dimension_.GetBreakDistanceDurationOfVehicle(vehicle)) {
1649 if (limit == 0 || total_transit == 0)
continue;
1650 const int num_breaks_lb = (total_transit - 1) / limit;
1651 const int64_t slack_lb =
CapProd(num_breaks_lb, min_break_duration);
1652 if (slack_lb > slack_max)
return false;
1653 min_total_slack =
std::max(min_total_slack, slack_lb);
1659 int64_t min_total_break = 0;
1660 int64_t max_path_end =
cumuls_[routing_model_.End(vehicle)]->Max();
1661 const int64_t max_start = ComputePathMaxStartFromEndCumul(
1662 delta_path_transits_, path, path_start, max_path_end);
1663 for (
const IntervalVar* br :
1664 dimension_.GetBreakIntervalsOfVehicle(vehicle)) {
1665 if (!br->MustBePerformed())
continue;
1666 if (max_start < br->EndMin() && br->StartMax() < min_end) {
1667 min_total_break =
CapAdd(min_total_break, br->DurationMin());
1670 if (min_total_break > slack_max)
return false;
1671 min_total_slack =
std::max(min_total_slack, min_total_break);
1673 if (filter_vehicle_costs) {
1674 cumul_cost_delta =
CapAdd(
1676 CapProd(vehicle_span_cost_coefficients_[vehicle], min_total_slack));
1677 const int64_t span_lower_bound =
CapAdd(total_transit, min_total_slack);
1678 if (FilterSoftSpanCost()) {
1679 const BoundCost bound_cost =
1680 dimension_.GetSoftSpanUpperBoundForVehicle(vehicle);
1681 if (bound_cost.bound < span_lower_bound) {
1682 const int64_t violation =
CapSub(span_lower_bound, bound_cost.bound);
1684 CapAdd(cumul_cost_delta,
CapProd(bound_cost.cost, violation));
1687 if (FilterSoftSpanQuadraticCost()) {
1688 const BoundCost bound_cost =
1689 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(vehicle);
1690 if (bound_cost.bound < span_lower_bound) {
1691 const int64_t violation =
CapSub(span_lower_bound, bound_cost.bound);
1698 if (
CapAdd(total_transit, min_total_slack) >
1699 vehicle_span_upper_bounds_[vehicle]) {
1703 if (FilterCumulSoftLowerBounds() && filter_vehicle_costs) {
1706 GetPathCumulSoftLowerBoundCost(delta_path_transits_, path));
1708 if (FilterPrecedences()) {
1709 StoreMinMaxCumulOfNodesOnPath(path, min_path_cumuls_,
true);
1711 if (!filter_vehicle_costs) {
1714 cumul_cost_delta = 0;
1715 delta_path_transits_.ClearPath(path);
1717 if (FilterSpanCost() || FilterCumulSoftBounds() || FilterSlackCost() ||
1718 FilterCumulSoftLowerBounds() || FilterCumulPiecewiseLinearCosts() ||
1719 FilterSoftSpanCost(vehicle) || FilterSoftSpanQuadraticCost(vehicle)) {
1720 delta_paths_.insert(GetPath(path_start));
1721 delta_path_cumul_cost_values_[vehicle] = cumul_cost_delta;
1723 CapSub(cumul_cost_delta, current_cumul_cost_values_[path_start]);
1724 if (filter_vehicle_costs) {
1725 delta_max_end_cumul_ =
std::max(delta_max_end_cumul_, min_end);
1728 cumul_cost_delta_ =
CapAdd(cumul_cost_delta_, cumul_cost_delta);
1732 bool PathCumulFilter::FinalizeAcceptPath(int64_t ,
1733 int64_t objective_max) {
1734 DCHECK(!lns_detected());
1735 if (!FilterSpanCost() && !FilterCumulSoftBounds() && !FilterSlackCost() &&
1736 !FilterCumulSoftLowerBounds() && !FilterCumulPiecewiseLinearCosts() &&
1737 !FilterPrecedences() && !FilterSoftSpanCost() &&
1738 !FilterSoftSpanQuadraticCost()) {
1741 if (FilterPrecedences()) {
1742 for (int64_t node : delta_nodes_with_precedences_and_changed_cumul_
1743 .PositionsSetAtLeastOnce()) {
1744 const std::pair<int64_t, int64_t> node_min_max_cumul_in_delta =
1749 DCHECK(node_min_max_cumul_in_delta.first >= 0 &&
1750 node_min_max_cumul_in_delta.second >= 0);
1751 for (
const RoutingDimension::NodePrecedence& precedence :
1752 node_index_to_precedences_[node]) {
1753 const bool node_is_first = (precedence.first_node == node);
1754 const int64_t other_node =
1755 node_is_first ? precedence.second_node : precedence.first_node;
1756 if (GetNext(other_node) == kUnassigned ||
1757 GetNext(other_node) == other_node) {
1764 const std::pair<int64_t, int64_t>& other_min_max_cumul_in_delta =
1767 current_min_max_node_cumuls_[other_node]);
1769 const int64_t first_min_cumul =
1770 node_is_first ? node_min_max_cumul_in_delta.first
1771 : other_min_max_cumul_in_delta.first;
1772 const int64_t second_max_cumul =
1773 node_is_first ? other_min_max_cumul_in_delta.second
1774 : node_min_max_cumul_in_delta.second;
1776 if (second_max_cumul < first_min_cumul + precedence.offset) {
1782 int64_t new_max_end = delta_max_end_cumul_;
1784 if (FilterSpanCost()) {
1785 if (new_max_end < current_max_end_.cumul_value) {
1789 if (!delta_paths_.contains(current_max_end_.cumul_value_support)) {
1790 new_max_end = current_max_end_.cumul_value;
1792 for (
int i = 0; i < current_max_end_.path_values.size(); ++i) {
1793 if (current_max_end_.path_values[i] > new_max_end &&
1794 !delta_paths_.contains(i)) {
1795 new_max_end = current_max_end_.path_values[i];
1803 for (
int r = 0; r < delta_path_transits_.NumPaths(); ++r) {
1805 std::min(ComputePathMaxStartFromEndCumul(delta_path_transits_, r,
1806 Start(r), new_max_end),
1809 if (new_max_end != current_max_end_.cumul_value) {
1810 for (
int r = 0; r < NumPaths(); ++r) {
1811 if (delta_paths_.contains(r)) {
1814 new_min_start =
std::min(new_min_start, ComputePathMaxStartFromEndCumul(
1815 current_path_transits_, r,
1816 Start(r), new_max_end));
1818 }
else if (new_min_start > current_min_start_.cumul_value) {
1822 if (!delta_paths_.contains(current_min_start_.cumul_value_support)) {
1823 new_min_start = current_min_start_.cumul_value;
1825 for (
int i = 0; i < current_min_start_.path_values.size(); ++i) {
1826 if (current_min_start_.path_values[i] < new_min_start &&
1827 !delta_paths_.contains(i)) {
1828 new_min_start = current_min_start_.path_values[i];
1836 accepted_objective_value_ =
1837 CapAdd(cumul_cost_delta_,
CapProd(global_span_cost_coefficient_,
1838 CapSub(new_max_end, new_min_start)));
1840 if (can_use_lp_ && optimizer_ !=
nullptr &&
1841 accepted_objective_value_ <= objective_max) {
1842 const size_t num_touched_paths = GetTouchedPathStarts().size();
1843 std::vector<int64_t> path_delta_cost_values(num_touched_paths, 0);
1844 std::vector<bool> requires_mp(num_touched_paths,
false);
1845 for (
int i = 0; i < num_touched_paths; ++i) {
1846 const int64_t
start = GetTouchedPathStarts()[i];
1847 const int vehicle = start_to_vehicle_[
start];
1848 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1851 int64_t path_delta_cost_with_lp = 0;
1853 optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1854 vehicle, [
this](int64_t node) {
return GetNext(node); },
1855 &path_delta_cost_with_lp);
1859 DCHECK(delta_paths_.contains(GetPath(
start)));
1860 const int64_t path_cost_diff_with_lp =
CapSub(
1861 path_delta_cost_with_lp, delta_path_cumul_cost_values_[vehicle]);
1862 if (path_cost_diff_with_lp > 0) {
1863 path_delta_cost_values[i] = path_delta_cost_with_lp;
1864 accepted_objective_value_ =
1865 CapAdd(accepted_objective_value_, path_cost_diff_with_lp);
1866 if (accepted_objective_value_ > objective_max) {
1870 path_delta_cost_values[i] = delta_path_cumul_cost_values_[vehicle];
1872 DCHECK_NE(mp_optimizer_,
nullptr);
1874 FilterBreakCost(vehicle) ||
1875 (
status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY);
1878 DCHECK_LE(accepted_objective_value_, objective_max);
1880 for (
int i = 0; i < num_touched_paths; ++i) {
1881 if (!requires_mp[i]) {
1884 const int64_t
start = GetTouchedPathStarts()[i];
1885 const int vehicle = start_to_vehicle_[
start];
1886 int64_t path_delta_cost_with_mp = 0;
1887 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
1888 vehicle, [
this](int64_t node) { return GetNext(node); },
1889 &path_delta_cost_with_mp) ==
1893 DCHECK(delta_paths_.contains(GetPath(
start)));
1894 const int64_t path_cost_diff_with_mp =
1895 CapSub(path_delta_cost_with_mp, path_delta_cost_values[i]);
1896 if (path_cost_diff_with_mp > 0) {
1897 accepted_objective_value_ =
1898 CapAdd(accepted_objective_value_, path_cost_diff_with_mp);
1899 if (accepted_objective_value_ > objective_max) {
1906 return accepted_objective_value_ <= objective_max;
1909 void PathCumulFilter::InitializeSupportedPathCumul(
1910 SupportedPathCumul* supported_cumul, int64_t default_value) {
1911 supported_cumul->cumul_value = default_value;
1912 supported_cumul->cumul_value_support = -1;
1913 supported_cumul->path_values.resize(NumPaths(), default_value);
1916 bool PathCumulFilter::PickupToDeliveryLimitsRespected(
1917 const PathTransits& path_transits,
int path,
1918 const std::vector<int64_t>& min_path_cumuls)
const {
1919 if (!dimension_.HasPickupToDeliveryLimits()) {
1922 const int num_pairs = routing_model_.GetPickupAndDeliveryPairs().size();
1923 DCHECK_GT(num_pairs, 0);
1924 std::vector<std::pair<int, int64_t>> visited_delivery_and_min_cumul_per_pair(
1925 num_pairs, {-1, -1});
1927 const int path_size = path_transits.PathSize(path);
1928 CHECK_EQ(min_path_cumuls.size(), path_size);
1930 int64_t max_cumul = min_path_cumuls.back();
1931 for (
int i = path_transits.PathSize(path) - 2; i >= 0; i--) {
1932 const int node_index = path_transits.Node(path, i);
1933 max_cumul =
CapSub(max_cumul, path_transits.Transit(path, i));
1936 const std::vector<std::pair<int, int>>& pickup_index_pairs =
1937 routing_model_.GetPickupIndexPairs(node_index);
1938 const std::vector<std::pair<int, int>>& delivery_index_pairs =
1939 routing_model_.GetDeliveryIndexPairs(node_index);
1940 if (!pickup_index_pairs.empty()) {
1944 DCHECK(delivery_index_pairs.empty());
1945 DCHECK_EQ(pickup_index_pairs.size(), 1);
1946 const int pair_index = pickup_index_pairs[0].first;
1948 const int delivery_index =
1949 visited_delivery_and_min_cumul_per_pair[pair_index].first;
1950 if (delivery_index < 0) {
1954 const int64_t cumul_diff_limit =
1955 dimension_.GetPickupToDeliveryLimitForPair(
1956 pair_index, pickup_index_pairs[0].second, delivery_index);
1957 if (
CapSub(visited_delivery_and_min_cumul_per_pair[pair_index].second,
1958 max_cumul) > cumul_diff_limit) {
1962 if (!delivery_index_pairs.empty()) {
1965 DCHECK(pickup_index_pairs.empty());
1966 DCHECK_EQ(delivery_index_pairs.size(), 1);
1967 const int pair_index = delivery_index_pairs[0].first;
1968 std::pair<int, int64_t>& delivery_index_and_cumul =
1969 visited_delivery_and_min_cumul_per_pair[pair_index];
1970 int& delivery_index = delivery_index_and_cumul.first;
1971 DCHECK_EQ(delivery_index, -1);
1972 delivery_index = delivery_index_pairs[0].second;
1973 delivery_index_and_cumul.second = min_path_cumuls[i];
1979 void PathCumulFilter::StoreMinMaxCumulOfNodesOnPath(
1980 int path,
const std::vector<int64_t>& min_path_cumuls,
bool is_delta) {
1981 const PathTransits& path_transits =
1982 is_delta ? delta_path_transits_ : current_path_transits_;
1984 const int path_size = path_transits.PathSize(path);
1985 DCHECK_EQ(min_path_cumuls.size(), path_size);
1987 int64_t max_cumul =
cumuls_[path_transits.Node(path, path_size - 1)]->Max();
1988 for (
int i = path_size - 1; i >= 0; i--) {
1989 const int node_index = path_transits.Node(path, i);
1991 if (i < path_size - 1) {
1992 max_cumul =
CapSub(max_cumul, path_transits.Transit(path, i));
1996 if (is_delta && node_index_to_precedences_[node_index].empty()) {
2001 std::pair<int64_t, int64_t>& min_max_cumuls =
2002 is_delta ? node_with_precedence_to_delta_min_max_cumuls_[node_index]
2003 : current_min_max_node_cumuls_[node_index];
2004 min_max_cumuls.first = min_path_cumuls[i];
2005 min_max_cumuls.second = max_cumul;
2007 if (is_delta && !routing_model_.IsEnd(node_index) &&
2008 (min_max_cumuls.first !=
2009 current_min_max_node_cumuls_[node_index].first ||
2010 max_cumul != current_min_max_node_cumuls_[node_index].second)) {
2011 delta_nodes_with_precedences_and_changed_cumul_.
Set(node_index);
2016 int64_t PathCumulFilter::ComputePathMaxStartFromEndCumul(
2017 const PathTransits& path_transits,
int path, int64_t path_start,
2018 int64_t min_end_cumul)
const {
2019 int64_t cumul_from_min_end = min_end_cumul;
2020 int64_t cumul_from_max_end =
2021 cumuls_[routing_model_.End(start_to_vehicle_[path_start])]->Max();
2022 for (
int i = path_transits.PathSize(path) - 2; i >= 0; --i) {
2023 const int64_t transit = path_transits.Transit(path, i);
2024 const int64_t node = path_transits.Node(path, i);
2025 cumul_from_min_end =
2027 cumul_from_max_end = dimension_.GetLastPossibleLessOrEqualValueForNode(
2028 node,
CapSub(cumul_from_max_end, transit));
2030 return std::min(cumul_from_min_end, cumul_from_max_end);
2036 bool propagate_own_objective_value,
2037 bool filter_objective_cost,
2040 return model.solver()->RevAlloc(
2041 new PathCumulFilter(
model, dimension, propagate_own_objective_value,
2042 filter_objective_cost, can_use_lp));
2047 bool DimensionHasCumulCost(
const RoutingDimension& dimension) {
2048 if (dimension.global_span_cost_coefficient() != 0)
return true;
2049 if (dimension.HasSoftSpanUpperBounds())
return true;
2050 if (dimension.HasQuadraticCostSoftSpanUpperBounds())
return true;
2051 for (
const int64_t
coefficient : dimension.vehicle_span_cost_coefficients()) {
2054 for (
int i = 0; i < dimension.cumuls().size(); ++i) {
2055 if (dimension.HasCumulVarSoftUpperBound(i))
return true;
2056 if (dimension.HasCumulVarSoftLowerBound(i))
return true;
2057 if (dimension.HasCumulVarPiecewiseLinearCost(i))
return true;
2062 bool DimensionHasPathCumulConstraint(
const RoutingDimension& dimension) {
2063 if (dimension.HasBreakConstraints())
return true;
2064 if (dimension.HasPickupToDeliveryLimits())
return true;
2065 for (
const int64_t
upper_bound : dimension.vehicle_span_upper_bounds()) {
2068 for (
const IntVar*
const slack : dimension.slacks()) {
2069 if (slack->Min() > 0)
return true;
2071 const std::vector<IntVar*>& cumuls = dimension.cumuls();
2072 for (
int i = 0; i < cumuls.size(); ++i) {
2073 IntVar*
const cumul_var = cumuls[i];
2074 if (cumul_var->Min() > 0 &&
2076 !dimension.model()->IsEnd(i)) {
2079 if (dimension.forbidden_intervals()[i].NumIntervals() > 0)
return true;
2088 const std::vector<RoutingDimension*>&
dimensions,
2089 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2095 const int num_vehicles = dimension->model()->vehicles();
2096 std::vector<Interval> path_capacity(num_vehicles);
2097 std::vector<int> path_class(num_vehicles);
2098 for (
int v = 0; v < num_vehicles; ++v) {
2099 const auto& vehicle_capacities = dimension->vehicle_capacities();
2100 path_capacity[v] = {0, vehicle_capacities[v]};
2101 path_class[v] = dimension->vehicle_to_class(v);
2108 const int num_vehicle_classes =
2109 1 + *std::max_element(path_class.begin(), path_class.end());
2110 const int num_cumuls = dimension->cumuls().size();
2111 const int num_slacks = dimension->slacks().size();
2112 std::vector<std::function<
Interval(int64_t, int64_t)>> transits(
2113 num_vehicle_classes,
nullptr);
2114 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
2117 const auto& unary_evaluator =
2118 dimension->GetUnaryTransitEvaluator(vehicle);
2119 if (unary_evaluator !=
nullptr) {
2120 transits[
vehicle_class] = [&unary_evaluator, dimension, num_slacks](
2122 if (node >= num_slacks)
return {0, 0};
2123 const int64_t min_transit = unary_evaluator(node);
2124 const int64_t max_transit =
2125 CapAdd(min_transit, dimension->SlackVar(node)->Max());
2126 return {min_transit, max_transit};
2129 const auto& binary_evaluator =
2130 dimension->GetBinaryTransitEvaluator(vehicle);
2132 transits[
vehicle_class] = [&binary_evaluator, dimension, num_slacks](
2134 if (node >= num_slacks)
return {0, 0};
2135 const int64_t min_transit = binary_evaluator(node,
next);
2136 const int64_t max_transit =
2137 CapAdd(min_transit, dimension->SlackVar(node)->Max());
2138 return {min_transit, max_transit};
2143 std::vector<Interval> node_capacity(num_cumuls);
2144 for (
int node = 0; node < num_cumuls; ++node) {
2145 const IntVar* cumul = dimension->CumulVar(node);
2146 node_capacity[node] = {cumul->
Min(), cumul->
Max()};
2149 auto checker = std::make_unique<DimensionChecker>(
2150 path_state, std::move(path_capacity), std::move(path_class),
2151 std::move(transits), std::move(node_capacity));
2152 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
2154 dimension->model()->solver(), std::move(checker), dimension->name());
2155 filters->push_back({filter, kAccept});
2160 const std::vector<RoutingDimension*>&
dimensions,
2161 const RoutingSearchParameters&
parameters,
bool filter_objective_cost,
2162 bool use_chain_cumul_filter,
2163 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2164 const auto kAccept = LocalSearchFilterManager::FilterEventType::kAccept;
2172 const int num_dimensions =
dimensions.size();
2174 const bool has_dimension_optimizers =
2175 !
parameters.disable_scheduling_beware_this_may_degrade_performance();
2176 std::vector<bool> use_path_cumul_filter(num_dimensions);
2177 std::vector<bool> use_cumul_bounds_propagator_filter(num_dimensions);
2178 std::vector<bool> use_global_lp_filter(num_dimensions);
2179 std::vector<bool> use_resource_assignment_filter(num_dimensions);
2180 for (
int d = 0; d < num_dimensions; d++) {
2182 const bool has_cumul_cost = DimensionHasCumulCost(dimension);
2183 use_path_cumul_filter[d] =
2184 has_cumul_cost || DimensionHasPathCumulConstraint(dimension);
2186 const int num_dimension_resource_groups =
2188 const bool can_use_cumul_bounds_propagator_filter =
2190 num_dimension_resource_groups == 0 &&
2191 (!filter_objective_cost || !has_cumul_cost);
2193 use_global_lp_filter[d] =
2194 has_dimension_optimizers &&
2195 ((has_precedences && !can_use_cumul_bounds_propagator_filter) ||
2196 (filter_objective_cost &&
2198 num_dimension_resource_groups > 1);
2200 use_cumul_bounds_propagator_filter[d] =
2201 has_precedences && !use_global_lp_filter[d];
2203 use_resource_assignment_filter[d] =
2204 has_dimension_optimizers && num_dimension_resource_groups > 0;
2207 for (
int d = 0; d < num_dimensions; d++) {
2214 const bool use_global_lp = use_global_lp_filter[d];
2215 const bool filter_resource_assignment = use_resource_assignment_filter[d];
2216 if (use_path_cumul_filter[d]) {
2219 !use_global_lp && !filter_resource_assignment,
2220 filter_objective_cost, has_dimension_optimizers),
2222 }
else if (use_chain_cumul_filter) {
2224 {
model.solver()->RevAlloc(
new ChainCumulFilter(
model, dimension)),
2228 if (use_cumul_bounds_propagator_filter[d]) {
2229 DCHECK(!use_global_lp);
2230 DCHECK(!filter_resource_assignment);
2235 if (filter_resource_assignment) {
2237 model.GetMutableLocalCumulLPOptimizer(dimension),
2238 model.GetMutableLocalCumulMPOptimizer(dimension),
2240 filter_objective_cost),
2244 if (use_global_lp) {
2246 model.GetMutableGlobalCumulLPOptimizer(dimension),
2247 model.GetMutableGlobalCumulMPOptimizer(dimension),
2248 filter_objective_cost),
2257 class PickupDeliveryFilter :
public BasePathFilter {
2259 PickupDeliveryFilter(
const std::vector<IntVar*>& nexts,
int next_domain_size,
2260 const RoutingModel::IndexPairs& pairs,
2261 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2263 ~PickupDeliveryFilter()
override {}
2264 bool AcceptPath(int64_t path_start, int64_t chain_start,
2265 int64_t chain_end)
override;
2266 std::string DebugString()
const override {
return "PickupDeliveryFilter"; }
2269 bool AcceptPathDefault(int64_t path_start);
2270 template <
bool lifo>
2271 bool AcceptPathOrdered(int64_t path_start);
2273 std::vector<int> pair_firsts_;
2274 std::vector<int> pair_seconds_;
2275 const RoutingModel::IndexPairs pairs_;
2276 SparseBitset<> visited_;
2277 std::deque<int> visited_deque_;
2278 const std::vector<RoutingModel::PickupAndDeliveryPolicy> vehicle_policies_;
2281 PickupDeliveryFilter::PickupDeliveryFilter(
2282 const std::vector<IntVar*>& nexts,
int next_domain_size,
2283 const RoutingModel::IndexPairs& pairs,
2284 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
2285 : BasePathFilter(nexts, next_domain_size),
2286 pair_firsts_(next_domain_size, kUnassigned),
2287 pair_seconds_(next_domain_size, kUnassigned),
2290 vehicle_policies_(vehicle_policies) {
2291 for (
int i = 0; i < pairs.size(); ++i) {
2292 const auto& index_pair = pairs[i];
2293 for (
int first : index_pair.first) {
2294 pair_firsts_[first] = i;
2296 for (
int second : index_pair.second) {
2297 pair_seconds_[second] = i;
2302 bool PickupDeliveryFilter::AcceptPath(int64_t path_start,
2305 switch (vehicle_policies_[GetPath(path_start)]) {
2306 case RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER:
2307 return AcceptPathDefault(path_start);
2308 case RoutingModel::PICKUP_AND_DELIVERY_LIFO:
2309 return AcceptPathOrdered<true>(path_start);
2310 case RoutingModel::PICKUP_AND_DELIVERY_FIFO:
2311 return AcceptPathOrdered<false>(path_start);
2317 bool PickupDeliveryFilter::AcceptPathDefault(int64_t path_start) {
2318 visited_.ClearAll();
2319 int64_t node = path_start;
2320 int64_t path_length = 1;
2321 while (node < Size()) {
2323 if (path_length > Size()) {
2326 if (pair_firsts_[node] != kUnassigned) {
2330 for (
int second : pairs_[pair_firsts_[node]].second) {
2331 if (visited_[second]) {
2336 if (pair_seconds_[node] != kUnassigned) {
2337 bool found_first =
false;
2338 bool some_synced =
false;
2339 for (
int first : pairs_[pair_seconds_[node]].first) {
2340 if (visited_[first]) {
2344 if (IsVarSynced(first)) {
2348 if (!found_first && some_synced) {
2353 const int64_t
next = GetNext(node);
2354 if (
next == kUnassigned) {
2361 for (
const int64_t node : visited_.PositionsSetAtLeastOnce()) {
2362 if (pair_firsts_[node] != kUnassigned) {
2363 bool found_second =
false;
2364 bool some_synced =
false;
2365 for (
int second : pairs_[pair_firsts_[node]].second) {
2366 if (visited_[second]) {
2367 found_second =
true;
2370 if (IsVarSynced(second)) {
2374 if (!found_second && some_synced) {
2382 template <
bool lifo>
2383 bool PickupDeliveryFilter::AcceptPathOrdered(int64_t path_start) {
2384 visited_deque_.clear();
2385 int64_t node = path_start;
2386 int64_t path_length = 1;
2387 while (node < Size()) {
2389 if (path_length > Size()) {
2392 if (pair_firsts_[node] != kUnassigned) {
2394 visited_deque_.push_back(node);
2396 visited_deque_.push_front(node);
2399 if (pair_seconds_[node] != kUnassigned) {
2400 bool found_first =
false;
2401 bool some_synced =
false;
2402 for (
int first : pairs_[pair_seconds_[node]].first) {
2403 if (!visited_deque_.empty() && visited_deque_.back() == first) {
2407 if (IsVarSynced(first)) {
2411 if (!found_first && some_synced) {
2413 }
else if (!visited_deque_.empty()) {
2414 visited_deque_.pop_back();
2417 const int64_t
next = GetNext(node);
2418 if (
next == kUnassigned) {
2425 while (!visited_deque_.empty()) {
2426 for (
int second : pairs_[pair_firsts_[visited_deque_.back()]].second) {
2427 if (IsVarSynced(second)) {
2431 visited_deque_.pop_back();
2440 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2442 return routing_model.
solver()->
RevAlloc(
new PickupDeliveryFilter(
2444 pairs, vehicle_policies));
2450 class VehicleVarFilter :
public BasePathFilter {
2452 explicit VehicleVarFilter(
const RoutingModel& routing_model);
2453 ~VehicleVarFilter()
override {}
2454 bool AcceptPath(int64_t path_start, int64_t chain_start,
2455 int64_t chain_end)
override;
2456 std::string DebugString()
const override {
return "VehicleVariableFilter"; }
2459 bool DisableFiltering()
const override;
2460 bool IsVehicleVariableConstrained(
int index)
const;
2462 std::vector<int64_t> start_to_vehicle_;
2463 std::vector<IntVar*> vehicle_vars_;
2464 const int64_t unconstrained_vehicle_var_domain_size_;
2467 VehicleVarFilter::VehicleVarFilter(
const RoutingModel& routing_model)
2468 : BasePathFilter(routing_model.Nexts(),
2469 routing_model.Size() + routing_model.vehicles()),
2470 vehicle_vars_(routing_model.VehicleVars()),
2471 unconstrained_vehicle_var_domain_size_(routing_model.vehicles()) {
2472 start_to_vehicle_.resize(Size(), -1);
2473 for (
int i = 0; i < routing_model.vehicles(); ++i) {
2474 start_to_vehicle_[routing_model.Start(i)] = i;
2478 bool VehicleVarFilter::AcceptPath(int64_t path_start, int64_t chain_start,
2479 int64_t chain_end) {
2480 const int64_t vehicle = start_to_vehicle_[path_start];
2481 int64_t node = chain_start;
2482 while (node != chain_end) {
2483 if (!vehicle_vars_[node]->Contains(vehicle)) {
2486 node = GetNext(node);
2488 return vehicle_vars_[node]->Contains(vehicle);
2491 bool VehicleVarFilter::DisableFiltering()
const {
2492 for (
int i = 0; i < vehicle_vars_.size(); ++i) {
2493 if (IsVehicleVariableConstrained(i))
return false;
2498 bool VehicleVarFilter::IsVehicleVariableConstrained(
int index)
const {
2499 const IntVar*
const vehicle_var = vehicle_vars_[
index];
2503 const int adjusted_unconstrained_vehicle_var_domain_size =
2504 vehicle_var->Min() >= 0 ? unconstrained_vehicle_var_domain_size_
2505 : unconstrained_vehicle_var_domain_size_ + 1;
2506 return vehicle_var->Size() != adjusted_unconstrained_vehicle_var_domain_size;
2513 return routing_model.
solver()->
RevAlloc(
new VehicleVarFilter(routing_model));
2518 class CumulBoundsPropagatorFilter :
public IntVarLocalSearchFilter {
2520 explicit CumulBoundsPropagatorFilter(
const RoutingDimension& dimension);
2521 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2522 int64_t objective_min, int64_t objective_max)
override;
2523 std::string DebugString()
const override {
2524 return "CumulBoundsPropagatorFilter(" + propagator_.dimension().name() +
2529 CumulBoundsPropagator propagator_;
2530 const int64_t cumul_offset_;
2531 SparseBitset<int64_t> delta_touched_;
2532 std::vector<int64_t> delta_nexts_;
2535 CumulBoundsPropagatorFilter::CumulBoundsPropagatorFilter(
2536 const RoutingDimension& dimension)
2537 : IntVarLocalSearchFilter(dimension.
model()->Nexts()),
2538 propagator_(&dimension),
2539 cumul_offset_(dimension.GetGlobalOptimizerOffset()),
2540 delta_touched_(Size()),
2541 delta_nexts_(Size()) {}
2543 bool CumulBoundsPropagatorFilter::Accept(
const Assignment*
delta,
2548 for (
const IntVarElement& delta_element :
2549 delta->IntVarContainer().elements()) {
2551 if (FindIndex(delta_element.Var(), &
index)) {
2552 if (!delta_element.Bound()) {
2557 delta_nexts_[
index] = delta_element.Value();
2560 const auto& next_accessor = [
this](int64_t
index) {
2564 return propagator_.PropagateCumulBounds(next_accessor, cumul_offset_);
2572 new CumulBoundsPropagatorFilter(dimension));
2577 class LPCumulFilter :
public IntVarLocalSearchFilter {
2579 LPCumulFilter(
const std::vector<IntVar*>& nexts,
2580 GlobalDimensionCumulOptimizer* optimizer,
2581 GlobalDimensionCumulOptimizer* mp_optimizer,
2582 bool filter_objective_cost);
2583 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2584 int64_t objective_min, int64_t objective_max)
override;
2585 int64_t GetAcceptedObjectiveValue()
const override;
2586 void OnSynchronize(
const Assignment*
delta)
override;
2587 int64_t GetSynchronizedObjectiveValue()
const override;
2588 std::string DebugString()
const override {
2589 return "LPCumulFilter(" + optimizer_.dimension()->name() +
")";
2593 GlobalDimensionCumulOptimizer& optimizer_;
2594 GlobalDimensionCumulOptimizer& mp_optimizer_;
2595 const bool filter_objective_cost_;
2596 int64_t synchronized_cost_without_transit_;
2597 int64_t delta_cost_without_transit_;
2598 SparseBitset<int64_t> delta_touched_;
2599 std::vector<int64_t> delta_nexts_;
2602 LPCumulFilter::LPCumulFilter(
const std::vector<IntVar*>& nexts,
2603 GlobalDimensionCumulOptimizer* optimizer,
2604 GlobalDimensionCumulOptimizer* mp_optimizer,
2605 bool filter_objective_cost)
2606 : IntVarLocalSearchFilter(nexts),
2607 optimizer_(*optimizer),
2608 mp_optimizer_(*mp_optimizer),
2609 filter_objective_cost_(filter_objective_cost),
2610 synchronized_cost_without_transit_(-1),
2611 delta_cost_without_transit_(-1),
2612 delta_touched_(Size()),
2613 delta_nexts_(Size()) {}
2615 bool LPCumulFilter::Accept(
const Assignment*
delta,
2617 int64_t , int64_t objective_max) {
2619 for (
const IntVarElement& delta_element :
2620 delta->IntVarContainer().elements()) {
2622 if (FindIndex(delta_element.Var(), &
index)) {
2623 if (!delta_element.Bound()) {
2628 delta_nexts_[
index] = delta_element.Value();
2631 const auto& next_accessor = [
this](int64_t
index) {
2635 if (!filter_objective_cost_) {
2637 delta_cost_without_transit_ = 0;
2639 optimizer_.ComputeCumuls(next_accessor, {},
nullptr,
nullptr,
nullptr);
2641 if (
status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY &&
2642 mp_optimizer_.ComputeCumuls(next_accessor, {},
nullptr,
nullptr,
2651 optimizer_.ComputeCumulCostWithoutFixedTransits(
2652 next_accessor, &delta_cost_without_transit_);
2657 if (delta_cost_without_transit_ > objective_max)
return false;
2659 if (
status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY &&
2660 mp_optimizer_.ComputeCumulCostWithoutFixedTransits(
2661 next_accessor, &delta_cost_without_transit_) !=
2666 return delta_cost_without_transit_ <= objective_max;
2669 int64_t LPCumulFilter::GetAcceptedObjectiveValue()
const {
2670 return delta_cost_without_transit_;
2673 void LPCumulFilter::OnSynchronize(
const Assignment* ) {
2676 const RoutingModel&
model = *optimizer_.dimension()->model();
2677 const auto& next_accessor = [
this, &
model](int64_t
index) {
2684 optimizer_.ComputeCumulCostWithoutFixedTransits(
2685 next_accessor, &synchronized_cost_without_transit_);
2689 synchronized_cost_without_transit_ = 0;
2691 if (
status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY &&
2692 mp_optimizer_.ComputeCumulCostWithoutFixedTransits(
2693 next_accessor, &synchronized_cost_without_transit_) !=
2697 synchronized_cost_without_transit_ = 0;
2701 int64_t LPCumulFilter::GetSynchronizedObjectiveValue()
const {
2702 return synchronized_cost_without_transit_;
2710 DCHECK_NE(optimizer,
nullptr);
2711 DCHECK_NE(mp_optimizer,
nullptr);
2713 return model.solver()->RevAlloc(
new LPCumulFilter(
2714 model.Nexts(), optimizer, mp_optimizer, filter_objective_cost));
2719 using ResourceGroup = RoutingModel::ResourceGroup;
2721 class ResourceGroupAssignmentFilter :
public BasePathFilter {
2723 ResourceGroupAssignmentFilter(
const std::vector<IntVar*>& nexts,
2724 const ResourceGroup* resource_group,
2725 LocalDimensionCumulOptimizer* lp_optimizer,
2726 LocalDimensionCumulOptimizer* mp_optimizer,
2727 bool filter_objective_cost);
2728 bool InitializeAcceptPath()
override;
2729 bool AcceptPath(int64_t path_start, int64_t chain_start,
2730 int64_t chain_end)
override;
2731 bool FinalizeAcceptPath(int64_t objective_min,
2732 int64_t objective_max)
override;
2733 void OnBeforeSynchronizePaths()
override;
2734 void OnSynchronizePathFromStart(int64_t
start)
override;
2735 void OnAfterSynchronizePaths()
override;
2737 int64_t GetAcceptedObjectiveValue()
const override {
2738 return lns_detected() ? 0 : delta_cost_without_transit_;
2740 int64_t GetSynchronizedObjectiveValue()
const override {
2741 return synchronized_cost_without_transit_;
2743 std::string DebugString()
const override {
2744 return "ResourceGroupAssignmentFilter(" + dimension_.name() +
")";
2748 const RoutingModel& model_;
2749 const RoutingDimension& dimension_;
2750 const ResourceGroup& resource_group_;
2751 LocalDimensionCumulOptimizer* lp_optimizer_;
2752 LocalDimensionCumulOptimizer* mp_optimizer_;
2753 const bool filter_objective_cost_;
2754 bool current_synch_failed_;
2755 int64_t synchronized_cost_without_transit_;
2756 int64_t delta_cost_without_transit_;
2757 std::vector<std::vector<int64_t>> vehicle_to_resource_assignment_costs_;
2758 std::vector<std::vector<int64_t>> delta_vehicle_to_resource_assignment_costs_;
2761 ResourceGroupAssignmentFilter::ResourceGroupAssignmentFilter(
2762 const std::vector<IntVar*>& nexts,
const ResourceGroup* resource_group,
2763 LocalDimensionCumulOptimizer* lp_optimizer,
2764 LocalDimensionCumulOptimizer* mp_optimizer,
bool filter_objective_cost)
2765 : BasePathFilter(nexts, lp_optimizer->dimension()->cumuls().size()),
2766 model_(*lp_optimizer->dimension()->
model()),
2767 dimension_(*lp_optimizer->dimension()),
2768 resource_group_(*resource_group),
2769 lp_optimizer_(lp_optimizer),
2770 mp_optimizer_(mp_optimizer),
2771 filter_objective_cost_(filter_objective_cost),
2772 current_synch_failed_(false),
2773 synchronized_cost_without_transit_(-1),
2774 delta_cost_without_transit_(-1) {
2775 vehicle_to_resource_assignment_costs_.resize(model_.
vehicles());
2776 delta_vehicle_to_resource_assignment_costs_.resize(model_.
vehicles());
2779 bool ResourceGroupAssignmentFilter::InitializeAcceptPath() {
2780 delta_vehicle_to_resource_assignment_costs_.assign(model_.vehicles(), {});
2783 int num_used_vehicles = 0;
2784 const int num_resources = resource_group_.Size();
2785 for (
int v : resource_group_.GetVehiclesRequiringAResource()) {
2786 if (GetNext(model_.Start(v)) != model_.End(v) ||
2787 model_.IsVehicleUsedWhenEmpty(v)) {
2788 if (++num_used_vehicles > num_resources) {
2796 bool ResourceGroupAssignmentFilter::AcceptPath(int64_t path_start,
2799 const int vehicle = model_.VehicleIndex(path_start);
2801 vehicle, resource_group_,
2802 [
this](int64_t
index) {
return GetNext(
index); },
2803 dimension_.transit_evaluator(vehicle), filter_objective_cost_,
2804 lp_optimizer_, mp_optimizer_,
2805 &delta_vehicle_to_resource_assignment_costs_[vehicle],
nullptr,
nullptr);
2808 bool ResourceGroupAssignmentFilter::FinalizeAcceptPath(
2809 int64_t , int64_t objective_max) {
2811 resource_group_.GetVehiclesRequiringAResource(), resource_group_.Size(),
2814 return PathStartTouched(model_.Start(v))
2815 ? &delta_vehicle_to_resource_assignment_costs_[v]
2816 : &vehicle_to_resource_assignment_costs_[v];
2819 return delta_cost_without_transit_ >= 0 &&
2820 delta_cost_without_transit_ <= objective_max;
2823 void ResourceGroupAssignmentFilter::OnBeforeSynchronizePaths() {
2824 current_synch_failed_ =
false;
2827 void ResourceGroupAssignmentFilter::OnSynchronizePathFromStart(int64_t
start) {
2832 const auto& next_accessor = [
this](int64_t
index) {
2834 : model_.IsStart(
index) ? model_.End(model_.VehicleIndex(
index))
2837 const int v = model_.VehicleIndex(
start);
2839 v, resource_group_, next_accessor, dimension_.transit_evaluator(v),
2840 filter_objective_cost_, lp_optimizer_, mp_optimizer_,
2841 &vehicle_to_resource_assignment_costs_[v],
nullptr,
nullptr)) {
2842 vehicle_to_resource_assignment_costs_[v].assign(resource_group_.Size(), -1);
2843 current_synch_failed_ =
true;
2847 void ResourceGroupAssignmentFilter::OnAfterSynchronizePaths() {
2848 synchronized_cost_without_transit_ =
2849 (current_synch_failed_ || !filter_objective_cost_)
2852 resource_group_.GetVehiclesRequiringAResource(),
2853 resource_group_.Size(),
2855 return &vehicle_to_resource_assignment_costs_[v];
2858 synchronized_cost_without_transit_ =
2859 std::max<int64_t>(synchronized_cost_without_transit_, 0);
2863 class ResourceAssignmentFilter :
public LocalSearchFilter {
2865 ResourceAssignmentFilter(
const std::vector<IntVar*>& nexts,
2866 LocalDimensionCumulOptimizer* optimizer,
2867 LocalDimensionCumulOptimizer* mp_optimizer,
2868 bool propagate_own_objective_value,
2869 bool filter_objective_cost);
2870 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2871 int64_t objective_min, int64_t objective_max)
override;
2872 void Synchronize(
const Assignment* assignment,
2873 const Assignment*
delta)
override;
2875 int64_t GetAcceptedObjectiveValue()
const override {
2876 return propagate_own_objective_value_ ? delta_cost_ : 0;
2878 int64_t GetSynchronizedObjectiveValue()
const override {
2879 return propagate_own_objective_value_ ? synchronized_cost_ : 0;
2881 std::string DebugString()
const override {
2882 return "ResourceAssignmentFilter(" + dimension_name_ +
")";
2886 std::vector<IntVarLocalSearchFilter*> resource_group_assignment_filters_;
2887 int64_t synchronized_cost_;
2888 int64_t delta_cost_;
2889 const bool propagate_own_objective_value_;
2890 const std::string dimension_name_;
2893 ResourceAssignmentFilter::ResourceAssignmentFilter(
2894 const std::vector<IntVar*>& nexts, LocalDimensionCumulOptimizer* optimizer,
2895 LocalDimensionCumulOptimizer* mp_optimizer,
2896 bool propagate_own_objective_value,
bool filter_objective_cost)
2897 : propagate_own_objective_value_(propagate_own_objective_value),
2898 dimension_name_(optimizer->dimension()->
name()) {
2899 const RoutingModel&
model = *optimizer->dimension()->model();
2900 for (
const auto& resource_group :
model.GetResourceGroups()) {
2901 resource_group_assignment_filters_.push_back(
2902 model.solver()->RevAlloc(
new ResourceGroupAssignmentFilter(
2903 nexts, resource_group.get(), optimizer, mp_optimizer,
2904 filter_objective_cost)));
2908 bool ResourceAssignmentFilter::Accept(
const Assignment*
delta,
2909 const Assignment* deltadelta,
2910 int64_t objective_min,
2911 int64_t objective_max) {
2913 for (LocalSearchFilter* group_filter : resource_group_assignment_filters_) {
2914 if (!group_filter->Accept(
delta, deltadelta, objective_min,
2919 std::max(delta_cost_, group_filter->GetAcceptedObjectiveValue());
2920 DCHECK_LE(delta_cost_, objective_max)
2921 <<
"ResourceGroupAssignmentFilter should return false when the "
2922 "objective_max is exceeded.";
2927 void ResourceAssignmentFilter::Synchronize(
const Assignment* assignment,
2928 const Assignment*
delta) {
2929 synchronized_cost_ = 0;
2930 for (LocalSearchFilter* group_filter : resource_group_assignment_filters_) {
2931 group_filter->Synchronize(assignment,
delta);
2933 synchronized_cost_, group_filter->GetSynchronizedObjectiveValue());
2942 bool propagate_own_objective_value,
bool filter_objective_cost) {
2944 DCHECK_NE(optimizer,
nullptr);
2945 DCHECK_NE(mp_optimizer,
nullptr);
2946 return model.solver()->RevAlloc(
new ResourceAssignmentFilter(
2947 model.Nexts(), optimizer, mp_optimizer, propagate_own_objective_value,
2948 filter_objective_cost));
2965 class CPFeasibilityFilter :
public IntVarLocalSearchFilter {
2967 explicit CPFeasibilityFilter(RoutingModel* routing_model);
2968 ~CPFeasibilityFilter()
override {}
2969 std::string DebugString()
const override {
return "CPFeasibilityFilter"; }
2970 bool Accept(
const Assignment*
delta,
const Assignment* deltadelta,
2971 int64_t objective_min, int64_t objective_max)
override;
2972 void OnSynchronize(
const Assignment*
delta)
override;
2975 void AddDeltaToAssignment(
const Assignment*
delta, Assignment* assignment);
2977 static const int64_t kUnassigned;
2978 const RoutingModel*
const model_;
2979 Solver*
const solver_;
2980 Assignment*
const assignment_;
2981 Assignment*
const temp_assignment_;
2982 DecisionBuilder*
const restore_;
2983 SearchLimit*
const limit_;
2986 const int64_t CPFeasibilityFilter::kUnassigned = -1;
2988 CPFeasibilityFilter::CPFeasibilityFilter(RoutingModel* routing_model)
2989 : IntVarLocalSearchFilter(routing_model->Nexts()),
2990 model_(routing_model),
2991 solver_(routing_model->solver()),
2992 assignment_(solver_->MakeAssignment()),
2993 temp_assignment_(solver_->MakeAssignment()),
2994 restore_(solver_->MakeRestoreAssignment(temp_assignment_)),
2995 limit_(solver_->MakeCustomLimit(
2996 [routing_model]() {
return routing_model->CheckLimit(); })) {
2997 assignment_->Add(routing_model->Nexts());
3000 bool CPFeasibilityFilter::Accept(
const Assignment*
delta,
3004 temp_assignment_->Copy(assignment_);
3005 AddDeltaToAssignment(
delta, temp_assignment_);
3007 return solver_->Solve(restore_,
limit_);
3010 void CPFeasibilityFilter::OnSynchronize(
const Assignment*
delta) {
3011 AddDeltaToAssignment(
delta, assignment_);
3014 void CPFeasibilityFilter::AddDeltaToAssignment(
const Assignment*
delta,
3015 Assignment* assignment) {
3016 if (
delta ==
nullptr) {
3019 Assignment::IntContainer*
const container =
3020 assignment->MutableIntVarContainer();
3021 const Assignment::IntContainer& delta_container =
delta->IntVarContainer();
3022 const int delta_size = delta_container.Size();
3024 for (
int i = 0; i < delta_size; i++) {
3025 const IntVarElement& delta_element = delta_container.Element(i);
3026 IntVar*
const var = delta_element.Var();
3027 int64_t
index = kUnassigned;
3030 if (!FindIndex(
var, &
index))
continue;
3032 const int64_t
value = delta_element.Value();
3035 if (model_->IsStart(
index)) {
3036 if (model_->IsEnd(
value)) {
3038 container->MutableElement(
index)->Deactivate();
3041 container->MutableElement(
index)->Activate();
3051 new CPFeasibilityFilter(routing_model));
std::vector< int > dimensions
const E & Element(const V *const var) const
An Assignment is a variable -> domains mapping, used to report solutions to the user.
AssignmentContainer< IntVar, IntVarElement > IntContainer
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max) override
Accepts a "delta" given the assignment with which the filter has been synchronized; the delta holds t...
int64_t Start(int i) const
static const int64_t kUnassigned
BasePathFilter(const std::vector< IntVar * > &nexts, int next_domain_size)
void OnSynchronize(const Assignment *delta) override
const RoutingDimension * dimension() const
virtual int64_t Min() const =0
virtual int64_t Max() const =0
The class IntVar is a subset of IntExpr.
bool FindIndex(IntVar *const var, int64_t *index) const
int64_t Value(int index) const
bool IsVarSynced(int index) const
const RoutingDimension * dimension() const
Local Search Filters are used for fast neighbor pruning.
Dimensions represent quantities accumulated at nodes along the routes.
RoutingModel * model() const
Returns the model on which the dimension was created.
int64_t global_span_cost_coefficient() const
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
const std::vector< NodePrecedence > & GetNodePrecedences() const
const std::vector< int > & GetDimensionResourceGroupIndices(const RoutingDimension *dimension) const
Returns the indices of resource groups for this dimension.
int64_t Size() const
Returns the number of next variables in the model.
Solver * solver() const
Returns the underlying constraint solver.
RoutingIndexPairs IndexPairs
int vehicles() const
Returns the number of vehicle routes in the model.
const std::vector< IntVar * > & Nexts() const
Returns all next variables of the model, such that Nexts(i) is the next variable of the node correspo...
RoutingDisjunctionIndex DisjunctionIndex
T * RevAlloc(T *object)
Registers the given object as being reversible.
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Set(IntegerType index)
const std::vector< IntVar * > cumuls_
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeCumulBoundsPropagatorFilter(const RoutingDimension &dimension)
Returns a filter handling dimension cumul bounds.
int64_t ComputeBestVehicleToResourceAssignment(std::vector< int > vehicles, int num_resources, std::function< const std::vector< int64_t > *(int)> vehicle_to_resource_assignment_costs, std::vector< int > *resource_indices)
int64_t CapSub(int64_t x, int64_t y)
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
LocalSearchFilter * MakeResourceAssignmentFilter(LocalDimensionCumulOptimizer *optimizer, LocalDimensionCumulOptimizer *mp_optimizer, bool propagate_own_objective_value, bool filter_objective_cost)
Returns a filter checking the feasibility and cost of the resource assignment.
IntVarLocalSearchFilter * MakeGlobalLPCumulFilter(GlobalDimensionCumulOptimizer *optimizer, GlobalDimensionCumulOptimizer *mp_optimizer, bool filter_objective_cost)
Returns a filter checking global linear constraints and costs.
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, const std::string &dimension_name)
DimensionSchedulingStatus
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
int64_t CapProd(int64_t x, int64_t y)
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, bool use_chain_cumul_filter, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
IntVarLocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model)
Returns a filter checking that vehicle variable domains are respected.
IntVarLocalSearchFilter * MakePathCumulFilter(const RoutingDimension &dimension, bool propagate_own_objective_value, bool filter_objective_cost, bool can_use_lp)
Returns a filter handling dimension costs and constraints.
IntVarLocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const RoutingModel::IndexPairs &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
Returns a filter enforcing pickup and delivery constraints for the given pair of nodes and given poli...
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
static const int kUnassigned
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
Appends dimension-based filters to the given list of filters using a path state.
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model, bool filter_cost)
Returns a filter ensuring that node disjunction constraints are enforced.
bool ComputeVehicleToResourcesAssignmentCosts(int v, const RoutingModel::ResourceGroup &resource_group, const std::function< int64_t(int64_t)> &next_accessor, const std::function< int64_t(int64_t, int64_t)> &transit_accessor, bool optimize_vehicle_costs, LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, std::vector< int64_t > *assignment_costs, std::vector< std::vector< int64_t >> *cumul_values, std::vector< std::vector< int64_t >> *break_values)
ABSL_FLAG(bool, routing_strong_debug_checks, false, "Run stronger checks in debug; these stronger tests might change " "the complexity of the code in particular.")
std::vector< int64_t > path_values
std::optional< int64_t > end