32#include "absl/algorithm/container.h"
33#include "absl/container/btree_set.h"
34#include "absl/container/flat_hash_map.h"
35#include "absl/container/flat_hash_set.h"
36#include "absl/flags/flag.h"
37#include "absl/log/check.h"
38#include "absl/strings/str_cat.h"
39#include "absl/strings/string_view.h"
40#include "absl/types/span.h"
49#include "ortools/constraint_solver/routing_parameters.pb.h"
57 "Run stronger checks in debug; these stronger tests might change "
58 "the complexity of the code in particular.");
65class RouteConstraintFilter :
public BasePathFilter {
67 explicit RouteConstraintFilter(
const RoutingModel& routing_model)
68 : BasePathFilter(routing_model.Nexts(),
69 routing_model.Size() + routing_model.vehicles(),
70 routing_model.GetPathsMetadata()),
71 routing_model_(routing_model),
72 current_vehicle_cost_(0),
73 delta_vehicle_cost_(0),
74 current_vehicle_costs_(routing_model.vehicles(), 0) {
75 start_to_vehicle_.resize(Size(), -1);
76 vehicle_to_start_.resize(routing_model.vehicles());
77 for (
int v = 0; v < routing_model.vehicles(); v++) {
78 const int64_t start = routing_model.Start(v);
79 start_to_vehicle_[start] = v;
80 vehicle_to_start_[v] = start;
83 ~RouteConstraintFilter()
override =
default;
84 std::string DebugString()
const override {
return "RouteConstraintFilter"; }
85 int64_t GetSynchronizedObjectiveValue()
const override {
86 return current_vehicle_cost_;
88 int64_t GetAcceptedObjectiveValue()
const override {
89 return lns_detected() ? 0 : delta_vehicle_cost_;
93 void OnSynchronizePathFromStart(int64_t start)
override {
96 while (node < Size()) {
97 route_.push_back(node);
100 route_.push_back(node);
101 std::optional<int64_t> route_cost = routing_model_.GetRouteCost(route_);
102 DCHECK(route_cost.has_value());
103 current_vehicle_costs_[start_to_vehicle_[start]] = route_cost.value();
105 void OnAfterSynchronizePaths()
override {
106 current_vehicle_cost_ = 0;
107 for (
int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
108 const int64_t start = vehicle_to_start_[vehicle];
109 DCHECK_EQ(vehicle, start_to_vehicle_[start]);
110 if (!IsVarSynced(start)) {
113 CapAddTo(current_vehicle_costs_[vehicle], ¤t_vehicle_cost_);
116 bool InitializeAcceptPath()
override {
117 delta_vehicle_cost_ = current_vehicle_cost_;
120 bool AcceptPath(int64_t path_start, int64_t ,
122 delta_vehicle_cost_ =
123 CapSub(delta_vehicle_cost_,
124 current_vehicle_costs_[start_to_vehicle_[path_start]]);
126 int64_t node = path_start;
127 while (node < Size()) {
128 route_.push_back(node);
129 node = GetNext(node);
131 route_.push_back(node);
132 std::optional<int64_t> route_cost = routing_model_.GetRouteCost(route_);
133 if (!route_cost.has_value()) {
136 CapAddTo(route_cost.value(), &delta_vehicle_cost_);
139 bool FinalizeAcceptPath(int64_t ,
140 int64_t objective_max)
override {
141 return delta_vehicle_cost_ <= objective_max;
144 const RoutingModel& routing_model_;
145 int64_t current_vehicle_cost_;
146 int64_t delta_vehicle_cost_;
147 std::vector<int64_t> current_vehicle_costs_;
148 std::vector<int64_t> vehicle_to_start_;
149 std::vector<int> start_to_vehicle_;
150 std::vector<int64_t> route_;
156 const RoutingModel& routing_model) {
157 return routing_model.solver()->RevAlloc(
158 new RouteConstraintFilter(routing_model));
165class MaxActiveVehiclesFilter :
public IntVarLocalSearchFilter {
167 explicit MaxActiveVehiclesFilter(
const RoutingModel& routing_model)
168 : IntVarLocalSearchFilter(routing_model.Nexts()),
169 routing_model_(routing_model),
170 is_active_(routing_model.vehicles(), false),
171 active_vehicles_(0) {}
172 bool Accept(
const Assignment* delta,
const Assignment* ,
173 int64_t , int64_t )
override {
176 int current_active_vehicles = active_vehicles_;
177 for (
const IntVarElement& new_element : container.elements()) {
178 IntVar*
const var = new_element.Var();
180 if (FindIndex(var, &index) && routing_model_.IsStart(index)) {
181 if (new_element.Min() != new_element.Max()) {
185 const int vehicle = routing_model_.VehicleIndex(index);
186 const bool is_active =
187 (new_element.Min() != routing_model_.End(vehicle));
188 if (is_active && !is_active_[vehicle]) {
189 ++current_active_vehicles;
190 }
else if (!is_active && is_active_[vehicle]) {
191 --current_active_vehicles;
195 return current_active_vehicles <=
196 routing_model_.GetMaximumNumberOfActiveVehicles();
200 void OnSynchronize(
const Assignment* )
override {
201 active_vehicles_ = 0;
202 for (
int i = 0;
i < routing_model_.vehicles(); ++
i) {
203 const int index = routing_model_.Start(i);
204 if (IsVarSynced(index) &&
Value(index) != routing_model_.End(i)) {
205 is_active_[
i] =
true;
208 is_active_[
i] =
false;
213 const RoutingModel& routing_model_;
214 std::vector<bool> is_active_;
215 int active_vehicles_;
220 const RoutingModel& routing_model) {
221 return routing_model.solver()->RevAlloc(
222 new MaxActiveVehiclesFilter(routing_model));
227class ActiveNodeGroupFilter :
public IntVarLocalSearchFilter {
229 explicit ActiveNodeGroupFilter(
const RoutingModel& routing_model)
230 : IntVarLocalSearchFilter(routing_model.Nexts()),
231 routing_model_(routing_model),
232 active_count_per_group_(routing_model.GetSameActivityGroupsCount(),
233 {.active = 0, .unknown = 0}),
234 node_is_active_(routing_model.Nexts().size(),
false),
235 node_is_unknown_(routing_model.Nexts().size(),
false) {}
237 bool Accept(
const Assignment* delta,
const Assignment* ,
238 int64_t , int64_t )
override {
239 active_count_per_group_.Revert();
241 for (
const IntVarElement& new_element : container.elements()) {
242 IntVar*
const var = new_element.Var();
244 if (!FindIndex(var, &index))
continue;
245 const int group = routing_model_.GetSameActivityGroupOfIndex(index);
246 ActivityCounts counts = active_count_per_group_.Get(group);
248 if (node_is_unknown_[index]) --counts.unknown;
249 if (node_is_active_[index]) --counts.active;
250 if (new_element.Min() != new_element.Max()) {
252 }
else if (new_element.Min() != index) {
255 active_count_per_group_.Set(group, counts);
257 for (
const int group : active_count_per_group_.ChangedIndices()) {
258 const ActivityCounts counts = active_count_per_group_.Get(group);
259 const int group_size =
260 routing_model_.GetSameActivityIndicesOfGroup(group).size();
263 if (counts.active == 0)
continue;
264 if (counts.active <= group_size &&
265 group_size <= counts.active + counts.unknown) {
272 std::string DebugString()
const override {
return "ActiveNodeGroupFilter"; }
275 void OnSynchronize(
const Assignment* )
override {
276 const int num_groups = routing_model_.GetSameActivityGroupsCount();
277 for (
int group = 0; group < num_groups; ++group) {
278 ActivityCounts counts = {.active = 0, .unknown = 0};
279 for (
int node : routing_model_.GetSameActivityIndicesOfGroup(group)) {
280 if (IsVarSynced(node)) {
281 const bool is_active = (
Value(node) != node);
282 node_is_active_[node] = is_active;
283 node_is_unknown_[node] =
false;
284 counts.active += is_active ? 1 : 0;
287 node_is_unknown_[node] =
true;
288 node_is_active_[node] =
false;
291 active_count_per_group_.Set(group, counts);
293 active_count_per_group_.Commit();
296 const RoutingModel& routing_model_;
297 struct ActivityCounts {
301 CommittableArray<ActivityCounts> active_count_per_group_;
304 std::vector<bool> node_is_active_;
307 std::vector<bool> node_is_unknown_;
313 const RoutingModel& routing_model) {
314 return routing_model.solver()->RevAlloc(
315 new ActiveNodeGroupFilter(routing_model));
321class NodeDisjunctionFilter :
public IntVarLocalSearchFilter {
323 explicit NodeDisjunctionFilter(
const RoutingModel& routing_model,
325 : IntVarLocalSearchFilter(routing_model.Nexts()),
326 routing_model_(routing_model),
327 count_per_disjunction_(routing_model.GetNumberOfDisjunctions(),
328 {.active = 0, .inactive = 0}),
329 synchronized_objective_value_(std::numeric_limits<int64_t>::min()),
330 accepted_objective_value_(std::numeric_limits<int64_t>::min()),
331 filter_cost_(filter_cost),
332 has_mandatory_disjunctions_(routing_model.HasMandatoryDisjunctions()) {}
334 using Disjunction = RoutingModel::DisjunctionIndex;
336 bool Accept(
const Assignment* delta,
const Assignment* ,
337 int64_t , int64_t objective_max)
override {
338 count_per_disjunction_.Revert();
339 bool lns_detected =
false;
341 for (
const IntVarElement& element : delta->IntVarContainer().elements()) {
343 if (!FindIndex(element.Var(), &node))
continue;
344 lns_detected |= element.Min() != element.Max();
346 const bool is_var_synced = IsVarSynced(node);
347 const bool was_active = is_var_synced &&
Value(node) != node;
348 const bool is_active = node < element.Min() || element.Max() < node;
349 ActivityCount contribution_delta = {.active = 0, .inactive = 0};
351 contribution_delta.active -= was_active;
352 contribution_delta.inactive -= !was_active;
354 contribution_delta.active += is_active;
355 contribution_delta.inactive += !is_active;
357 if (contribution_delta.active == 0 && contribution_delta.inactive == 0) {
361 for (
const Disjunction disjunction :
362 routing_model_.GetDisjunctionIndices(node)) {
363 ActivityCount new_count =
364 count_per_disjunction_.Get(disjunction.value());
365 new_count.active += contribution_delta.active;
366 new_count.inactive += contribution_delta.inactive;
367 count_per_disjunction_.Set(disjunction.value(), new_count);
371 for (
const int index : count_per_disjunction_.ChangedIndices()) {
372 if (count_per_disjunction_.Get(index).active >
373 routing_model_.GetDisjunctionMaxCardinality(Disjunction(index))) {
377 if (lns_detected || (!filter_cost_ && !has_mandatory_disjunctions_)) {
378 accepted_objective_value_ = 0;
382 accepted_objective_value_ = synchronized_objective_value_;
383 for (
const int index : count_per_disjunction_.ChangedIndices()) {
385 const int old_inactives =
386 count_per_disjunction_.GetCommitted(index).inactive;
387 const int new_inactives = count_per_disjunction_.Get(index).inactive;
388 if (old_inactives == new_inactives)
continue;
390 const Disjunction disjunction(index);
391 const int64_t penalty = routing_model_.GetDisjunctionPenalty(disjunction);
392 if (penalty == 0)
continue;
395 const int max_inactives =
396 routing_model_.GetDisjunctionNodeIndices(disjunction).size() -
397 routing_model_.GetDisjunctionMaxCardinality(disjunction);
398 int new_violation = std::max(0, new_inactives - max_inactives);
399 int old_violation = std::max(0, old_inactives - max_inactives);
401 if (penalty < 0 && new_violation > 0)
return false;
402 if (routing_model_.GetDisjunctionPenaltyCostBehavior(disjunction) ==
403 RoutingModel::PenaltyCostBehavior::PENALIZE_ONCE) {
404 new_violation = std::min(1, new_violation);
405 old_violation = std::min(1, old_violation);
408 &accepted_objective_value_);
411 return accepted_objective_value_ <= objective_max;
413 std::string DebugString()
const override {
return "NodeDisjunctionFilter"; }
414 int64_t GetSynchronizedObjectiveValue()
const override {
415 return synchronized_objective_value_;
417 int64_t GetAcceptedObjectiveValue()
const override {
418 return accepted_objective_value_;
422 void OnSynchronize(
const Assignment* )
override {
423 synchronized_objective_value_ = 0;
424 count_per_disjunction_.Revert();
425 const int num_disjunctions = routing_model_.GetNumberOfDisjunctions();
426 for (Disjunction disjunction(0); disjunction < num_disjunctions;
429 ActivityCount count = {.active = 0, .inactive = 0};
430 const auto& nodes = routing_model_.GetDisjunctionNodeIndices(disjunction);
431 for (
const int64_t node : nodes) {
432 if (!IsVarSynced(node))
continue;
433 const int is_active =
Value(node) != node;
434 count.active += is_active;
435 count.inactive += !is_active;
437 count_per_disjunction_.Set(disjunction.value(), count);
439 if (!filter_cost_)
continue;
440 const int64_t penalty = routing_model_.GetDisjunctionPenalty(disjunction);
441 const int max_actives =
442 routing_model_.GetDisjunctionMaxCardinality(disjunction);
443 int violation = count.inactive - (nodes.size() - max_actives);
444 if (violation > 0 && penalty > 0) {
445 if (routing_model_.GetDisjunctionPenaltyCostBehavior(disjunction) ==
446 RoutingModel::PenaltyCostBehavior::PENALIZE_ONCE) {
447 violation = std::min(1, violation);
452 count_per_disjunction_.Commit();
453 accepted_objective_value_ = synchronized_objective_value_;
456 const RoutingModel& routing_model_;
457 struct ActivityCount {
461 CommittableArray<ActivityCount> count_per_disjunction_;
462 int64_t synchronized_objective_value_;
463 int64_t accepted_objective_value_;
464 const bool filter_cost_;
465 const bool has_mandatory_disjunctions_;
470 const RoutingModel& routing_model,
bool filter_cost) {
471 return routing_model.solver()->RevAlloc(
472 new NodeDisjunctionFilter(routing_model, filter_cost));
478 int next_domain_size,
479 const PathsMetadata& paths_metadata)
481 paths_metadata_(paths_metadata),
483 new_synchronized_unperformed_nodes_(nexts.size()),
485 touched_paths_(nexts.size()),
488 lns_detected_(
false) {}
492 int64_t objective_min, int64_t objective_max) {
493 lns_detected_ =
false;
494 for (
const int touched : delta_touched_) {
497 delta_touched_.clear();
499 delta_touched_.reserve(container.
Size());
506 for (int64_t touched_path : touched_paths_.PositionsSetAtLeastOnce()) {
509 touched_paths_.ResetAllToFalse();
511 const auto update_touched_path_chain_start_end = [
this](int64_t index) {
512 const int64_t start = node_path_starts_[index];
514 touched_paths_.Set(start);
516 int64_t& chain_start = touched_path_chain_start_ends_[start].first;
517 if (chain_start ==
kUnassigned || paths_metadata_.IsStart(index) ||
518 ranks_[index] < ranks_[chain_start]) {
522 int64_t& chain_end = touched_path_chain_start_ends_[start].second;
523 if (chain_end ==
kUnassigned || paths_metadata_.IsEnd(index) ||
524 ranks_[index] > ranks_[chain_end]) {
533 if (!new_element.Bound()) {
535 lns_detected_ =
true;
538 new_nexts_[index] = new_element.Value();
539 delta_touched_.push_back(index);
540 update_touched_path_chain_start_end(index);
541 update_touched_path_chain_start_end(new_nexts_[index]);
545 if (!InitializeAcceptPath())
return false;
546 for (
const int64_t touched_start : touched_paths_.PositionsSetAtLeastOnce()) {
547 const std::pair<int64_t, int64_t> start_end =
548 touched_path_chain_start_ends_[touched_start];
549 if (!AcceptPath(touched_start, start_end.first, start_end.second)) {
555 return FinalizeAcceptPath(objective_min, objective_max);
558void BasePathFilter::ComputePathStarts(std::vector<int64_t>* path_starts,
559 std::vector<int>* index_to_path) {
560 path_starts->clear();
561 const int nexts_size =
Size();
564 for (
int i = 0; i < nexts_size; ++i) {
568 const int next =
Value(i);
569 if (next < nexts_size) {
574 for (
int i = 0;
i < nexts_size; ++
i) {
576 (*index_to_path)[
i] = path_starts->size();
577 path_starts->push_back(i);
582void BasePathFilter::SynchronizeFullAssignment() {
583 for (int64_t index = 0; index <
Size(); index++) {
587 new_synchronized_unperformed_nodes_.Set(index);
591 node_path_starts_.assign(node_path_starts_.size(),
kUnassigned);
593 const int nexts_size =
Size();
594 for (
int path = 0; path <
NumPaths(); ++path) {
595 const int64_t start =
Start(path);
596 node_path_starts_[start] = start;
598 int64_t next =
Value(start);
599 while (next < nexts_size) {
601 node_path_starts_[node] = start;
605 node_path_starts_[next] = start;
607 node_path_starts_[
End(path)] = start;
609 for (
const int touched : delta_touched_) {
612 delta_touched_.clear();
613 OnBeforeSynchronizePaths(
true);
615 OnAfterSynchronizePaths();
619 new_synchronized_unperformed_nodes_.ResetAllToFalse();
620 if (delta ==
nullptr || delta->
Empty() ||
621 absl::c_all_of(ranks_, [](
int rank) { return rank == kUnassigned; })) {
622 SynchronizeFullAssignment();
626 touched_paths_.ResetAllToFalse();
629 if (
FindIndex(new_element.Var(), &index)) {
630 const int64_t start = node_path_starts_[index];
632 touched_paths_.Set(start);
633 if (
Value(index) == index) {
635 DCHECK_LT(index, new_nexts_.size());
636 new_synchronized_unperformed_nodes_.Set(index);
642 for (
const int touched : delta_touched_) {
645 delta_touched_.clear();
646 OnBeforeSynchronizePaths(
false);
647 for (
const int64_t touched_start : touched_paths_.PositionsSetAtLeastOnce()) {
648 int64_t node = touched_start;
649 while (node <
Size()) {
650 node_path_starts_[node] = touched_start;
653 node_path_starts_[node] = touched_start;
654 UpdatePathRanksFromStart(touched_start);
655 OnSynchronizePathFromStart(touched_start);
657 OnAfterSynchronizePaths();
660void BasePathFilter::UpdateAllRanks() {
662 for (
int r = 0; r <
NumPaths(); ++r) {
663 const int64_t start =
Start(r);
665 UpdatePathRanksFromStart(start);
666 OnSynchronizePathFromStart(start);
670void BasePathFilter::UpdatePathRanksFromStart(
int start) {
672 int64_t node = start;
673 while (node <
Size()) {
685 explicit VehicleAmortizedCostFilter(
const RoutingModel& routing_model);
686 ~VehicleAmortizedCostFilter()
override =
default;
687 std::string DebugString()
const override {
688 return "VehicleAmortizedCostFilter";
690 int64_t GetSynchronizedObjectiveValue()
const override {
691 return current_vehicle_cost_;
693 int64_t GetAcceptedObjectiveValue()
const override {
694 return lns_detected() ? 0 : delta_vehicle_cost_;
698 void OnSynchronizePathFromStart(int64_t start)
override;
699 void OnAfterSynchronizePaths()
override;
700 bool InitializeAcceptPath()
override;
701 bool AcceptPath(int64_t path_start, int64_t chain_start,
702 int64_t chain_end)
override;
703 bool FinalizeAcceptPath(int64_t objective_min,
704 int64_t objective_max)
override;
706 int64_t current_vehicle_cost_;
707 int64_t delta_vehicle_cost_;
708 std::vector<int> current_route_lengths_;
709 std::vector<int64_t> start_to_end_;
710 std::vector<int> start_to_vehicle_;
711 std::vector<int64_t> vehicle_to_start_;
712 const std::vector<int64_t>& linear_cost_factor_of_vehicle_;
713 const std::vector<int64_t>& quadratic_cost_factor_of_vehicle_;
716VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
717 const RoutingModel& routing_model)
719 routing_model.Size() + routing_model.vehicles(),
720 routing_model.GetPathsMetadata()),
721 current_vehicle_cost_(0),
722 delta_vehicle_cost_(0),
723 current_route_lengths_(Size(), -1),
724 linear_cost_factor_of_vehicle_(
725 routing_model.GetAmortizedLinearCostFactorOfVehicles()),
726 quadratic_cost_factor_of_vehicle_(
727 routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) {
728 start_to_end_.resize(
Size(), -1);
729 start_to_vehicle_.resize(
Size(), -1);
730 vehicle_to_start_.resize(routing_model.vehicles());
731 for (
int v = 0; v < routing_model.vehicles(); v++) {
732 const int64_t start = routing_model.Start(v);
733 start_to_vehicle_[start] = v;
734 start_to_end_[start] = routing_model.End(v);
735 vehicle_to_start_[v] = start;
739void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(int64_t start) {
740 const int64_t
end = start_to_end_[start];
742 const int route_length = Rank(end) - 1;
743 CHECK_GE(route_length, 0);
744 current_route_lengths_[start] = route_length;
747void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
748 current_vehicle_cost_ = 0;
749 for (
int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
750 const int64_t start = vehicle_to_start_[vehicle];
751 DCHECK_EQ(vehicle, start_to_vehicle_[start]);
752 if (!IsVarSynced(start)) {
755 const int route_length = current_route_lengths_[start];
756 DCHECK_GE(route_length, 0);
758 if (route_length == 0) {
763 const int64_t linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle];
764 const int64_t route_length_cost =
765 CapProd(quadratic_cost_factor_of_vehicle_[vehicle],
766 route_length * route_length);
769 ¤t_vehicle_cost_);
773bool VehicleAmortizedCostFilter::InitializeAcceptPath() {
774 delta_vehicle_cost_ = current_vehicle_cost_;
778bool VehicleAmortizedCostFilter::AcceptPath(int64_t path_start,
782 const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start);
783 CHECK_GE(previous_chain_nodes, 0);
784 int new_chain_nodes = 0;
785 int64_t node = GetNext(chain_start);
786 while (node != chain_end) {
788 node = GetNext(node);
791 const int previous_route_length = current_route_lengths_[path_start];
792 CHECK_GE(previous_route_length, 0);
793 const int new_route_length =
794 previous_route_length - previous_chain_nodes + new_chain_nodes;
796 const int vehicle = start_to_vehicle_[path_start];
797 CHECK_GE(vehicle, 0);
798 DCHECK_EQ(path_start, vehicle_to_start_[vehicle]);
802 if (previous_route_length == 0) {
804 CHECK_GT(new_route_length, 0);
805 CapAddTo(linear_cost_factor_of_vehicle_[vehicle], &delta_vehicle_cost_);
806 }
else if (new_route_length == 0) {
808 delta_vehicle_cost_ =
809 CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
813 const int64_t quadratic_cost_factor =
814 quadratic_cost_factor_of_vehicle_[vehicle];
816 previous_route_length * previous_route_length),
817 &delta_vehicle_cost_);
818 delta_vehicle_cost_ =
CapSub(
820 CapProd(quadratic_cost_factor, new_route_length * new_route_length));
825bool VehicleAmortizedCostFilter::FinalizeAcceptPath(int64_t ,
826 int64_t objective_max) {
827 return delta_vehicle_cost_ <= objective_max;
833 const RoutingModel& routing_model) {
834 return routing_model.solver()->RevAlloc(
835 new VehicleAmortizedCostFilter(routing_model));
840class TypeRegulationsFilter :
public BasePathFilter {
842 explicit TypeRegulationsFilter(
const RoutingModel& model);
843 ~TypeRegulationsFilter()
override =
default;
844 std::string DebugString()
const override {
return "TypeRegulationsFilter"; }
847 void OnSynchronizePathFromStart(int64_t start)
override;
848 bool AcceptPath(int64_t path_start, int64_t chain_start,
849 int64_t chain_end)
override;
851 bool HardIncompatibilitiesRespected(
int vehicle, int64_t chain_start,
854 const RoutingModel& routing_model_;
855 std::vector<int> start_to_vehicle_;
858 std::vector<std::vector<int>> hard_incompatibility_type_counts_per_vehicle_;
860 TypeIncompatibilityChecker temporal_incompatibility_checker_;
861 TypeRequirementChecker requirement_checker_;
864TypeRegulationsFilter::TypeRegulationsFilter(
const RoutingModel& model)
865 : BasePathFilter(model.Nexts(), model.Size() + model.vehicles(),
866 model.GetPathsMetadata()),
867 routing_model_(model),
868 start_to_vehicle_(model.Size(), -1),
869 temporal_incompatibility_checker_(model,
871 requirement_checker_(model) {
872 const int num_vehicles = model.vehicles();
873 const bool has_hard_type_incompatibilities =
874 model.HasHardTypeIncompatibilities();
875 if (has_hard_type_incompatibilities) {
876 hard_incompatibility_type_counts_per_vehicle_.resize(num_vehicles);
878 const int num_visit_types = model.GetNumberOfVisitTypes();
879 for (
int vehicle = 0; vehicle < num_vehicles; vehicle++) {
880 const int64_t start = model.Start(vehicle);
881 start_to_vehicle_[start] = vehicle;
882 if (has_hard_type_incompatibilities) {
883 hard_incompatibility_type_counts_per_vehicle_[vehicle].resize(
889void TypeRegulationsFilter::OnSynchronizePathFromStart(int64_t start) {
890 if (!routing_model_.HasHardTypeIncompatibilities())
return;
892 const int vehicle = start_to_vehicle_[start];
893 CHECK_GE(vehicle, 0);
894 std::vector<int>& type_counts =
895 hard_incompatibility_type_counts_per_vehicle_[vehicle];
896 std::fill(type_counts.begin(), type_counts.end(), 0);
897 const int num_types = type_counts.size();
899 int64_t node = start;
900 while (node < Size()) {
901 DCHECK(IsVarSynced(node));
902 const int type = routing_model_.GetVisitType(node);
903 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
904 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
905 CHECK_LT(type, num_types);
912bool TypeRegulationsFilter::HardIncompatibilitiesRespected(
int vehicle,
915 if (!routing_model_.HasHardTypeIncompatibilities())
return true;
917 const std::vector<int>& previous_type_counts =
918 hard_incompatibility_type_counts_per_vehicle_[vehicle];
920 absl::flat_hash_map< int,
int> new_type_counts;
921 absl::flat_hash_set<int> types_to_check;
924 int64_t node = GetNext(chain_start);
925 while (node != chain_end) {
926 const int type = routing_model_.GetVisitType(node);
927 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
928 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
929 DCHECK_LT(type, previous_type_counts.size());
931 previous_type_counts[type]);
932 if (type_count++ == 0) {
934 types_to_check.insert(type);
937 node = GetNext(node);
942 if (IsVarSynced(chain_start)) {
943 node =
Value(chain_start);
944 while (node != chain_end) {
945 const int type = routing_model_.GetVisitType(node);
946 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
947 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
948 DCHECK_LT(type, previous_type_counts.size());
950 previous_type_counts[type]);
951 CHECK_GE(type_count, 1);
959 for (
int type : types_to_check) {
960 for (
int incompatible_type :
961 routing_model_.GetHardTypeIncompatibilitiesOfType(type)) {
963 previous_type_counts[incompatible_type]) > 0) {
971bool TypeRegulationsFilter::AcceptPath(int64_t path_start, int64_t chain_start,
973 const int vehicle = start_to_vehicle_[path_start];
974 CHECK_GE(vehicle, 0);
975 const auto next_accessor = [
this](int64_t node) {
return GetNext(node); };
976 return HardIncompatibilitiesRespected(vehicle, chain_start, chain_end) &&
977 temporal_incompatibility_checker_.CheckVehicle(vehicle,
979 requirement_checker_.CheckVehicle(vehicle, next_accessor);
985 const RoutingModel& routing_model) {
986 return routing_model.solver()->RevAlloc(
987 new TypeRegulationsFilter(routing_model));
997class ChainCumulFilter :
public BasePathFilter {
999 ChainCumulFilter(
const RoutingModel& routing_model,
1000 const RoutingDimension& dimension);
1001 ~ChainCumulFilter()
override =
default;
1002 std::string DebugString()
const override {
1003 return "ChainCumulFilter(" + name_ +
")";
1007 void OnSynchronizePathFromStart(int64_t start)
override;
1008 bool AcceptPath(int64_t path_start, int64_t chain_start,
1009 int64_t chain_end)
override;
1011 const std::vector<IntVar*> cumuls_;
1012 std::vector<int64_t> start_to_vehicle_;
1013 std::vector<int64_t> start_to_end_;
1014 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1015 const std::vector<int64_t> vehicle_capacities_;
1016 std::vector<int64_t> current_path_cumul_mins_;
1017 std::vector<int64_t> current_max_of_path_end_cumul_mins_;
1018 std::vector<int64_t> old_nexts_;
1019 std::vector<int> old_vehicles_;
1020 std::vector<int64_t> current_transits_;
1021 const std::string name_;
1024ChainCumulFilter::ChainCumulFilter(
const RoutingModel& routing_model,
1025 const RoutingDimension& dimension)
1026 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(),
1027 routing_model.GetPathsMetadata()),
1028 cumuls_(dimension.cumuls()),
1029 evaluators_(routing_model.vehicles(), nullptr),
1030 vehicle_capacities_(dimension.vehicle_capacities()),
1031 current_path_cumul_mins_(dimension.cumuls().size(), 0),
1032 current_max_of_path_end_cumul_mins_(dimension.cumuls().size(), 0),
1033 old_nexts_(routing_model.Size(), kUnassigned),
1034 old_vehicles_(routing_model.Size(), kUnassigned),
1035 current_transits_(routing_model.Size(), 0),
1036 name_(dimension.name()) {
1037 start_to_vehicle_.resize(Size(), -1);
1038 start_to_end_.resize(Size(), -1);
1039 for (
int i = 0;
i < routing_model.vehicles(); ++
i) {
1040 start_to_vehicle_[routing_model.Start(i)] =
i;
1041 start_to_end_[routing_model.Start(i)] = routing_model.End(i);
1042 evaluators_[
i] = &dimension.transit_evaluator(i);
1049void ChainCumulFilter::OnSynchronizePathFromStart(int64_t start) {
1050 const int vehicle = start_to_vehicle_[start];
1051 std::vector<int64_t> path_nodes;
1052 int64_t node = start;
1053 int64_t cumul = cumuls_[node]->Min();
1054 while (node < Size()) {
1055 path_nodes.push_back(node);
1056 current_path_cumul_mins_[node] = cumul;
1057 const int64_t next =
Value(node);
1058 if (next != old_nexts_[node] || vehicle != old_vehicles_[node]) {
1059 old_nexts_[node] = next;
1060 old_vehicles_[node] = vehicle;
1061 current_transits_[node] = (*evaluators_[vehicle])(node, next);
1063 CapAddTo(current_transits_[node], &cumul);
1064 cumul = std::max(cumuls_[next]->Min(), cumul);
1067 path_nodes.push_back(node);
1068 current_path_cumul_mins_[node] = cumul;
1069 int64_t max_cumuls = cumul;
1070 for (
int i = path_nodes.size() - 1; i >= 0; --i) {
1071 const int64_t node = path_nodes[
i];
1072 max_cumuls = std::max(max_cumuls, current_path_cumul_mins_[node]);
1073 current_max_of_path_end_cumul_mins_[node] = max_cumuls;
1078bool ChainCumulFilter::AcceptPath(int64_t path_start, int64_t chain_start,
1079 int64_t chain_end) {
1080 const int vehicle = start_to_vehicle_[path_start];
1081 const int64_t capacity = vehicle_capacities_[vehicle];
1082 int64_t node = chain_start;
1083 int64_t cumul = current_path_cumul_mins_[node];
1084 while (node != chain_end) {
1085 const int64_t next = GetNext(node);
1086 if (IsVarSynced(node) && next ==
Value(node) &&
1087 vehicle == old_vehicles_[node]) {
1088 CapAddTo(current_transits_[node], &cumul);
1090 CapAddTo((*evaluators_[vehicle])(node, next), &cumul);
1092 cumul = std::max(cumuls_[next]->Min(), cumul);
1093 if (cumul > capacity)
return false;
1096 const int64_t
end = start_to_end_[path_start];
1097 const int64_t end_cumul_delta =
1098 CapSub(current_path_cumul_mins_[end], current_path_cumul_mins_[node]);
1099 const int64_t after_chain_cumul_delta =
1100 CapSub(current_max_of_path_end_cumul_mins_[node],
1101 current_path_cumul_mins_[node]);
1102 return CapAdd(cumul, after_chain_cumul_delta) <= capacity &&
1103 CapAdd(cumul, end_cumul_delta) <= cumuls_[
end]->Max();
1109 int path, int64_t capacity, int64_t span_upper_bound,
1110 absl::Span<const DimensionValues::Interval> cumul_of_node,
1111 absl::Span<const DimensionValues::Interval> slack_of_node,
1112 absl::AnyInvocable<int64_t(int64_t, int64_t)
const> evaluator,
1116 const int num_nodes = dimension_values.
NumNodes(path);
1117 absl::Span<const int> nodes = dimension_values.
Nodes(path);
1118 absl::Span<Interval> cumuls = dimension_values.
MutableCumuls(path);
1119 for (
int r = 0; r < num_nodes; ++r) {
1120 const int node = nodes[r];
1121 Interval cumul = cumul_of_node[node];
1122 if (!cumul.DecreaseMax(capacity))
return false;
1129 absl::Span<int64_t> travels = dimension_values.
MutableTravels(path);
1131 absl::Span<const int> cnodes = dimension_values.
CommittedNodes(path);
1132 absl::Span<const int64_t> ctravels =
1140 const int i_limit = std::min(cnodes.size(), nodes.size());
1141 DCHECK(cnodes.empty() || cnodes[0] == nodes[0]);
1143 while (i < i_limit && cnodes[i] == nodes[i]) {
1144 travels[i - 1] = ctravels[i - 1];
1147 DCHECK(cnodes.empty() || cnodes.back() == nodes.back());
1148 int j = num_nodes - 2;
1149 const int delta = cnodes.size() - num_nodes;
1150 const int j_limit = i + std::max(0, -delta);
1151 while (j_limit <= j && nodes[j] == cnodes[j + delta]) {
1152 travels[j] = ctravels[j + delta];
1156 for (
int r = i; r <= j; ++r) {
1157 const int64_t travel = evaluator(nodes[r - 1], nodes[r]);
1158 travels[r - 1] = travel;
1162 absl::Span<Interval> transits = dimension_values.
MutableTransits(path);
1164 int64_t total_travel = 0;
1166 for (
int r = 1; r < num_nodes; ++r) {
1167 const int64_t travel = travels[r - 1];
1169 travel_sums[r] = total_travel;
1170 Interval transit{.min = travel, .max = travel};
1171 transit.Add(slack_of_node[nodes[r - 1]]);
1172 transits[r - 1] = transit;
1174 if (travel_sums.back() > span_upper_bound)
return false;
1175 dimension_values.
MutableSpan(path) = {.min = travel_sums.back(),
1176 .max = span_upper_bound};
1190 absl::AnyInvocable<int64_t(int64_t, int64_t)
const> pre_travel_evaluator,
1191 absl::AnyInvocable<int64_t(int64_t, int64_t)
const> post_travel_evaluator,
1193 const int num_nodes = dimension_values.
NumNodes(path);
1197 absl::Span<const int> nodes = dimension_values.
Nodes(path);
1198 if (pre_travel_evaluator ==
nullptr) {
1199 absl::c_fill(post_visits, 0);
1201 for (
int i = 0; i < num_nodes - 1; ++i) {
1202 post_visits[i] = pre_travel_evaluator(nodes[i], nodes[i + 1]);
1204 post_visits.back() = 0;
1206 if (post_travel_evaluator ==
nullptr) {
1207 absl::c_fill(pre_visits, 0);
1210 for (
int i = 1; i < num_nodes; ++i) {
1211 pre_visits[i] = post_travel_evaluator(nodes[i - 1], nodes[i]);
1218 absl::Span<
const std::pair<int64_t, int64_t>> interbreaks) {
1221 const int64_t total_travel = dimension_values.
TravelSums(path).back();
1225 int64_t lb_span_tw = total_travel;
1226 const absl::Span<Interval> cumuls = dimension_values.
MutableCumuls(path);
1227 Interval& start = cumuls.front();
1228 Interval&
end = cumuls.back();
1233 if (br.is_performed.min == 0)
continue;
1234 if (br.end.min <= start.max ||
end.min <= br.start.max)
continue;
1236 CapAddTo(br.duration.min, &lb_span_tw);
1237 if (!start.DecreaseMax(br.start.max))
return false;
1238 if (!
end.IncreaseMin(br.end.min))
return false;
1240 Interval& span = dimension_values.
MutableSpan(path);
1241 if (!span.IncreaseMin(std::max(lb_span_tw,
CapSub(
end.min, start.max)))) {
1249 if (!start.IncreaseMin(
CapSub(
end.min, span.max)))
return false;
1250 if (!
end.DecreaseMax(
CapAdd(start.max, span.max)))
return false;
1252 int num_feasible_breaks = 0;
1254 if (start.min <= br.start.max && br.end.min <=
end.max) {
1255 break_start_min = std::min(break_start_min, br.start.min);
1256 break_end_max = std::max(break_end_max, br.end.max);
1257 ++num_feasible_breaks;
1264 for (
const auto& [max_interbreak, min_break_duration] : interbreaks) {
1269 if (max_interbreak == 0) {
1270 if (total_travel > 0)
return false;
1273 int64_t min_num_breaks =
1274 std::max<int64_t>(0, (total_travel - 1) / max_interbreak);
1275 if (span.min > max_interbreak) {
1276 min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
1278 if (min_num_breaks > num_feasible_breaks)
return false;
1279 if (!span.IncreaseMin(
CapAdd(
1280 total_travel,
CapProd(min_num_breaks, min_break_duration)))) {
1283 if (min_num_breaks > 0) {
1284 if (!start.IncreaseMin(
CapSub(break_start_min, max_interbreak))) {
1287 if (!
end.DecreaseMax(
CapAdd(break_end_max, max_interbreak))) {
1292 return start.DecreaseMax(
CapSub(
end.max, span.min)) &&
1293 end.IncreaseMin(
CapAdd(start.min, span.min));
1298class PathCumulFilter :
public BasePathFilter {
1300 PathCumulFilter(
const RoutingModel& routing_model,
1301 const RoutingDimension& dimension,
1302 bool propagate_own_objective_value,
1303 bool filter_objective_cost,
bool may_use_optimizers);
1304 ~PathCumulFilter()
override =
default;
1305 std::string DebugString()
const override {
1306 return "PathCumulFilter(" + name_ +
")";
1308 int64_t GetSynchronizedObjectiveValue()
const override {
1309 return propagate_own_objective_value_ ? synchronized_objective_value_ : 0;
1311 int64_t GetAcceptedObjectiveValue()
const override {
1312 return lns_detected() || !propagate_own_objective_value_
1314 : accepted_objective_value_;
1316 bool UsesDimensionOptimizers() {
1317 if (!may_use_optimizers_)
return false;
1318 for (
int vehicle = 0; vehicle < routing_model_.vehicles(); ++vehicle) {
1319 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle))
return true;
1325 using Interval = DimensionValues::Interval;
1328 int64_t coefficient = 0;
1332 std::vector<Interval> ExtractInitialCumulIntervals();
1333 std::vector<Interval> ExtractInitialSlackIntervals();
1334 std::vector<std::vector<RoutingDimension::NodePrecedence>>
1335 ExtractNodeIndexToPrecedences()
const;
1336 std::vector<SoftBound> ExtractCumulSoftUpperBounds()
const;
1337 std::vector<SoftBound> ExtractCumulSoftLowerBounds()
const;
1338 std::vector<const PiecewiseLinearFunction*> ExtractCumulPiecewiseLinearCosts()
1340 std::vector<const RoutingModel::TransitCallback2*> ExtractEvaluators()
const;
1341 using VehicleBreak = DimensionValues::VehicleBreak;
1342 std::vector<std::vector<VehicleBreak>> ExtractInitialVehicleBreaks()
const;
1344 bool InitializeAcceptPath()
override {
1345 accepted_objective_value_ = synchronized_objective_value_;
1346 dimension_values_.Revert();
1347 cost_of_path_.Revert();
1348 global_span_cost_.Revert();
1349 location_of_node_.Revert();
1352 bool AcceptPath(int64_t path_start, int64_t chain_start,
1353 int64_t chain_end)
override;
1354 bool FinalizeAcceptPath(int64_t objective_min,
1355 int64_t objective_max)
override;
1357 void OnBeforeSynchronizePaths(
bool synchronizing_all_paths)
override;
1358 void OnSynchronizePathFromStart(int64_t start)
override;
1359 void OnAfterSynchronizePaths()
override;
1364 bool FillDimensionValues(
int path);
1367 bool PropagateTransits(
int path);
1370 bool PropagateTransitsWithoutForbiddenIntervals(
int path);
1372 bool PropagateTransitsWithForbiddenIntervals(
int path);
1374 bool PropagateSpan(
int path);
1376 bool PropagatePickupToDeliveryLimits(
int path);
1378 bool PropagateVehicleBreaks(
int path);
1381 bool FilterPrecedences()
const {
return !node_index_to_precedences_.empty(); }
1383 bool FilterGlobalSpanCost()
const {
1384 return global_span_cost_coefficient_ != 0;
1387 bool FilterVehicleBreaks(
int path)
const {
1388 return dimension_.HasBreakConstraints() &&
1389 (!dimension_.GetBreakIntervalsOfVehicle(path).empty() ||
1390 !dimension_.GetBreakDistanceDurationOfVehicle(path).empty());
1393 bool FilterSoftSpanCost(
int path)
const {
1394 return dimension_.HasSoftSpanUpperBounds() &&
1395 dimension_.GetSoftSpanUpperBoundForVehicle(path).cost > 0;
1398 bool FilterSoftSpanQuadraticCost(
int path)
const {
1399 if (!dimension_.HasQuadraticCostSoftSpanUpperBounds())
return false;
1400 const auto [
bound, cost] =
1401 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(path);
1405 bool FilterWithDimensionCumulOptimizerForVehicle(
int vehicle)
const {
1406 if (!may_use_optimizers_)
return false;
1407 if (!cumul_piecewise_linear_costs_.empty())
return false;
1409 int num_linear_constraints = 0;
1410 if (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0 ||
1411 dimension_.GetSlackCostCoefficientForVehicle(vehicle) > 0) {
1412 ++num_linear_constraints;
1414 if (FilterSoftSpanCost(vehicle)) ++num_linear_constraints;
1415 if (!cumul_soft_upper_bounds_.empty()) ++num_linear_constraints;
1416 if (!cumul_soft_lower_bounds_.empty()) ++num_linear_constraints;
1417 if (path_span_upper_bounds_[vehicle] <
kint64max) {
1418 ++num_linear_constraints;
1420 const bool has_breaks = FilterVehicleBreaks(vehicle);
1421 if (has_breaks) ++num_linear_constraints;
1429 return num_linear_constraints >= 2 &&
1430 (has_breaks || filter_objective_cost_);
1434 const RoutingModel& routing_model_;
1435 const RoutingDimension& dimension_;
1436 const std::vector<Interval> initial_cumul_;
1437 const std::vector<Interval> initial_slack_;
1438 const std::vector<std::vector<VehicleBreak>> initial_vehicle_breaks_;
1440 const std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1441 const std::vector<int64_t> path_capacities_;
1442 const std::vector<int64_t> path_span_upper_bounds_;
1443 const std::vector<int64_t> path_total_slack_cost_coefficients_;
1468 const int64_t global_span_cost_coefficient_;
1472 bool operator<(
const ValuedPath& other)
const {
1473 return std::tie(value, path) < std::tie(other.value, other.path);
1475 bool operator>(
const ValuedPath& other)
const {
1476 return std::tie(value, path) > std::tie(other.value, other.path);
1480 std::vector<ValuedPath> paths_by_decreasing_span_min_;
1482 std::vector<ValuedPath> paths_by_decreasing_end_min_;
1484 std::vector<ValuedPath> paths_by_increasing_start_max_;
1486 CommittableValue<int64_t> global_span_cost_;
1489 const std::vector<SoftBound> cumul_soft_upper_bounds_;
1491 const std::vector<SoftBound> cumul_soft_lower_bounds_;
1493 const std::vector<const PiecewiseLinearFunction*>
1494 cumul_piecewise_linear_costs_;
1496 const bool has_forbidden_intervals_;
1500 DimensionValues dimension_values_;
1503 CommittableArray<int64_t> cost_of_path_;
1504 int64_t synchronized_objective_value_;
1505 int64_t accepted_objective_value_;
1507 struct RankAndIndex {
1515 CommittableArray<RankAndIndex> pickup_rank_and_alternative_index_of_pair_;
1520 const std::vector<std::vector<RoutingDimension::NodePrecedence>>
1521 node_index_to_precedences_;
1522 struct PathAndRank {
1528 CommittableArray<PathAndRank> location_of_node_;
1531 const std::string name_;
1533 LocalDimensionCumulOptimizer* lp_optimizer_;
1534 LocalDimensionCumulOptimizer* mp_optimizer_;
1535 const std::function<int64_t(int64_t)> path_accessor_;
1536 const bool filter_objective_cost_;
1538 const bool may_use_optimizers_;
1539 const bool propagate_own_objective_value_;
1543template <
typename T>
1544std::vector<T> SumOfVectors(
const std::vector<T>& v1,
1545 const std::vector<T>& v2) {
1546 DCHECK_EQ(v1.size(), v2.size());
1547 std::vector<T> sum(v1.size());
1548 absl::c_transform(v1, v2, sum.begin(), [](T a, T b) { return CapAdd(a, b); });
1553std::vector<PathCumulFilter::Interval>
1554PathCumulFilter::ExtractInitialCumulIntervals() {
1555 std::vector<Interval> intervals;
1556 intervals.reserve(dimension_.cumuls().size());
1557 for (
const IntVar* cumul : dimension_.cumuls()) {
1558 intervals.push_back({cumul->Min(), cumul->Max()});
1563std::vector<PathCumulFilter::Interval>
1564PathCumulFilter::ExtractInitialSlackIntervals() {
1565 std::vector<Interval> intervals;
1566 intervals.reserve(dimension_.slacks().size());
1567 for (
const IntVar* slack : dimension_.slacks()) {
1568 intervals.push_back({slack->Min(), slack->Max()});
1573std::vector<PathCumulFilter::SoftBound>
1574PathCumulFilter::ExtractCumulSoftUpperBounds()
const {
1575 const int num_cumuls = dimension_.cumuls().size();
1576 std::vector<SoftBound> bounds(num_cumuls,
1577 {.bound =
kint64max, .coefficient = 0});
1578 bool has_some_bound =
false;
1579 for (
int i = 0;
i < num_cumuls; ++
i) {
1580 if (!dimension_.HasCumulVarSoftUpperBound(i))
continue;
1581 const int64_t
bound = dimension_.GetCumulVarSoftUpperBound(i);
1582 const int64_t coeff = dimension_.GetCumulVarSoftUpperBoundCoefficient(i);
1583 bounds[
i] = {.bound =
bound, .coefficient = coeff};
1586 if (!has_some_bound) bounds.clear();
1590std::vector<PathCumulFilter::SoftBound>
1591PathCumulFilter::ExtractCumulSoftLowerBounds()
const {
1592 const int num_cumuls = dimension_.cumuls().size();
1593 std::vector<SoftBound> bounds(num_cumuls, {.bound = 0, .coefficient = 0});
1594 bool has_some_bound =
false;
1595 for (
int i = 0;
i < num_cumuls; ++
i) {
1596 if (!dimension_.HasCumulVarSoftLowerBound(i))
continue;
1597 const int64_t
bound = dimension_.GetCumulVarSoftLowerBound(i);
1598 const int64_t coeff = dimension_.GetCumulVarSoftLowerBoundCoefficient(i);
1599 bounds[
i] = {.bound =
bound, .coefficient = coeff};
1600 has_some_bound |=
bound > 0 && coeff != 0;
1602 if (!has_some_bound) bounds.clear();
1606std::vector<const PiecewiseLinearFunction*>
1607PathCumulFilter::ExtractCumulPiecewiseLinearCosts()
const {
1608 const int num_cumuls = dimension_.cumuls().size();
1609 std::vector<const PiecewiseLinearFunction*> costs(num_cumuls,
nullptr);
1610 bool has_some_cost =
false;
1611 for (
int i = 0;
i < dimension_.cumuls().size(); ++
i) {
1612 if (!dimension_.HasCumulVarPiecewiseLinearCost(i))
continue;
1613 const PiecewiseLinearFunction*
const cost =
1614 dimension_.GetCumulVarPiecewiseLinearCost(i);
1615 if (cost ==
nullptr)
continue;
1616 has_some_cost =
true;
1619 if (!has_some_cost) costs.clear();
1623std::vector<const RoutingModel::TransitCallback2*>
1624PathCumulFilter::ExtractEvaluators()
const {
1625 const int num_paths = NumPaths();
1626 std::vector<const RoutingModel::TransitCallback2*> evaluators(num_paths);
1627 for (
int i = 0;
i < num_paths; ++
i) {
1628 evaluators[
i] = &dimension_.transit_evaluator(i);
1633std::vector<std::vector<RoutingDimension::NodePrecedence>>
1634PathCumulFilter::ExtractNodeIndexToPrecedences()
const {
1635 std::vector<std::vector<RoutingDimension::NodePrecedence>>
1636 node_index_to_precedences;
1637 const std::vector<RoutingDimension::NodePrecedence>& node_precedences =
1638 dimension_.GetNodePrecedences();
1639 if (!node_precedences.empty()) {
1640 node_index_to_precedences.resize(initial_cumul_.size());
1641 for (
const auto& node_precedence : node_precedences) {
1642 node_index_to_precedences[node_precedence.first_node].push_back(
1644 node_index_to_precedences[node_precedence.second_node].push_back(
1648 return node_index_to_precedences;
1651std::vector<std::vector<DimensionValues::VehicleBreak>>
1652PathCumulFilter::ExtractInitialVehicleBreaks()
const {
1653 const int num_vehicles = routing_model_.vehicles();
1654 std::vector<std::vector<VehicleBreak>> vehicle_breaks(num_vehicles);
1655 if (!dimension_.HasBreakConstraints())
return vehicle_breaks;
1656 for (
int v = 0; v < num_vehicles; ++v) {
1657 for (
const IntervalVar* br : dimension_.GetBreakIntervalsOfVehicle(v)) {
1658 vehicle_breaks[v].push_back(
1659 {.start = {.min = br->StartMin(), .max = br->StartMax()},
1660 .end = {.min = br->EndMin(), .max = br->EndMax()},
1661 .duration = {.min = br->DurationMin(), .max = br->DurationMax()},
1662 .is_performed = {.min = br->MustBePerformed(),
1663 .max = br->MayBePerformed()}});
1666 return vehicle_breaks;
1669PathCumulFilter::PathCumulFilter(
const RoutingModel& routing_model,
1670 const RoutingDimension& dimension,
1671 bool propagate_own_objective_value,
1672 bool filter_objective_cost,
1673 bool may_use_optimizers)
1674 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(),
1675 routing_model.GetPathsMetadata()),
1676 routing_model_(routing_model),
1677 dimension_(dimension),
1678 initial_cumul_(ExtractInitialCumulIntervals()),
1679 initial_slack_(ExtractInitialSlackIntervals()),
1680 initial_vehicle_breaks_(ExtractInitialVehicleBreaks()),
1681 evaluators_(ExtractEvaluators()),
1683 path_capacities_(dimension.vehicle_capacities()),
1684 path_span_upper_bounds_(dimension.vehicle_span_upper_bounds()),
1685 path_total_slack_cost_coefficients_(
1686 SumOfVectors(dimension.vehicle_span_cost_coefficients(),
1687 dimension.vehicle_slack_cost_coefficients())),
1688 global_span_cost_coefficient_(dimension.global_span_cost_coefficient()),
1689 global_span_cost_(0),
1690 cumul_soft_upper_bounds_(ExtractCumulSoftUpperBounds()),
1691 cumul_soft_lower_bounds_(ExtractCumulSoftLowerBounds()),
1692 cumul_piecewise_linear_costs_(ExtractCumulPiecewiseLinearCosts()),
1693 has_forbidden_intervals_(
1694 absl::c_any_of(dimension.forbidden_intervals(),
1695 [](const SortedDisjointIntervalList& intervals) {
1696 return intervals.NumIntervals() > 0;
1699 dimension_values_(routing_model.vehicles(), dimension.cumuls().size()),
1700 cost_of_path_(NumPaths(), 0),
1701 synchronized_objective_value_(0),
1702 accepted_objective_value_(0),
1703 pickup_rank_and_alternative_index_of_pair_(
1704 routing_model_.GetPickupAndDeliveryPairs().size(), {-1, -1}),
1705 node_index_to_precedences_(ExtractNodeIndexToPrecedences()),
1706 location_of_node_(dimension.cumuls().size(), {-1, -1}),
1707 name_(dimension.name()),
1708 lp_optimizer_(routing_model.GetMutableLocalCumulLPOptimizer(dimension)),
1709 mp_optimizer_(routing_model.GetMutableLocalCumulMPOptimizer(dimension)),
1710 path_accessor_([
this](int64_t node) {
return GetNext(node); }),
1711 filter_objective_cost_(filter_objective_cost),
1712 may_use_optimizers_(may_use_optimizers),
1713 propagate_own_objective_value_(propagate_own_objective_value) {
1715 for (
int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
1716 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
1717 DCHECK_NE(lp_optimizer_,
nullptr);
1718 DCHECK_NE(mp_optimizer_,
nullptr);
1724bool PathCumulFilter::PropagateTransits(
int path) {
1725 if (has_forbidden_intervals_) {
1726 return PropagateTransitsWithForbiddenIntervals(path);
1728 return PropagateTransitsWithoutForbiddenIntervals(path);
1732bool PathCumulFilter::PropagateTransitsWithForbiddenIntervals(
int path) {
1733 DCHECK_LT(0, dimension_values_.NumNodes(path));
1734 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
1735 absl::Span<const Interval> transits = dimension_values_.Transits(path);
1736 absl::Span<const int> nodes = dimension_values_.Nodes(path);
1737 const int num_nodes = dimension_values_.NumNodes(path);
1738 auto reduce_to_allowed_interval = [
this](Interval& interval,
1740 DCHECK(!interval.IsEmpty());
1741 interval.min = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
1742 node, interval.min);
1743 if (interval.IsEmpty())
return false;
1745 dimension_.GetLastPossibleLessOrEqualValueForNode(node, interval.max);
1746 return !interval.IsEmpty();
1749 if (!reduce_to_allowed_interval(cumuls[0], nodes[0]))
return false;
1750 Interval cumul = cumuls[0];
1751 for (
int r = 1; r < num_nodes; ++r) {
1752 cumul.Add(transits[r - 1]);
1753 if (!cumul.IntersectWith(cumuls[r]))
return false;
1754 if (!reduce_to_allowed_interval(cumul, nodes[r]))
return false;
1758 for (
int r = num_nodes - 2; r >= 0; --r) {
1759 cumul.Subtract(transits[r]);
1760 if (!cumul.IntersectWith(cumuls[r]))
return false;
1761 if (!reduce_to_allowed_interval(cumul, nodes[r]))
return false;
1767bool PathCumulFilter::PropagateTransitsWithoutForbiddenIntervals(
int path) {
1768 DCHECK_LT(0, dimension_values_.NumNodes(path));
1769 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
1770 absl::Span<const Interval> transits = dimension_values_.Transits(path);
1771 const int num_nodes = dimension_values_.NumNodes(path);
1773 Interval cumul = cumuls.front();
1774 for (
int r = 1; r < num_nodes; ++r) {
1775 cumul.Add(transits[r - 1]);
1776 if (!cumul.IntersectWith(cumuls[r]))
return false;
1780 for (
int r = num_nodes - 2; r >= 0; --r) {
1781 cumul.Subtract(transits[r]);
1782 if (!cumul.IntersectWith(cumuls[r]))
return false;
1788bool PathCumulFilter::PropagateSpan(
int path) {
1789 absl::Span<const int64_t> travel_sums = dimension_values_.TravelSums(path);
1790 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
1793 Interval start = cumuls.front();
1794 Interval
end = cumuls.back();
1795 Interval span = dimension_values_.Span(path);
1796 if (!span.IncreaseMin(travel_sums.back()))
return false;
1800 if (!
end.DecreaseMax(
CapAdd(start.max, span.max)))
return false;
1801 if (!start.IncreaseMin(
CapSub(
end.min, span.max)))
return false;
1802 if (!span.IncreaseMin(
CapSub(
end.min, start.max)))
return false;
1805 if (!span.DecreaseMax(
CapSub(
end.max, start.min)))
return false;
1806 if (!
end.IncreaseMin(
CapAdd(start.min, span.min)))
return false;
1807 if (!start.DecreaseMax(
CapSub(
end.max, span.min)))
return false;
1809 cumuls.front() = start;
1810 cumuls.back() =
end;
1811 dimension_values_.MutableSpan(path) = span;
1815bool PathCumulFilter::FillDimensionValues(
int path) {
1817 int node = Start(path);
1818 dimension_values_.PushNode(node);
1819 while (node < Size()) {
1820 const int next = GetNext(node);
1821 dimension_values_.PushNode(next);
1824 dimension_values_.MakePathFromNewNodes(path);
1826 path, path_capacities_[path], path_span_upper_bounds_[path],
1827 initial_cumul_, initial_slack_, *evaluators_[path],
1828 dimension_values_)) {
1831 dimension_values_.MutableVehicleBreaks(path) = initial_vehicle_breaks_[path];
1835bool PathCumulFilter::PropagatePickupToDeliveryLimits(
int path) {
1836 const int num_nodes = dimension_values_.NumNodes(path);
1837 absl::Span<const int> nodes = dimension_values_.Nodes(path);
1838 absl::Span<const int64_t> travel_sums = dimension_values_.TravelSums(path);
1839 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
1846 pickup_rank_and_alternative_index_of_pair_.Revert();
1847 for (
int rank = 1; rank < num_nodes - 1; ++rank) {
1848 const int node = nodes[rank];
1849 const std::optional<RoutingModel::PickupDeliveryPosition> pickup_pos =
1850 routing_model_.GetPickupPosition(node);
1852 if (pickup_pos.has_value()) {
1853 const auto [pair_index, pickup_alternative_index] = *pickup_pos;
1854 pickup_rank_and_alternative_index_of_pair_.Set(
1855 pair_index, {.rank = rank, .index = pickup_alternative_index});
1862 const std::optional<RoutingModel::PickupDeliveryPosition> delivery_pos =
1863 routing_model_.GetDeliveryPosition(node);
1864 if (!delivery_pos.has_value())
continue;
1865 const auto [pair_index, delivery_alt_index] = *delivery_pos;
1866 const auto [pickup_rank, pickup_alt_index] =
1867 pickup_rank_and_alternative_index_of_pair_.Get(pair_index);
1868 if (pickup_rank == -1)
continue;
1870 const int64_t limit = dimension_.GetPickupToDeliveryLimitForPair(
1871 pair_index, pickup_alt_index, delivery_alt_index);
1876 const int64_t total_travel =
1877 CapSub(travel_sums[rank], travel_sums[pickup_rank]);
1878 if (total_travel > limit)
return false;
1881 if (!cumuls[rank].DecreaseMax(
CapAdd(cumuls[pickup_rank].max, limit))) {
1884 if (!cumuls[pickup_rank].IncreaseMin(
CapSub(cumuls[rank].min, limit))) {
1891bool PathCumulFilter::PropagateVehicleBreaks(
int path) {
1893 path, dimension_values_,
1894 dimension_.GetBreakDistanceDurationOfVehicle(path));
1897bool PathCumulFilter::AcceptPath(int64_t path_start, int64_t ,
1899 const int path = GetPath(path_start);
1900 if (!FillDimensionValues(path))
return false;
1905 if (!PropagateSpan(path) || !PropagateTransits(path) ||
1906 !PropagateSpan(path)) {
1909 if (dimension_.HasPickupToDeliveryLimits()) {
1910 if (!PropagatePickupToDeliveryLimits(path))
return false;
1912 if (!PropagateSpan(path) || !PropagateTransits(path) ||
1913 !PropagateSpan(path)) {
1917 if (FilterVehicleBreaks(path) && !PropagateVehicleBreaks(path))
return false;
1921 if (!filter_objective_cost_)
return true;
1923 CapSubFrom(cost_of_path_.Get(path), &accepted_objective_value_);
1924 if (routing_model_.IsEnd(GetNext(path_start)) &&
1925 !routing_model_.IsVehicleUsedWhenEmpty(path)) {
1926 cost_of_path_.Set(path, 0);
1930 const absl::Span<const Interval> cumuls = dimension_values_.Cumuls(path);
1931 const absl::Span<const int> nodes = dimension_values_.Nodes(path);
1932 const Interval span = dimension_values_.Span(path);
1933 const int64_t total_travel = dimension_values_.TravelSums(path).back();
1934 const int64_t min_total_slack =
CapSub(span.min, total_travel);
1936 int64_t new_path_cost = 0;
1938 CapAddTo(
CapProd(path_total_slack_cost_coefficients_[path], min_total_slack),
1941 if (dimension_.HasSoftSpanUpperBounds()) {
1942 const auto [
bound, cost] = dimension_.GetSoftSpanUpperBoundForVehicle(path);
1946 if (dimension_.HasQuadraticCostSoftSpanUpperBounds()) {
1947 const auto [
bound, cost] =
1948 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(path);
1949 const int64_t violation = std::max<int64_t>(0,
CapSub(span.min, bound));
1953 const int num_path_nodes = dimension_values_.NumNodes(path);
1954 if (!cumul_soft_lower_bounds_.empty()) {
1955 for (
int r = 0; r < num_path_nodes; ++r) {
1956 const auto [
bound, coef] = cumul_soft_lower_bounds_[nodes[r]];
1958 CapProd(coef, std::max<int64_t>(0,
CapSub(bound, cumuls[r].max))),
1963 if (!cumul_soft_upper_bounds_.empty()) {
1964 for (
int r = 0; r < num_path_nodes; ++r) {
1965 const auto [
bound, coef] = cumul_soft_upper_bounds_[nodes[r]];
1967 CapProd(coef, std::max<int64_t>(0,
CapSub(cumuls[r].min, bound))),
1974 if (!cumul_piecewise_linear_costs_.empty()) {
1975 for (
int r = 0; r < num_path_nodes; ++r) {
1976 const PiecewiseLinearFunction* cost =
1977 cumul_piecewise_linear_costs_[nodes[r]];
1978 if (cost ==
nullptr)
continue;
1979 CapAddTo(cost->Value(cumuls[r].min), &new_path_cost);
1983 CapAddTo(new_path_cost, &accepted_objective_value_);
1984 cost_of_path_.Set(path, new_path_cost);
1988bool PathCumulFilter::FinalizeAcceptPath(int64_t ,
1989 int64_t objective_max) {
1990 if (lns_detected())
return true;
1991 if (FilterPrecedences()) {
1994 for (
const int path : dimension_values_.ChangedPaths()) {
1995 for (
const int node : dimension_values_.CommittedNodes(path)) {
1996 location_of_node_.Set(node, {.path = -1, .rank = -1});
1999 for (
const int path : dimension_values_.ChangedPaths()) {
2000 const absl::Span<const int> nodes = dimension_values_.Nodes(path);
2001 const int num_nodes = nodes.size();
2002 for (
int rank = 0; rank < num_nodes; ++rank) {
2003 location_of_node_.Set(nodes[rank], {.path = path, .rank = rank});
2007 for (
const int path : dimension_values_.ChangedPaths()) {
2008 const absl::Span<const int> nodes = dimension_values_.Nodes(path);
2009 const int num_nodes = nodes.size();
2010 for (
int rank = 0; rank < num_nodes; ++rank) {
2011 const int node = nodes[rank];
2012 for (
const auto [first_node, second_node, offset,
2013 performed_constraint] :
2014 node_index_to_precedences_[node]) {
2015 const auto [path1, rank1] = location_of_node_.Get(first_node);
2016 const auto [path2, rank2] = location_of_node_.Get(second_node);
2017 if (path1 == -1 && !IsVarSynced(first_node))
continue;
2018 if (path2 == -1 && !IsVarSynced(second_node))
continue;
2019 switch (RoutingDimension::GetPrecedenceStatus(
2020 path1 == -1, path2 == -1, performed_constraint)) {
2021 case RoutingDimension::PrecedenceStatus::kActive:
2023 case RoutingDimension::PrecedenceStatus::kInactive:
2025 case RoutingDimension::PrecedenceStatus::kInvalid:
2028 DCHECK(node == first_node || node == second_node);
2029 DCHECK_EQ(first_node, dimension_values_.Nodes(path1)[rank1]);
2030 DCHECK_EQ(second_node, dimension_values_.Nodes(path2)[rank2]);
2032 if (
CapAdd(dimension_values_.Cumuls(path1)[rank1].min, offset) >
2033 dimension_values_.Cumuls(path2)[rank2].max)
2039 if (filter_objective_cost_ && FilterGlobalSpanCost()) {
2042 int64_t global_span_min = 0;
2044 for (
const int path : dimension_values_.ChangedPaths()) {
2045 if (dimension_values_.NumNodes(path) == 0)
continue;
2046 if (dimension_values_.NumNodes(path) == 2 &&
2047 !routing_model_.IsVehicleUsedWhenEmpty(path)) {
2051 std::max(global_span_min, dimension_values_.Span(path).min);
2052 const int64_t end_min = dimension_values_.Cumuls(path).back().min;
2053 global_end_min = std::max(global_end_min, end_min);
2054 const int64_t start_max = dimension_values_.Cumuls(path).front().max;
2055 global_start_max = std::min(global_start_max, start_max);
2058 for (
const auto& [span_min, path] : paths_by_decreasing_span_min_) {
2059 if (dimension_values_.PathHasChanged(path))
continue;
2060 global_span_min = std::max(global_span_min, span_min);
2063 for (
const auto& [end_min, path] : paths_by_decreasing_end_min_) {
2064 if (dimension_values_.PathHasChanged(path))
continue;
2065 global_end_min = std::max(global_end_min, end_min);
2068 for (
const auto& [start_max, path] : paths_by_increasing_start_max_) {
2069 if (dimension_values_.PathHasChanged(path))
continue;
2070 global_start_max = std::min(global_start_max, start_max);
2075 std::max(global_span_min,
CapSub(global_end_min, global_start_max));
2076 const int64_t global_span_cost =
2077 CapProd(global_span_min, global_span_cost_coefficient_);
2078 CapSubFrom(global_span_cost_.Get(), &accepted_objective_value_);
2079 global_span_cost_.Set(global_span_cost);
2080 CapAddTo(global_span_cost, &accepted_objective_value_);
2084 if (may_use_optimizers_ && lp_optimizer_ !=
nullptr &&
2085 accepted_objective_value_ <= objective_max) {
2086 std::vector<int> paths_requiring_mp_optimizer;
2089 int solve_duration_shares = dimension_values_.ChangedPaths().size();
2090 for (
const int vehicle : dimension_values_.ChangedPaths()) {
2091 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
2094 int64_t path_cost_with_lp = 0;
2096 lp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
2097 vehicle, 1.0 / solve_duration_shares,
2098 path_accessor_,
nullptr,
2099 filter_objective_cost_ ? &path_cost_with_lp :
nullptr);
2100 solve_duration_shares--;
2101 if (status == DimensionSchedulingStatus::INFEASIBLE) {
2105 if (filter_objective_cost_ &&
2106 (status == DimensionSchedulingStatus::OPTIMAL ||
2107 status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) &&
2108 path_cost_with_lp > cost_of_path_.Get(vehicle)) {
2109 CapSubFrom(cost_of_path_.Get(vehicle), &accepted_objective_value_);
2110 CapAddTo(path_cost_with_lp, &accepted_objective_value_);
2111 if (accepted_objective_value_ > objective_max)
return false;
2112 cost_of_path_.Set(vehicle, path_cost_with_lp);
2115 DCHECK_NE(mp_optimizer_,
nullptr);
2116 if (FilterVehicleBreaks(vehicle) ||
2117 (filter_objective_cost_ &&
2118 (FilterSoftSpanQuadraticCost(vehicle) ||
2119 status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY))) {
2120 paths_requiring_mp_optimizer.push_back(vehicle);
2124 DCHECK_LE(accepted_objective_value_, objective_max);
2126 solve_duration_shares = paths_requiring_mp_optimizer.size();
2127 for (
const int vehicle : paths_requiring_mp_optimizer) {
2128 int64_t path_cost_with_mp = 0;
2130 mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
2131 vehicle, 1.0 / solve_duration_shares,
2132 path_accessor_,
nullptr,
2133 filter_objective_cost_ ? &path_cost_with_mp :
nullptr);
2134 solve_duration_shares--;
2135 if (status == DimensionSchedulingStatus::INFEASIBLE) {
2138 if (filter_objective_cost_ &&
2139 status == DimensionSchedulingStatus::OPTIMAL &&
2140 path_cost_with_mp > cost_of_path_.Get(vehicle)) {
2141 CapSubFrom(cost_of_path_.Get(vehicle), &accepted_objective_value_);
2142 CapAddTo(path_cost_with_mp, &accepted_objective_value_);
2143 if (accepted_objective_value_ > objective_max)
return false;
2144 cost_of_path_.Set(vehicle, path_cost_with_mp);
2148 return accepted_objective_value_ <= objective_max;
2151void PathCumulFilter::OnBeforeSynchronizePaths(
bool synchronizing_all_paths) {
2152 if (synchronizing_all_paths) {
2156 dimension_values_.Reset();
2157 cost_of_path_.SetAllAndCommit(0);
2158 location_of_node_.SetAllAndCommit({-1, -1});
2159 global_span_cost_.SetAndCommit(0);
2160 synchronized_objective_value_ = 0;
2163 accepted_objective_value_ = synchronized_objective_value_;
2164 if (HasAnySyncedPath()) {
2167 InitializeAcceptPath();
2171void PathCumulFilter::OnSynchronizePathFromStart(int64_t start) {
2172 DCHECK(IsVarSynced(start));
2175 const int path = GetPath(start);
2176 const bool is_accepted = AcceptPath(start, start, End(path));
2177 DCHECK(is_accepted);
2180void PathCumulFilter::OnAfterSynchronizePaths() {
2181 if (filter_objective_cost_ && FilterGlobalSpanCost()) {
2184 paths_by_decreasing_span_min_.clear();
2185 paths_by_decreasing_end_min_.clear();
2186 paths_by_increasing_start_max_.clear();
2187 for (
int path = 0; path < NumPaths(); ++path) {
2188 const int num_path_nodes = dimension_values_.Nodes(path).size();
2189 if (num_path_nodes == 0 ||
2190 (num_path_nodes == 2 &&
2191 !routing_model_.IsVehicleUsedWhenEmpty(path))) {
2194 paths_by_decreasing_span_min_.push_back(
2195 {.value = dimension_values_.Span(path).min, .path = path});
2196 paths_by_decreasing_end_min_.push_back(
2197 {.value = dimension_values_.Cumuls(path).back().min, .path = path});
2198 paths_by_increasing_start_max_.push_back(
2199 {.value = dimension_values_.Cumuls(path)[0].max, .path = path});
2201 absl::c_sort(paths_by_decreasing_span_min_, std::greater<ValuedPath>());
2202 absl::c_sort(paths_by_decreasing_end_min_, std::greater<ValuedPath>());
2203 absl::c_sort(paths_by_increasing_start_max_);
2207 dimension_values_.Commit();
2208 cost_of_path_.Commit();
2209 global_span_cost_.Commit();
2210 location_of_node_.Commit();
2211 synchronized_objective_value_ = accepted_objective_value_;
2217 bool propagate_own_objective_value,
2218 bool filter_objective_cost,
2219 bool may_use_optimizers) {
2220 RoutingModel& model = *dimension.model();
2221 return model.solver()->RevAlloc(
2222 new PathCumulFilter(model, dimension, propagate_own_objective_value,
2223 filter_objective_cost, may_use_optimizers));
2228bool DimensionHasCumulCost(
const RoutingDimension& dimension) {
2229 if (dimension.global_span_cost_coefficient() != 0)
return true;
2230 if (dimension.HasSoftSpanUpperBounds())
return true;
2231 if (dimension.HasQuadraticCostSoftSpanUpperBounds())
return true;
2232 if (absl::c_any_of(dimension.vehicle_span_cost_coefficients(),
2233 [](int64_t coefficient) { return coefficient != 0; })) {
2236 if (absl::c_any_of(dimension.vehicle_slack_cost_coefficients(),
2237 [](int64_t coefficient) { return coefficient != 0; })) {
2240 for (
int i = 0;
i < dimension.cumuls().size(); ++
i) {
2241 if (dimension.HasCumulVarSoftUpperBound(i))
return true;
2242 if (dimension.HasCumulVarSoftLowerBound(i))
return true;
2243 if (dimension.HasCumulVarPiecewiseLinearCost(i))
return true;
2248bool DimensionHasPathCumulConstraint(
const RoutingDimension& dimension) {
2249 if (dimension.HasBreakConstraints())
return true;
2250 if (dimension.HasPickupToDeliveryLimits())
return true;
2252 dimension.vehicle_span_upper_bounds(), [](int64_t upper_bound) {
2253 return upper_bound != std::numeric_limits<int64_t>::max();
2257 if (absl::c_any_of(dimension.slacks(),
2258 [](IntVar* slack) { return slack->Min() > 0; })) {
2261 const std::vector<IntVar*>& cumuls = dimension.cumuls();
2262 for (
int i = 0;
i < cumuls.size(); ++
i) {
2263 IntVar*
const cumul_var = cumuls[
i];
2264 if (cumul_var->Min() > 0 &&
2265 cumul_var->Max() < std::numeric_limits<int64_t>::max() &&
2266 !dimension.model()->IsEnd(i)) {
2269 if (dimension.forbidden_intervals()[i].NumIntervals() > 0)
return true;
2278 const std::vector<RoutingDimension*>& dimensions,
2279 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2283 for (
const RoutingDimension* dimension : dimensions) {
2285 const int num_vehicles = dimension->model()->vehicles();
2286 std::vector<Interval> path_capacity(num_vehicles);
2287 std::vector<int> path_class(num_vehicles);
2288 for (
int v = 0; v < num_vehicles; ++v) {
2289 const auto& vehicle_capacities = dimension->vehicle_capacities();
2290 path_capacity[v] = {0, vehicle_capacities[v]};
2291 path_class[v] = dimension->vehicle_to_class(v);
2298 const int num_vehicle_classes =
2299 1 + *std::max_element(path_class.begin(), path_class.end());
2300 const int num_cumuls = dimension->cumuls().size();
2301 const int num_slacks = dimension->slacks().size();
2302 std::vector<std::function<Interval(int64_t, int64_t)>> transits(
2303 num_vehicle_classes,
nullptr);
2304 for (
int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
2305 const int vehicle_class = path_class[vehicle];
2306 if (transits[vehicle_class] !=
nullptr)
continue;
2307 const auto& unary_evaluator =
2308 dimension->GetUnaryTransitEvaluator(vehicle);
2309 if (unary_evaluator !=
nullptr) {
2310 transits[vehicle_class] = [&unary_evaluator, dimension, num_slacks](
2311 int64_t node, int64_t) -> Interval {
2312 if (node >= num_slacks)
return {0, 0};
2313 const int64_t min_transit = unary_evaluator(node);
2314 const int64_t max_transit =
2315 CapAdd(min_transit, dimension->SlackVar(node)->Max());
2316 return {min_transit, max_transit};
2319 const auto& binary_evaluator =
2320 dimension->GetBinaryTransitEvaluator(vehicle);
2322 transits[vehicle_class] = [&binary_evaluator, dimension, num_slacks](
2323 int64_t node, int64_t next) -> Interval {
2324 if (node >= num_slacks)
return {0, 0};
2325 const int64_t min_transit = binary_evaluator(node, next);
2326 const int64_t max_transit =
2327 CapAdd(min_transit, dimension->SlackVar(node)->Max());
2328 return {min_transit, max_transit};
2333 std::vector<Interval> node_capacity(num_cumuls);
2334 for (
int node = 0; node < num_cumuls; ++node) {
2335 const IntVar* cumul = dimension->CumulVar(node);
2336 node_capacity[node] = {cumul->
Min(), cumul->
Max()};
2339 auto checker = std::make_unique<DimensionChecker>(
2340 path_state, std::move(path_capacity), std::move(path_class),
2341 std::move(transits), std::move(node_capacity));
2344 dimension->model()->solver(), std::move(checker), dimension->name());
2345 filters->push_back({filter, kAccept});
2350 const std::vector<RoutingDimension*>& dimensions,
2351 const RoutingSearchParameters& parameters,
bool filter_objective_cost,
2352 bool use_chain_cumul_filter,
2353 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2362 const int num_dimensions = dimensions.size();
2364 const bool has_dimension_optimizers =
2365 !parameters.disable_scheduling_beware_this_may_degrade_performance();
2366 std::vector<bool> use_path_cumul_filter(num_dimensions);
2367 std::vector<bool> use_cumul_bounds_propagator_filter(num_dimensions);
2368 std::vector<bool> use_global_lp_filter(num_dimensions);
2369 std::vector<bool> use_resource_assignment_filter(num_dimensions);
2370 for (
int d = 0; d < num_dimensions; d++) {
2371 const RoutingDimension& dimension = *dimensions[d];
2372 const bool has_cumul_cost = DimensionHasCumulCost(dimension);
2373 use_path_cumul_filter[d] =
2374 has_cumul_cost || DimensionHasPathCumulConstraint(dimension);
2376 const int num_dimension_resource_groups =
2377 dimension.model()->GetDimensionResourceGroupIndices(&dimension).size();
2378 const bool can_use_cumul_bounds_propagator_filter =
2379 !dimension.HasBreakConstraints() &&
2380 num_dimension_resource_groups == 0 &&
2381 (!filter_objective_cost || !has_cumul_cost);
2382 const bool has_precedences = !dimension.GetNodePrecedences().empty();
2383 use_global_lp_filter[d] =
2384 has_dimension_optimizers &&
2385 ((has_precedences && !can_use_cumul_bounds_propagator_filter) ||
2386 (filter_objective_cost &&
2387 dimension.global_span_cost_coefficient() > 0) ||
2388 num_dimension_resource_groups > 1);
2390 use_cumul_bounds_propagator_filter[d] =
2391 has_precedences && !use_global_lp_filter[d];
2393 use_resource_assignment_filter[d] =
2394 has_dimension_optimizers && num_dimension_resource_groups > 0;
2397 for (
int d = 0; d < num_dimensions; d++) {
2398 const RoutingDimension& dimension = *dimensions[d];
2399 const RoutingModel& model = *dimension.model();
2404 const bool use_global_lp = use_global_lp_filter[d];
2405 const bool filter_resource_assignment = use_resource_assignment_filter[d];
2406 if (use_path_cumul_filter[d]) {
2407 PathCumulFilter* filter = model.solver()->RevAlloc(
new PathCumulFilter(
2408 model, dimension, !use_global_lp &&
2409 !filter_resource_assignment,
2410 filter_objective_cost, has_dimension_optimizers));
2411 const int priority = filter->UsesDimensionOptimizers() ? 1 : 0;
2412 filters->push_back({filter, kAccept, priority});
2413 }
else if (use_chain_cumul_filter) {
2415 {model.solver()->RevAlloc(
new ChainCumulFilter(model, dimension)),
2419 if (use_cumul_bounds_propagator_filter[d]) {
2420 DCHECK(!use_global_lp);
2421 DCHECK(!filter_resource_assignment);
2426 if (filter_resource_assignment) {
2428 model.GetMutableLocalCumulLPOptimizer(dimension),
2429 model.GetMutableLocalCumulMPOptimizer(dimension),
2431 filter_objective_cost),
2435 if (use_global_lp) {
2437 model.GetMutableGlobalCumulLPOptimizer(dimension),
2438 model.GetMutableGlobalCumulMPOptimizer(dimension),
2439 filter_objective_cost),
2448class PickupDeliveryFilter :
public LocalSearchFilter {
2450 PickupDeliveryFilter(
const PathState* path_state,
2451 absl::Span<const PickupDeliveryPair> pairs,
2452 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2454 ~PickupDeliveryFilter()
override =
default;
2455 bool Accept(
const Assignment* ,
const Assignment* ,
2456 int64_t , int64_t )
override;
2458 void Reset()
override;
2459 void Synchronize(
const Assignment* ,
2460 const Assignment* )
override;
2461 std::string DebugString()
const override {
return "PickupDeliveryFilter"; }
2464 template <
bool check_as
signed_pairs>
2465 bool AcceptPathDispatch();
2466 template <
bool check_as
signed_pairs>
2467 bool AcceptPathDefault(
int path);
2468 template <
bool lifo,
bool check_as
signed_pairs>
2469 bool AcceptPathOrdered(
int path);
2470 void AssignAllVisitedPairsAndLoopNodes();
2472 const PathState*
const path_state_;
2478 int pair_index : 30;
2479 PairInfo() : is_paired(
false), pair_index(-1) {}
2480 PairInfo(
bool is_paired,
bool is_pickup,
int pair_index)
2481 : is_paired(is_paired), is_pickup(is_pickup), pair_index(pair_index) {}
2483 std::vector<PairInfo> pair_info_of_node_;
2489 PairStatus() : pickup(
false), delivery(
false) {}
2491 CommittableArray<PairStatus> assigned_status_of_pair_;
2492 SparseBitset<int> pair_is_open_;
2493 CommittableValue<int> num_assigned_pairs_;
2494 std::deque<int> visited_deque_;
2495 const std::vector<RoutingModel::PickupAndDeliveryPolicy> vehicle_policies_;
2498PickupDeliveryFilter::PickupDeliveryFilter(
2499 const PathState* path_state, absl::Span<const PickupDeliveryPair> pairs,
2500 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
2501 : path_state_(path_state),
2502 pair_info_of_node_(path_state->NumNodes()),
2503 assigned_status_of_pair_(pairs.size(), {}),
2504 pair_is_open_(pairs.size()),
2505 num_assigned_pairs_(0),
2506 vehicle_policies_(vehicle_policies) {
2507 for (
int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
2508 const auto& [pickups, deliveries] = pairs[pair_index];
2509 for (
const int pickup : pickups) {
2510 pair_info_of_node_[pickup] =
2513 PairInfo{
true,
true,
2516 for (
const int delivery : deliveries) {
2517 pair_info_of_node_[delivery] =
2520 PairInfo{
true,
false,
2526void PickupDeliveryFilter::Reset() {
2527 assigned_status_of_pair_.SetAllAndCommit({});
2528 num_assigned_pairs_.SetAndCommit(0);
2531void PickupDeliveryFilter::AssignAllVisitedPairsAndLoopNodes() {
2532 assigned_status_of_pair_.Revert();
2533 num_assigned_pairs_.Revert();
2534 int num_assigned_pairs = num_assigned_pairs_.Get();
2535 if (num_assigned_pairs == assigned_status_of_pair_.Size())
return;
2538 auto assign_node = [
this](
int node) ->
bool {
2539 const auto [is_paired, is_pickup, pair_index] = pair_info_of_node_[node];
2540 if (!is_paired)
return false;
2541 bool assigned_pair =
false;
2542 PairStatus assigned_status = assigned_status_of_pair_.Get(pair_index);
2543 if (is_pickup && !assigned_status.pickup) {
2544 assigned_pair = assigned_status.delivery;
2545 assigned_status.pickup =
true;
2546 assigned_status_of_pair_.Set(pair_index, assigned_status);
2548 if (!is_pickup && !assigned_status.delivery) {
2549 assigned_pair = assigned_status.pickup;
2550 assigned_status.delivery =
true;
2551 assigned_status_of_pair_.Set(pair_index, assigned_status);
2553 return assigned_pair;
2555 for (
const int path : path_state_->ChangedPaths()) {
2556 for (
const int node : path_state_->Nodes(path)) {
2557 num_assigned_pairs += assign_node(node) ? 1 : 0;
2560 for (
const int loop : path_state_->ChangedLoops()) {
2561 num_assigned_pairs += assign_node(loop) ? 1 : 0;
2563 num_assigned_pairs_.Set(num_assigned_pairs);
2566void PickupDeliveryFilter::Synchronize(
const Assignment* ,
2567 const Assignment* ) {
2568 AssignAllVisitedPairsAndLoopNodes();
2569 assigned_status_of_pair_.Commit();
2570 num_assigned_pairs_.Commit();
2573bool PickupDeliveryFilter::Accept(
const Assignment* ,
2577 if (path_state_->IsInvalid())
return true;
2578 AssignAllVisitedPairsAndLoopNodes();
2579 const bool check_assigned_pairs =
2580 num_assigned_pairs_.Get() < assigned_status_of_pair_.Size();
2581 if (check_assigned_pairs) {
2582 return AcceptPathDispatch<true>();
2584 return AcceptPathDispatch<false>();
2588template <
bool check_as
signed_pairs>
2589bool PickupDeliveryFilter::AcceptPathDispatch() {
2590 for (
const int path : path_state_->ChangedPaths()) {
2591 switch (vehicle_policies_[path]) {
2592 case RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER:
2593 if (!AcceptPathDefault<check_assigned_pairs>(path))
return false;
2595 case RoutingModel::PICKUP_AND_DELIVERY_LIFO:
2596 if (!AcceptPathOrdered<true, check_assigned_pairs>(path))
return false;
2598 case RoutingModel::PICKUP_AND_DELIVERY_FIFO:
2599 if (!AcceptPathOrdered<false, check_assigned_pairs>(path))
return false;
2608template <
bool check_as
signed_pairs>
2609bool PickupDeliveryFilter::AcceptPathDefault(
int path) {
2610 pair_is_open_.ResetAllToFalse();
2611 int num_opened_pairs = 0;
2612 for (
const int node : path_state_->Nodes(path)) {
2613 const auto [is_paired, is_pickup, pair_index] = pair_info_of_node_[node];
2614 if (!is_paired)
continue;
2615 if constexpr (check_assigned_pairs) {
2616 const PairStatus status = assigned_status_of_pair_.Get(pair_index);
2617 if (!status.pickup || !status.delivery)
continue;
2620 pair_is_open_.Set(pair_index);
2623 if (!pair_is_open_[pair_index])
return false;
2624 pair_is_open_.Clear(pair_index);
2630 if (num_opened_pairs > 0)
return false;
2631 pair_is_open_.NotifyAllClear();
2635template <
bool lifo,
bool check_as
signed_pairs>
2636bool PickupDeliveryFilter::AcceptPathOrdered(
int path) {
2637 visited_deque_.clear();
2638 for (
const int node : path_state_->Nodes(path)) {
2639 const auto [is_paired, is_pickup, pair_index] = pair_info_of_node_[node];
2640 if (!is_paired)
continue;
2641 if constexpr (check_assigned_pairs) {
2642 const PairStatus status = assigned_status_of_pair_.Get(pair_index);
2643 if (!status.pickup || !status.delivery)
continue;
2646 visited_deque_.emplace_back(pair_index);
2648 if (visited_deque_.empty())
return false;
2649 if constexpr (lifo) {
2650 const int last_pair_index = visited_deque_.back();
2651 if (last_pair_index != pair_index)
return false;
2652 visited_deque_.pop_back();
2654 const int first_pair_index = visited_deque_.front();
2655 if (first_pair_index != pair_index)
return false;
2656 visited_deque_.pop_front();
2660 return visited_deque_.empty();
2666 const RoutingModel& routing_model,
const PathState* path_state,
2667 const std::vector<PickupDeliveryPair>& pairs,
2668 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2670 return routing_model.solver()->RevAlloc(
2671 new PickupDeliveryFilter(path_state, pairs, vehicle_policies));
2677class VehicleVarFilter :
public LocalSearchFilter {
2679 VehicleVarFilter(
const RoutingModel& routing_model,
2680 const PathState* path_state);
2681 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
2682 int64_t objective_min, int64_t objective_max)
override;
2683 void Synchronize(
const Assignment* ,
2684 const Assignment* )
override;
2685 std::string DebugString()
const override {
return "VehicleVariableFilter"; }
2688 bool HasConstrainedVehicleVars()
const;
2690 const PathState* path_state_;
2691 std::vector<IntVar*> vehicle_vars_;
2692 const int num_vehicles_;
2696VehicleVarFilter::VehicleVarFilter(
const RoutingModel& routing_model,
2697 const PathState* path_state)
2698 : path_state_(path_state),
2699 vehicle_vars_(routing_model.VehicleVars()),
2700 num_vehicles_(routing_model.vehicles()),
2701 is_disabled_(!HasConstrainedVehicleVars()) {}
2703bool VehicleVarFilter::HasConstrainedVehicleVars()
const {
2704 for (
const IntVar* var : vehicle_vars_) {
2705 const int unconstrained_size = num_vehicles_ + ((var->Min() >= 0) ? 0 : 1);
2706 if (var->Size() != unconstrained_size)
return true;
2711void VehicleVarFilter::Synchronize(
const Assignment* ,
2712 const Assignment* ) {
2713 is_disabled_ = !HasConstrainedVehicleVars();
2716bool VehicleVarFilter::Accept(
const Assignment* ,
2720 if (is_disabled_)
return true;
2721 for (
const int path : path_state_->ChangedPaths()) {
2723 for (
const PathState::Chain chain :
2724 path_state_->Chains(path).DropFirstChain().DropLastChain()) {
2725 for (
const int node : chain) {
2726 if (!vehicle_vars_[node]->Contains(path))
return false;
2737 return routing_model.solver()->RevAlloc(
2738 new VehicleVarFilter(routing_model, path_state));
2743class CumulBoundsPropagatorFilter :
public IntVarLocalSearchFilter {
2745 explicit CumulBoundsPropagatorFilter(
const RoutingDimension& dimension);
2746 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
2747 int64_t objective_min, int64_t objective_max)
override;
2748 std::string DebugString()
const override {
2749 return "CumulBoundsPropagatorFilter(" + propagator_.dimension().name() +
2754 CumulBoundsPropagator propagator_;
2755 const int64_t cumul_offset_;
2756 SparseBitset<int64_t> delta_touched_;
2757 std::vector<int64_t> delta_nexts_;
2760CumulBoundsPropagatorFilter::CumulBoundsPropagatorFilter(
2761 const RoutingDimension& dimension)
2762 : IntVarLocalSearchFilter(dimension.model()->Nexts()),
2763 propagator_(&dimension),
2764 cumul_offset_(dimension.GetGlobalOptimizerOffset()),
2765 delta_touched_(Size()),
2766 delta_nexts_(Size()) {}
2768bool CumulBoundsPropagatorFilter::Accept(
const Assignment* delta,
2772 delta_touched_.ResetAllToFalse();
2773 for (
const IntVarElement& delta_element :
2774 delta->IntVarContainer().elements()) {
2776 if (FindIndex(delta_element.Var(), &index)) {
2777 if (!delta_element.Bound()) {
2781 delta_touched_.Set(index);
2782 delta_nexts_[index] = delta_element.Value();
2785 const auto& next_accessor = [
this](int64_t index) -> int64_t {
2786 return delta_touched_[index] ? delta_nexts_[index]
2787 : !IsVarSynced(index) ? -1
2791 return propagator_.PropagateCumulBounds(next_accessor, cumul_offset_);
2797 const RoutingDimension& dimension) {
2798 return dimension.model()->solver()->RevAlloc(
2799 new CumulBoundsPropagatorFilter(dimension));
2804class LPCumulFilter :
public IntVarLocalSearchFilter {
2806 LPCumulFilter(
const std::vector<IntVar*>& nexts,
2807 GlobalDimensionCumulOptimizer* optimizer,
2808 GlobalDimensionCumulOptimizer* mp_optimizer,
2809 bool filter_objective_cost);
2810 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
2811 int64_t objective_min, int64_t objective_max)
override;
2812 int64_t GetAcceptedObjectiveValue()
const override;
2813 void OnSynchronize(
const Assignment* delta)
override;
2814 int64_t GetSynchronizedObjectiveValue()
const override;
2815 std::string DebugString()
const override {
2816 return "LPCumulFilter(" + lp_optimizer_.dimension()->name() +
")";
2820 GlobalDimensionCumulOptimizer& lp_optimizer_;
2821 GlobalDimensionCumulOptimizer& mp_optimizer_;
2822 const bool filter_objective_cost_;
2823 int64_t synchronized_cost_without_transit_;
2824 int64_t delta_cost_without_transit_;
2825 SparseBitset<int64_t> delta_touched_;
2826 std::vector<int64_t> delta_nexts_;
2829LPCumulFilter::LPCumulFilter(
const std::vector<IntVar*>& nexts,
2830 GlobalDimensionCumulOptimizer* lp_optimizer,
2831 GlobalDimensionCumulOptimizer* mp_optimizer,
2832 bool filter_objective_cost)
2833 : IntVarLocalSearchFilter(nexts),
2834 lp_optimizer_(*lp_optimizer),
2835 mp_optimizer_(*mp_optimizer),
2836 filter_objective_cost_(filter_objective_cost),
2837 synchronized_cost_without_transit_(-1),
2838 delta_cost_without_transit_(-1),
2839 delta_touched_(Size()),
2840 delta_nexts_(Size()) {}
2842bool LPCumulFilter::Accept(
const Assignment* delta,
2844 int64_t , int64_t objective_max) {
2845 delta_touched_.ResetAllToFalse();
2846 for (
const IntVarElement& delta_element :
2847 delta->IntVarContainer().elements()) {
2849 if (FindIndex(delta_element.Var(), &index)) {
2850 if (!delta_element.Bound()) {
2854 delta_touched_.Set(index);
2855 delta_nexts_[index] = delta_element.Value();
2858 const auto& next_accessor = [
this](int64_t index) {
2859 return delta_touched_[index] ? delta_nexts_[index]
2860 : !IsVarSynced(index) ? -1
2864 if (!filter_objective_cost_) {
2866 delta_cost_without_transit_ = 0;
2868 next_accessor, {},
nullptr,
nullptr,
nullptr);
2869 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
2870 status = mp_optimizer_.ComputeCumuls(next_accessor, {},
nullptr,
nullptr,
2873 DCHECK(status != DimensionSchedulingStatus::FEASIBLE)
2874 <<
"FEASIBLE without filtering objective cost should be OPTIMAL";
2875 return status == DimensionSchedulingStatus::OPTIMAL;
2879 lp_optimizer_.ComputeCumulCostWithoutFixedTransits(
2880 next_accessor, &delta_cost_without_transit_);
2882 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY ||
2883 status == DimensionSchedulingStatus::FEASIBLE) {
2886 status = mp_optimizer_.ComputeCumulCostWithoutFixedTransits(next_accessor,
2888 if (lp_status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY &&
2889 status == DimensionSchedulingStatus::OPTIMAL) {
2893 delta_cost_without_transit_ = mp_cost;
2894 }
else if (lp_status == DimensionSchedulingStatus::FEASIBLE &&
2895 status != DimensionSchedulingStatus::INFEASIBLE) {
2898 delta_cost_without_transit_ =
2899 std::min(delta_cost_without_transit_, mp_cost);
2903 if (status == DimensionSchedulingStatus::INFEASIBLE) {
2904 delta_cost_without_transit_ = std::numeric_limits<int64_t>::max();
2907 return delta_cost_without_transit_ <= objective_max;
2910int64_t LPCumulFilter::GetAcceptedObjectiveValue()
const {
2911 return delta_cost_without_transit_;
2914void LPCumulFilter::OnSynchronize(
const Assignment* ) {
2917 const RoutingModel& model = *lp_optimizer_.dimension()->model();
2918 const auto& next_accessor = [
this, &model](int64_t index) {
2919 return IsVarSynced(index) ?
Value(index)
2920 : model.IsStart(index) ? model.End(model.VehicleIndex(index))
2924 if (!filter_objective_cost_) {
2925 synchronized_cost_without_transit_ = 0;
2928 filter_objective_cost_
2929 ? lp_optimizer_.ComputeCumulCostWithoutFixedTransits(
2930 next_accessor, &synchronized_cost_without_transit_)
2931 : lp_optimizer_.ComputeCumuls(next_accessor, {},
nullptr,
nullptr,
2933 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
2934 status = filter_objective_cost_
2935 ? mp_optimizer_.ComputeCumulCostWithoutFixedTransits(
2936 next_accessor, &synchronized_cost_without_transit_)
2937 : mp_optimizer_.ComputeCumuls(next_accessor, {},
nullptr,
2940 if (status == DimensionSchedulingStatus::INFEASIBLE) {
2943 synchronized_cost_without_transit_ = 0;
2947int64_t LPCumulFilter::GetSynchronizedObjectiveValue()
const {
2948 return synchronized_cost_without_transit_;
2956 DCHECK_NE(lp_optimizer,
nullptr);
2957 DCHECK_NE(mp_optimizer,
nullptr);
2958 const RoutingModel& model = *lp_optimizer->
dimension()->model();
2959 return model.solver()->RevAlloc(
new LPCumulFilter(
2960 model.Nexts(), lp_optimizer, mp_optimizer, filter_objective_cost));
2965using ResourceGroup = RoutingModel::ResourceGroup;
2967class ResourceGroupAssignmentFilter :
public BasePathFilter {
2969 ResourceGroupAssignmentFilter(
const std::vector<IntVar*>& nexts,
2970 const ResourceGroup* resource_group,
2971 LocalDimensionCumulOptimizer* lp_optimizer,
2972 LocalDimensionCumulOptimizer* mp_optimizer,
2973 bool filter_objective_cost);
2974 bool InitializeAcceptPath()
override;
2975 bool AcceptPath(int64_t path_start, int64_t, int64_t)
override;
2976 bool FinalizeAcceptPath(int64_t objective_min,
2977 int64_t objective_max)
override;
2978 void OnBeforeSynchronizePaths(
bool synchronizing_all_paths)
override;
2979 void OnSynchronizePathFromStart(int64_t start)
override;
2980 void OnAfterSynchronizePaths()
override;
2982 int64_t GetAcceptedObjectiveValue()
const override {
2983 return lns_detected() ? 0 : delta_cost_without_transit_;
2985 int64_t GetSynchronizedObjectiveValue()
const override {
2986 return synchronized_cost_without_transit_;
2988 std::string DebugString()
const override {
2989 return "ResourceGroupAssignmentFilter(" + dimension_.name() +
")";
2993 using RCIndex = RoutingModel::ResourceClassIndex;
2995 bool VehicleRequiresResourceAssignment(
2996 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
2997 bool* is_infeasible);
2998 int64_t ComputeRouteCumulCostWithoutResourceAssignment(
2999 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor)
const;
3001 const RoutingModel& model_;
3002 const RoutingDimension& dimension_;
3003 const ResourceGroup& resource_group_;
3004 LocalDimensionCumulOptimizer* lp_optimizer_;
3005 LocalDimensionCumulOptimizer* mp_optimizer_;
3006 const bool filter_objective_cost_;
3007 bool current_synch_failed_;
3008 int64_t synchronized_cost_without_transit_;
3009 int64_t delta_cost_without_transit_;
3010 std::vector<std::vector<int64_t>> vehicle_to_resource_class_assignment_costs_;
3011 std::vector<int> vehicles_requiring_resource_assignment_;
3012 std::vector<bool> vehicle_requires_resource_assignment_;
3013 std::vector<std::vector<int64_t>>
3014 delta_vehicle_to_resource_class_assignment_costs_;
3015 std::vector<int> delta_vehicles_requiring_resource_assignment_;
3016 std::vector<bool> delta_vehicle_requires_resource_assignment_;
3018 std::vector<int> bound_resource_index_of_vehicle_;
3019 util_intops::StrongVector<RCIndex, absl::flat_hash_set<int>>
3020 ignored_resources_per_class_;
3023ResourceGroupAssignmentFilter::ResourceGroupAssignmentFilter(
3024 const std::vector<IntVar*>& nexts,
const ResourceGroup* resource_group,
3025 LocalDimensionCumulOptimizer* lp_optimizer,
3026 LocalDimensionCumulOptimizer* mp_optimizer,
bool filter_objective_cost)
3027 : BasePathFilter(nexts, lp_optimizer->dimension()->cumuls().size(),
3028 lp_optimizer->dimension()->model()->GetPathsMetadata()),
3029 model_(*lp_optimizer->dimension()->model()),
3030 dimension_(*lp_optimizer->dimension()),
3031 resource_group_(*resource_group),
3032 lp_optimizer_(lp_optimizer),
3033 mp_optimizer_(mp_optimizer),
3034 filter_objective_cost_(filter_objective_cost),
3035 current_synch_failed_(
false),
3036 synchronized_cost_without_transit_(-1),
3037 delta_cost_without_transit_(-1) {
3038 vehicle_to_resource_class_assignment_costs_.resize(model_.vehicles());
3039 delta_vehicle_to_resource_class_assignment_costs_.resize(model_.vehicles());
3042bool ResourceGroupAssignmentFilter::InitializeAcceptPath() {
3043 delta_vehicle_to_resource_class_assignment_costs_.assign(model_.vehicles(),
3045 if (current_synch_failed_) {
3050 int num_used_vehicles = 0;
3051 const int num_resources = resource_group_.Size();
3052 for (
int v : resource_group_.GetVehiclesRequiringAResource()) {
3053 if (GetNext(model_.Start(v)) != model_.End(v) ||
3054 model_.IsVehicleUsedWhenEmpty(v)) {
3055 if (++num_used_vehicles > num_resources) {
3060 delta_vehicle_requires_resource_assignment_ =
3061 vehicle_requires_resource_assignment_;
3065bool ResourceGroupAssignmentFilter::AcceptPath(int64_t path_start, int64_t,
3067 if (current_synch_failed_) {
3070 const int vehicle = model_.VehicleIndex(path_start);
3071 bool is_infeasible =
false;
3072 delta_vehicle_requires_resource_assignment_[vehicle] =
3073 VehicleRequiresResourceAssignment(
3074 vehicle, [
this](int64_t n) {
return GetNext(n); }, &is_infeasible);
3075 return !is_infeasible;
3078bool ResourceGroupAssignmentFilter::FinalizeAcceptPath(
3079 int64_t , int64_t objective_max) {
3080 delta_cost_without_transit_ = 0;
3081 if (current_synch_failed_) {
3084 const auto& next_accessor = [
this](int64_t index) {
return GetNext(index); };
3085 delta_vehicles_requiring_resource_assignment_.clear();
3088 for (
int v = 0; v < model_.vehicles(); ++v) {
3089 if (delta_vehicle_requires_resource_assignment_[v]) {
3090 delta_vehicles_requiring_resource_assignment_.push_back(v);
3093 int64_t route_cost = 0;
3094 int64_t start = model_.Start(v);
3095 if (PathStartTouched(start)) {
3097 ComputeRouteCumulCostWithoutResourceAssignment(v, next_accessor);
3098 if (route_cost < 0) {
3101 }
else if (IsVarSynced(start)) {
3102 DCHECK_EQ(vehicle_to_resource_class_assignment_costs_[v].size(), 1);
3103 route_cost = vehicle_to_resource_class_assignment_costs_[v][0];
3105 CapAddTo(route_cost, &delta_cost_without_transit_);
3106 if (delta_cost_without_transit_ > objective_max) {
3112 for (int64_t start : GetTouchedPathStarts()) {
3113 const int vehicle = model_.VehicleIndex(start);
3114 if (!delta_vehicle_requires_resource_assignment_[vehicle]) {
3119 vehicle, 1.0, resource_group_,
3120 ignored_resources_per_class_, next_accessor,
3121 dimension_.transit_evaluator(vehicle), filter_objective_cost_,
3122 lp_optimizer_, mp_optimizer_,
3123 &delta_vehicle_to_resource_class_assignment_costs_[vehicle],
3124 nullptr,
nullptr)) {
3129 delta_vehicles_requiring_resource_assignment_,
3130 resource_group_.GetResourceIndicesPerClass(),
3131 ignored_resources_per_class_,
3134 return PathStartTouched(model_.Start(v))
3135 ? &delta_vehicle_to_resource_class_assignment_costs_[v]
3136 : &vehicle_to_resource_class_assignment_costs_[v];
3139 CapAddTo(assignment_cost, &delta_cost_without_transit_);
3140 return assignment_cost >= 0 && delta_cost_without_transit_ <= objective_max;
3143void ResourceGroupAssignmentFilter::OnBeforeSynchronizePaths(
bool) {
3144 if (!HasAnySyncedPath()) {
3145 vehicle_to_resource_class_assignment_costs_.assign(model_.vehicles(), {});
3147 bound_resource_index_of_vehicle_.assign(model_.vehicles(), -1);
3148 vehicles_requiring_resource_assignment_.clear();
3149 vehicles_requiring_resource_assignment_.reserve(
3150 resource_group_.GetVehiclesRequiringAResource().size());
3151 vehicle_requires_resource_assignment_.assign(model_.vehicles(),
false);
3152 ignored_resources_per_class_.assign(resource_group_.GetResourceClassesCount(),
3153 absl::flat_hash_set<int>());
3155 for (
int v : resource_group_.GetVehiclesRequiringAResource()) {
3156 const int64_t start = model_.Start(v);
3157 if (!IsVarSynced(start)) {
3160 vehicle_requires_resource_assignment_[v] =
3161 VehicleRequiresResourceAssignment(
3162 v, [
this](int64_t n) {
return Value(n); }, ¤t_synch_failed_);
3163 if (vehicle_requires_resource_assignment_[v]) {
3164 vehicles_requiring_resource_assignment_.push_back(v);
3166 if (current_synch_failed_) {
3170 synchronized_cost_without_transit_ = 0;
3173void ResourceGroupAssignmentFilter::OnSynchronizePathFromStart(int64_t start) {
3174 if (current_synch_failed_)
return;
3175 DCHECK(IsVarSynced(start));
3176 const int v = model_.VehicleIndex(start);
3177 const auto& next_accessor = [
this](int64_t index) {
3178 return IsVarSynced(index) ?
Value(index) : -1;
3180 if (!vehicle_requires_resource_assignment_[v]) {
3181 const int64_t route_cost =
3182 ComputeRouteCumulCostWithoutResourceAssignment(v, next_accessor);
3183 if (route_cost < 0) {
3184 current_synch_failed_ =
true;
3187 CapAddTo(route_cost, &synchronized_cost_without_transit_);
3188 vehicle_to_resource_class_assignment_costs_[v] = {route_cost};
3198 v, 1.0, resource_group_,
3199 ignored_resources_per_class_, next_accessor,
3200 dimension_.transit_evaluator(v), filter_objective_cost_,
3201 lp_optimizer_, mp_optimizer_,
3202 &vehicle_to_resource_class_assignment_costs_[v],
nullptr,
nullptr)) {
3203 vehicle_to_resource_class_assignment_costs_[v].assign(
3204 resource_group_.GetResourceClassesCount(), -1);
3205 current_synch_failed_ =
true;
3209void ResourceGroupAssignmentFilter::OnAfterSynchronizePaths() {
3210 if (current_synch_failed_) {
3211 synchronized_cost_without_transit_ = 0;
3214 if (!filter_objective_cost_) {
3215 DCHECK_EQ(synchronized_cost_without_transit_, 0);
3219 vehicles_requiring_resource_assignment_,
3220 resource_group_.GetResourceIndicesPerClass(),
3221 ignored_resources_per_class_,
3222 [
this](
int v) { return &vehicle_to_resource_class_assignment_costs_[v]; },
3224 if (assignment_cost < 0) {
3225 synchronized_cost_without_transit_ = 0;
3226 current_synch_failed_ =
true;
3228 DCHECK_GE(synchronized_cost_without_transit_, 0);
3229 CapAddTo(assignment_cost, &synchronized_cost_without_transit_);
3233bool ResourceGroupAssignmentFilter::VehicleRequiresResourceAssignment(
3234 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor,
3235 bool* is_infeasible) {
3236 *is_infeasible =
false;
3237 if (!resource_group_.VehicleRequiresAResource(vehicle))
return false;
3238 const IntVar* res_var = model_.ResourceVar(vehicle, resource_group_.Index());
3239 if (!model_.IsVehicleUsedWhenEmpty(vehicle) &&
3240 next_accessor(model_.Start(vehicle)) == model_.End(vehicle)) {
3241 if (res_var->Bound() && res_var->Value() >= 0) {
3243 *is_infeasible =
true;
3248 if (res_var->Bound()) {
3250 const int res = res_var->Value();
3253 *is_infeasible =
true;
3255 bound_resource_index_of_vehicle_[vehicle] = res;
3256 const RCIndex rc = resource_group_.GetResourceClassIndex(res);
3257 ignored_resources_per_class_[rc].insert(res);
3266ResourceGroupAssignmentFilter::ComputeRouteCumulCostWithoutResourceAssignment(
3267 int vehicle,
const std::function<int64_t(int64_t)>& next_accessor)
const {
3268 if (next_accessor(model_.Start(vehicle)) == model_.End(vehicle) &&
3269 !model_.IsVehicleUsedWhenEmpty(vehicle)) {
3272 using Resource = RoutingModel::ResourceGroup::Resource;
3273 const Resource* resource =
nullptr;
3274 if (resource_group_.VehicleRequiresAResource(vehicle)) {
3275 DCHECK_GE(bound_resource_index_of_vehicle_[vehicle], 0);
3277 &resource_group_.GetResource(bound_resource_index_of_vehicle_[vehicle]);
3279 int64_t route_cost = 0;
3281 lp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
3282 vehicle, 1.0, next_accessor, resource,
3283 filter_objective_cost_ ? &route_cost :
nullptr);
3285 case DimensionSchedulingStatus::INFEASIBLE:
3287 case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
3288 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
3289 vehicle, 1.0, next_accessor, resource,
3290 filter_objective_cost_ ? &route_cost :
nullptr) ==
3291 DimensionSchedulingStatus::INFEASIBLE) {
3296 DCHECK(status == DimensionSchedulingStatus::OPTIMAL ||
3297 status == DimensionSchedulingStatus::FEASIBLE);
3303class ResourceAssignmentFilter :
public LocalSearchFilter {
3305 ResourceAssignmentFilter(
const std::vector<IntVar*>& nexts,
3306 LocalDimensionCumulOptimizer* optimizer,
3307 LocalDimensionCumulOptimizer* mp_optimizer,
3308 bool propagate_own_objective_value,
3309 bool filter_objective_cost);
3310 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
3311 int64_t objective_min, int64_t objective_max)
override;
3312 void Synchronize(
const Assignment* assignment,
3313 const Assignment* delta)
override;
3315 int64_t GetAcceptedObjectiveValue()
const override {
3316 return propagate_own_objective_value_ ? delta_cost_ : 0;
3318 int64_t GetSynchronizedObjectiveValue()
const override {
3319 return propagate_own_objective_value_ ? synchronized_cost_ : 0;
3321 std::string DebugString()
const override {
3322 return "ResourceAssignmentFilter(" + dimension_name_ +
")";
3326 std::vector<IntVarLocalSearchFilter*> resource_group_assignment_filters_;
3327 int64_t synchronized_cost_;
3328 int64_t delta_cost_;
3329 const bool propagate_own_objective_value_;
3330 const std::string dimension_name_;
3333ResourceAssignmentFilter::ResourceAssignmentFilter(
3334 const std::vector<IntVar*>& nexts,
3335 LocalDimensionCumulOptimizer* lp_optimizer,
3336 LocalDimensionCumulOptimizer* mp_optimizer,
3337 bool propagate_own_objective_value,
bool filter_objective_cost)
3338 : propagate_own_objective_value_(propagate_own_objective_value),
3339 dimension_name_(lp_optimizer->dimension()->name()) {
3340 const RoutingModel& model = *lp_optimizer->dimension()->model();
3341 for (
const auto& resource_group : model.GetResourceGroups()) {
3342 resource_group_assignment_filters_.push_back(
3343 model.solver()->RevAlloc(new ResourceGroupAssignmentFilter(
3344 nexts, resource_group.get(), lp_optimizer, mp_optimizer,
3345 filter_objective_cost)));
3349bool ResourceAssignmentFilter::Accept(
const Assignment* delta,
3350 const Assignment* deltadelta,
3351 int64_t objective_min,
3352 int64_t objective_max) {
3354 for (LocalSearchFilter* group_filter : resource_group_assignment_filters_) {
3355 if (!group_filter->Accept(delta, deltadelta, objective_min,
3360 std::max(delta_cost_, group_filter->GetAcceptedObjectiveValue());
3361 DCHECK_LE(delta_cost_, objective_max)
3362 <<
"ResourceGroupAssignmentFilter should return false when the "
3363 "objective_max is exceeded.";
3368void ResourceAssignmentFilter::Synchronize(
const Assignment* assignment,
3369 const Assignment* delta) {
3370 synchronized_cost_ = 0;
3371 for (LocalSearchFilter* group_filter : resource_group_assignment_filters_) {
3372 group_filter->Synchronize(assignment, delta);
3373 synchronized_cost_ = std::max(
3374 synchronized_cost_, group_filter->GetSynchronizedObjectiveValue());
3383 bool propagate_own_objective_value,
bool filter_objective_cost) {
3384 const RoutingModel& model = *lp_optimizer->
dimension()->model();
3385 DCHECK_NE(lp_optimizer,
nullptr);
3386 DCHECK_NE(mp_optimizer,
nullptr);
3387 return model.solver()->RevAlloc(
new ResourceAssignmentFilter(
3388 model.Nexts(), lp_optimizer, mp_optimizer, propagate_own_objective_value,
3389 filter_objective_cost));
3406class CPFeasibilityFilter :
public IntVarLocalSearchFilter {
3408 explicit CPFeasibilityFilter(RoutingModel* routing_model);
3409 ~CPFeasibilityFilter()
override =
default;
3410 std::string DebugString()
const override {
return "CPFeasibilityFilter"; }
3411 bool Accept(
const Assignment* delta,
const Assignment* deltadelta,
3412 int64_t objective_min, int64_t objective_max)
override;
3413 void OnSynchronize(
const Assignment* delta)
override;
3416 void AddDeltaToAssignment(
const Assignment* delta, Assignment* assignment);
3418 static const int64_t kUnassigned;
3419 const RoutingModel*
const model_;
3420 Solver*
const solver_;
3421 Assignment*
const assignment_;
3422 Assignment*
const temp_assignment_;
3423 DecisionBuilder*
const restore_;
3424 SearchLimit*
const limit_;
3427const int64_t CPFeasibilityFilter::kUnassigned = -1;
3429CPFeasibilityFilter::CPFeasibilityFilter(RoutingModel* routing_model)
3430 : IntVarLocalSearchFilter(routing_model->Nexts()),
3431 model_(routing_model),
3432 solver_(routing_model->solver()),
3433 assignment_(solver_->MakeAssignment()),
3434 temp_assignment_(solver_->MakeAssignment()),
3435 restore_(solver_->MakeRestoreAssignment(temp_assignment_)),
3436 limit_(solver_->MakeCustomLimit(
3437 [routing_model]() {
return routing_model->CheckLimit(); })) {
3438 assignment_->Add(routing_model->Nexts());
3441bool CPFeasibilityFilter::Accept(
const Assignment* delta,
3445 temp_assignment_->Copy(assignment_);
3446 AddDeltaToAssignment(delta, temp_assignment_);
3448 return solver_->Solve(restore_, limit_);
3451void CPFeasibilityFilter::OnSynchronize(
const Assignment* delta) {
3452 AddDeltaToAssignment(delta, assignment_);
3455void CPFeasibilityFilter::AddDeltaToAssignment(
const Assignment* delta,
3456 Assignment* assignment) {
3457 if (delta ==
nullptr) {
3460 Assignment::IntContainer*
const container =
3461 assignment->MutableIntVarContainer();
3462 for (
const IntVarElement& delta_element :
3463 delta->IntVarContainer().elements()) {
3464 IntVar*
const var = delta_element.Var();
3465 int64_t index = kUnassigned;
3468 if (!FindIndex(var, &index))
continue;
3469 DCHECK_EQ(var, Var(index));
3470 const int64_t value = delta_element.Value();
3472 container->AddAtPosition(var, index)->SetValue(value);
3473 if (model_->IsStart(index)) {
3474 if (model_->IsEnd(value)) {
3476 container->MutableElement(index)->Deactivate();
3479 container->MutableElement(index)->Activate();
3488 return routing_model->solver()->RevAlloc(
3489 new CPFeasibilityFilter(routing_model));
3493 std::vector<int> path_end)
3494 : num_nodes_(num_nodes),
3495 num_paths_(path_start.size()),
3496 num_nodes_threshold_(
std::max(16, 4 * num_nodes_))
3498 DCHECK_EQ(path_start.size(), num_paths_);
3499 DCHECK_EQ(path_end.size(), num_paths_);
3500 for (
int p = 0; p < num_paths_; ++p) {
3501 path_start_end_.push_back({path_start[p], path_end[p]});
3507 is_invalid_ =
false;
3509 committed_index_.assign(num_nodes_, -1);
3511 committed_nodes_.assign(2 * num_paths_, -1);
3512 chains_.assign(num_paths_ + 1, {-1, -1});
3513 paths_.assign(num_paths_, {-1, -1});
3514 for (
int path = 0; path < num_paths_; ++path) {
3515 const int index = 2 * path;
3516 const auto& [start,
end] = path_start_end_[path];
3517 committed_index_[start] = index;
3518 committed_index_[
end] = index + 1;
3520 committed_nodes_[index] = start;
3521 committed_nodes_[index + 1] =
end;
3523 committed_paths_[start] = path;
3524 committed_paths_[
end] = path;
3526 chains_[path] = {index, index + 2};
3527 paths_[path] = {path, path + 1};
3529 chains_[num_paths_] = {0, 0};
3532 for (
int node = 0; node < num_nodes_; ++node) {
3533 if (committed_index_[node] != -1)
continue;
3534 committed_index_[node] = committed_nodes_.size();
3535 committed_nodes_.push_back(node);
3540 const PathBounds bounds = paths_[path];
3542 chains_.data() + bounds.end_index,
3543 committed_nodes_.data());
3547 const PathBounds bounds = paths_[path];
3549 chains_.data() + bounds.end_index,
3550 committed_nodes_.data());
3554 changed_paths_.push_back(path);
3555 const int path_begin_index = chains_.size();
3556 chains_.insert(chains_.end(), chains.begin(), chains.end());
3557 const int path_end_index = chains_.size();
3558 paths_[path] = {path_begin_index, path_end_index};
3559 chains_.emplace_back(0, 0);
3563 for (
const int loop : new_loops) {
3567 changed_loops_.push_back(loop);
3573 if (committed_nodes_.size() < num_nodes_threshold_) {
3574 IncrementalCommit();
3581 is_invalid_ =
false;
3582 chains_.resize(num_paths_ + 1);
3583 for (
const int path : changed_paths_) {
3584 paths_[path] = {path, path + 1};
3586 changed_paths_.clear();
3587 changed_loops_.clear();
3590void PathState::CopyNewPathAtEndOfNodes(
int path) {
3592 const PathBounds path_bounds = paths_[path];
3593 for (
int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
3594 const ChainBounds chain_bounds = chains_[i];
3595 committed_nodes_.insert(committed_nodes_.end(),
3596 committed_nodes_.data() + chain_bounds.begin_index,
3597 committed_nodes_.data() + chain_bounds.end_index);
3598 if (committed_paths_[committed_nodes_.back()] == path)
continue;
3599 for (
int i = chain_bounds.begin_index; i < chain_bounds.end_index; ++i) {
3600 const int node = committed_nodes_[i];
3601 committed_paths_[node] = path;
3608void PathState::IncrementalCommit() {
3609 const int new_nodes_begin = committed_nodes_.size();
3611 const int chain_begin = committed_nodes_.size();
3612 CopyNewPathAtEndOfNodes(path);
3613 const int chain_end = committed_nodes_.size();
3614 chains_[path] = {chain_begin, chain_end};
3617 const int new_nodes_end = committed_nodes_.size();
3618 for (
int i = new_nodes_begin;
i < new_nodes_end; ++
i) {
3619 const int node = committed_nodes_[
i];
3620 committed_index_[node] =
i;
3625 committed_paths_[loop] =
kLoop;
3631void PathState::FullCommit() {
3634 const int old_num_nodes = committed_nodes_.size();
3635 for (
int path = 0; path < num_paths_; ++path) {
3636 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
3637 CopyNewPathAtEndOfNodes(path);
3638 const int new_path_end = committed_nodes_.size() - old_num_nodes;
3639 chains_[path] = {new_path_begin, new_path_end};
3641 committed_nodes_.erase(committed_nodes_.begin(),
3642 committed_nodes_.begin() + old_num_nodes);
3645 constexpr int kUnindexed = -1;
3646 committed_index_.assign(num_nodes_, kUnindexed);
3648 for (
const int node : committed_nodes_) {
3649 committed_index_[node] = index++;
3651 for (
int node = 0; node < num_nodes_; ++node) {
3652 if (committed_index_[node] != kUnindexed)
continue;
3653 committed_index_[node] = index++;
3654 committed_nodes_.push_back(node);
3657 committed_paths_[loop] =
kLoop;
3667 std::string DebugString()
const override {
return "PathStateFilter"; }
3668 PathStateFilter(std::unique_ptr<PathState> path_state,
3669 const std::vector<IntVar*>& nexts);
3670 void Relax(
const Assignment* delta,
const Assignment* deltadelta)
override;
3671 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
3674 void Synchronize(
const Assignment*,
const Assignment*)
override {};
3675 void Commit(
const Assignment* assignment,
const Assignment* delta)
override;
3676 void Revert()
override;
3677 void Reset()
override;
3681 struct TailHeadIndices {
3688 bool operator<(
const IndexArc& other)
const {
return index < other.index; }
3695 void MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
3698 void MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
3700 const std::unique_ptr<PathState> path_state_;
3702 std::vector<int> variable_index_to_node_;
3706 std::vector<int> changed_paths_;
3707 std::vector<bool> path_has_changed_;
3708 std::vector<std::pair<int, int>> changed_arcs_;
3709 std::vector<int> changed_loops_;
3710 std::vector<TailHeadIndices> tail_head_indices_;
3711 std::vector<IndexArc> arcs_by_tail_index_;
3712 std::vector<IndexArc> arcs_by_head_index_;
3713 std::vector<int> next_arc_;
3714 std::vector<PathState::ChainBounds> path_chains_;
3717PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
3718 const std::vector<IntVar*>& nexts)
3719 : path_state_(std::move(path_state)) {
3721 int min_index = std::numeric_limits<int>::max();
3722 int max_index = std::numeric_limits<int>::min();
3723 for (
const IntVar* next : nexts) {
3724 const int index = next->index();
3725 min_index = std::min<int>(min_index, index);
3726 max_index = std::max<int>(max_index, index);
3728 variable_index_to_node_.resize(max_index - min_index + 1, -1);
3729 index_offset_ = min_index;
3732 for (
int node = 0; node < nexts.size(); ++node) {
3733 const int index = nexts[node]->index() - index_offset_;
3734 variable_index_to_node_[index] = node;
3736 path_has_changed_.assign(path_state_->NumPaths(),
false);
3740 path_state_->Revert();
3741 changed_arcs_.clear();
3742 for (
const IntVarElement& var_value : delta->IntVarContainer().elements()) {
3743 if (var_value.Var() ==
nullptr)
continue;
3744 const int index = var_value.Var()->index() - index_offset_;
3745 if (index < 0 || variable_index_to_node_.size() <= index)
continue;
3746 const int node = variable_index_to_node_[index];
3747 if (node == -1)
continue;
3748 if (var_value.Bound()) {
3749 changed_arcs_.emplace_back(node, var_value.Value());
3751 path_state_->Revert();
3752 path_state_->SetInvalid();
3759void PathStateFilter::Reset() { path_state_->Reset(); }
3764void PathStateFilter::Commit(
const Assignment* assignment,
3765 const Assignment* delta) {
3766 path_state_->Revert();
3767 if (delta ==
nullptr || delta->Empty()) {
3768 Relax(assignment,
nullptr);
3770 Relax(delta,
nullptr);
3772 path_state_->Commit();
3775void PathStateFilter::Revert() { path_state_->Revert(); }
3777void PathStateFilter::CutChains() {
3781 for (
const int path : changed_paths_) path_has_changed_[path] =
false;
3782 changed_paths_.clear();
3783 tail_head_indices_.clear();
3784 changed_loops_.clear();
3785 int num_changed_arcs = 0;
3786 for (
const auto [node, next] : changed_arcs_) {
3787 const int node_index = path_state_->CommittedIndex(node);
3788 const int next_index = path_state_->CommittedIndex(next);
3789 const int node_path = path_state_->Path(node);
3791 (next_index != node_index + 1 || node_path < 0)) {
3792 tail_head_indices_.push_back({node_index, next_index});
3793 changed_arcs_[num_changed_arcs++] = {node, next};
3794 if (node_path >= 0 && !path_has_changed_[node_path]) {
3795 path_has_changed_[node_path] =
true;
3796 changed_paths_.push_back(node_path);
3798 }
else if (node == next && node_path != PathState::kLoop) {
3799 changed_loops_.push_back(node);
3802 changed_arcs_.resize(num_changed_arcs);
3804 path_state_->ChangeLoops(changed_loops_);
3805 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
3806 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
3808 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
3812void PathStateFilter::
3813 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
3814 int num_visited_changed_arcs = 0;
3815 const int num_changed_arcs = tail_head_indices_.size();
3817 for (
const int path : changed_paths_) {
3818 path_chains_.clear();
3819 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
3820 int current_index = start_index;
3824 int selected_arc = -1;
3825 int selected_tail_index = std::numeric_limits<int>::max();
3826 for (
int i = num_visited_changed_arcs;
i < num_changed_arcs; ++
i) {
3827 const int tail_index = tail_head_indices_[
i].tail_index;
3828 if (current_index <= tail_index && tail_index < selected_tail_index) {
3830 selected_tail_index = tail_index;
3838 if (start_index <= current_index && current_index < end_index &&
3839 end_index <= selected_tail_index) {
3840 path_chains_.emplace_back(current_index, end_index);
3843 path_chains_.emplace_back(current_index, selected_tail_index + 1);
3844 current_index = tail_head_indices_[selected_arc].head_index;
3845 std::swap(tail_head_indices_[num_visited_changed_arcs],
3846 tail_head_indices_[selected_arc]);
3847 ++num_visited_changed_arcs;
3850 path_state_->ChangePath(path, path_chains_);
3854void PathStateFilter::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
3870 for (
const int path : changed_paths_) {
3871 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
3872 tail_head_indices_.push_back({end_index - 1, start_index});
3877 const int num_arc_indices = tail_head_indices_.size();
3878 arcs_by_tail_index_.resize(num_arc_indices);
3879 arcs_by_head_index_.resize(num_arc_indices);
3880 for (
int i = 0;
i < num_arc_indices; ++
i) {
3881 arcs_by_tail_index_[
i] = {tail_head_indices_[
i].tail_index,
i};
3882 arcs_by_head_index_[
i] = {tail_head_indices_[
i].head_index,
i};
3884 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
3885 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
3887 next_arc_.resize(num_arc_indices);
3888 for (
int i = 0;
i < num_arc_indices; ++
i) {
3889 next_arc_[arcs_by_head_index_[
i].arc] = arcs_by_tail_index_[
i].arc;
3895 const int first_fake_arc = num_arc_indices - changed_paths_.size();
3896 for (
int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
3897 path_chains_.clear();
3898 int32_t arc = fake_arc;
3900 const int chain_begin = tail_head_indices_[arc].head_index;
3901 arc = next_arc_[arc];
3902 const int chain_end = tail_head_indices_[arc].tail_index + 1;
3903 path_chains_.emplace_back(chain_begin, chain_end);
3904 }
while (arc != fake_arc);
3905 const int path = changed_paths_[fake_arc - first_fake_arc];
3906 path_state_->ChangePath(path, path_chains_);
3913 std::unique_ptr<PathState> path_state,
3914 const std::vector<IntVar*>& nexts) {
3915 PathStateFilter* filter =
new PathStateFilter(std::move(path_state), nexts);
3920using EInterval = DimensionChecker::ExtendedInterval;
3922constexpr int64_t
kint64min = std::numeric_limits<int64_t>::min();
3923constexpr int64_t
kint64max = std::numeric_limits<int64_t>::max();
3925EInterval operator&(
const EInterval& i1,
const EInterval& i2) {
3926 return {std::max(i1.num_negative_infinity == 0 ? i1.min :
kint64min,
3927 i2.num_negative_infinity == 0 ? i2.min :
kint64min),
3928 std::min(i1.num_positive_infinity == 0 ? i1.max :
kint64max,
3929 i2.num_positive_infinity == 0 ? i2.max :
kint64max),
3930 std::min(i1.num_negative_infinity, i2.num_negative_infinity),
3931 std::min(i1.num_positive_infinity, i2.num_positive_infinity)};
3934EInterval operator&=(EInterval& i1,
const EInterval& i2) {
3939bool IsEmpty(
const EInterval& interval) {
3940 const int64_t minimum_value =
3941 interval.num_negative_infinity == 0 ? interval.min :
kint64min;
3942 const int64_t maximum_value =
3943 interval.num_positive_infinity == 0 ? interval.max :
kint64max;
3944 return minimum_value > maximum_value;
3947EInterval
operator+(
const EInterval& i1,
const EInterval& i2) {
3948 return {
CapAdd(i1.min, i2.min),
CapAdd(i1.max, i2.max),
3949 i1.num_negative_infinity + i2.num_negative_infinity,
3950 i1.num_positive_infinity + i2.num_positive_infinity};
3953EInterval& operator+=(EInterval& i1,
const EInterval& i2) {
3958EInterval
operator-(
const EInterval& i1,
const EInterval& i2) {
3959 return {
CapSub(i1.min, i2.max),
CapSub(i1.max, i2.min),
3960 i1.num_negative_infinity + i2.num_positive_infinity,
3961 i1.num_positive_infinity + i2.num_negative_infinity};
3966EInterval Delta(
const EInterval& from,
const EInterval&
to) {
3968 to.num_negative_infinity - from.num_negative_infinity,
3969 to.num_positive_infinity - from.num_positive_infinity};
3972EInterval ToExtendedInterval(DimensionChecker::Interval interval) {
3973 const bool is_neg_infinity = interval.min ==
kint64min;
3974 const bool is_pos_infinity = interval.max ==
kint64max;
3975 return {is_neg_infinity ? 0 : interval.min,
3976 is_pos_infinity ? 0 : interval.max, is_neg_infinity ? 1 : 0,
3977 is_pos_infinity ? 1 : 0};
3980std::vector<EInterval> ToExtendedIntervals(
3981 absl::Span<const DimensionChecker::Interval> intervals) {
3982 std::vector<EInterval> extended_intervals;
3983 extended_intervals.reserve(intervals.size());
3984 for (
const auto& interval : intervals) {
3985 extended_intervals.push_back(ToExtendedInterval(interval));
3987 return extended_intervals;
3992 const PathState* path_state, std::vector<Interval> path_capacity,
3993 std::vector<int> path_class,
3994 std::vector<std::function<
Interval(int64_t, int64_t)>>
3995 demand_per_path_class,
3996 std::vector<Interval> node_capacity,
int min_range_size_for_riq)
3997 : path_state_(path_state),
3998 path_capacity_(ToExtendedIntervals(path_capacity)),
3999 path_class_(
std::move(path_class)),
4000 demand_per_path_class_(
std::move(demand_per_path_class)),
4001 node_capacity_(ToExtendedIntervals(node_capacity)),
4002 index_(path_state_->NumNodes(), 0),
4003 maximum_riq_layer_size_(
std::max(
4004 16, 4 * path_state_->NumNodes())),
4005 min_range_size_for_riq_(min_range_size_for_riq) {
4006 const int num_nodes = path_state_->NumNodes();
4007 cached_demand_.resize(num_nodes);
4008 const int num_paths = path_state_->NumPaths();
4009 DCHECK_EQ(num_paths, path_capacity_.size());
4010 DCHECK_EQ(num_paths, path_class_.size());
4012 riq_.resize(maximum_riq_exponent + 1);
4017 if (path_state_->IsInvalid())
return true;
4018 for (
const int path : path_state_->ChangedPaths()) {
4019 const EInterval path_capacity = path_capacity_[path];
4020 const int path_class = path_class_[path];
4023 int prev_node = path_state_->Start(path);
4024 EInterval cumul = node_capacity_[prev_node] & path_capacity;
4025 if (IsEmpty(cumul))
return false;
4027 for (
const auto chain : path_state_->Chains(path)) {
4028 const int first_node = chain.First();
4029 const int last_node = chain.Last();
4031 if (prev_node != first_node) {
4034 const EInterval demand = ToExtendedInterval(
4035 demand_per_path_class_[path_class](prev_node, first_node));
4037 cumul &= path_capacity;
4038 cumul &= node_capacity_[first_node];
4039 if (IsEmpty(cumul))
return false;
4040 prev_node = first_node;
4044 const int first_index = index_[first_node];
4045 const int last_index = index_[last_node];
4046 const int chain_path = path_state_->Path(first_node);
4047 const int chain_path_class =
4048 chain_path < 0 ? -1 : path_class_[chain_path];
4052 const bool chain_is_cached = chain_path_class == path_class;
4053 if (last_index - first_index > min_range_size_for_riq_ &&
4055 UpdateCumulUsingChainRIQ(first_index, last_index, path_capacity, cumul);
4056 if (IsEmpty(cumul))
return false;
4057 prev_node = chain.Last();
4059 for (
const int node : chain.WithoutFirstNode()) {
4060 const EInterval demand =
4062 ? cached_demand_[prev_node]
4063 : ToExtendedInterval(
4064 demand_per_path_class_[path_class](prev_node, node));
4066 cumul &= node_capacity_[node];
4067 cumul &= path_capacity;
4068 if (IsEmpty(cumul))
return false;
4078 const int current_layer_size = riq_[0].size();
4079 int change_size = path_state_->ChangedPaths().size();
4080 for (
const int path : path_state_->ChangedPaths()) {
4081 for (
const auto chain : path_state_->Chains(path)) {
4082 change_size += chain.NumNodes();
4085 if (current_layer_size + change_size <= maximum_riq_layer_size_) {
4086 IncrementalCommit();
4092void DimensionChecker::IncrementalCommit() {
4094 const int begin_index = riq_[0].size();
4095 AppendPathDemandsToSums(path);
4096 UpdateRIQStructure(begin_index, riq_[0].size());
4100void DimensionChecker::FullCommit() {
4102 for (
auto& layer : riq_) layer.clear();
4104 const int num_paths = path_state_->NumPaths();
4105 for (
int path = 0; path < num_paths; ++path) {
4106 const int begin_index = riq_[0].size();
4107 AppendPathDemandsToSums(path);
4108 UpdateRIQStructure(begin_index, riq_[0].size());
4112void DimensionChecker::AppendPathDemandsToSums(
int path) {
4115 const int path_class = path_class_[path];
4116 EInterval demand_sum = {0, 0, 0, 0};
4117 int prev = path_state_->Start(path);
4118 int index = riq_[0].size();
4119 for (
const int node : path_state_->Nodes(path)) {
4121 const EInterval demand =
4122 prev == node ? EInterval{0, 0, 0, 0}
4123 : ToExtendedInterval(
4124 demand_per_path_class_[path_class](prev, node));
4125 demand_sum += demand;
4126 cached_demand_[prev] = demand;
4129 index_[node] = index++;
4130 riq_[0].push_back({.cumuls_to_fst = node_capacity_[node],
4131 .tightest_tsum = demand_sum,
4132 .cumuls_to_lst = node_capacity_[node],
4133 .tsum_at_fst = demand_sum,
4134 .tsum_at_lst = demand_sum});
4136 cached_demand_[path_state_->End(path)] = {0, 0, 0, 0};
4139void DimensionChecker::UpdateRIQStructure(
int begin_index,
int end_index) {
4142 const int max_layer =
4144 for (
int layer = 1, half_window = 1; layer <= max_layer;
4145 ++layer, half_window *= 2) {
4146 riq_[layer].resize(end_index);
4147 for (
int i = begin_index + 2 * half_window - 1;
i < end_index; ++
i) {
4153 const RIQNode& fw = riq_[layer - 1][
i - half_window];
4154 const RIQNode& lw = riq_[layer - 1][
i];
4155 const EInterval lst_to_lst = Delta(fw.tsum_at_lst, lw.tsum_at_lst);
4156 const EInterval fst_to_fst = Delta(fw.tsum_at_fst, lw.tsum_at_fst);
4159 .cumuls_to_fst = fw.cumuls_to_fst & lw.cumuls_to_fst - fst_to_fst,
4160 .tightest_tsum = fw.tightest_tsum & lw.tightest_tsum,
4161 .cumuls_to_lst = fw.cumuls_to_lst + lst_to_lst & lw.cumuls_to_lst,
4162 .tsum_at_fst = fw.tsum_at_fst,
4163 .tsum_at_lst = lw.tsum_at_lst};
4172void DimensionChecker::UpdateCumulUsingChainRIQ(
4173 int first_index,
int last_index,
const ExtendedInterval& path_capacity,
4174 ExtendedInterval& cumul)
const {
4175 DCHECK_LE(0, first_index);
4176 DCHECK_LT(first_index, last_index);
4177 DCHECK_LT(last_index, riq_[0].size());
4179 const int window = 1 << layer;
4180 const RIQNode& fw = riq_[layer][first_index + window - 1];
4181 const RIQNode& lw = riq_[layer][last_index];
4184 cumul &= fw.cumuls_to_fst;
4185 cumul &= lw.cumuls_to_fst - Delta(fw.tsum_at_fst, lw.tsum_at_fst);
4186 cumul &= path_capacity -
4187 Delta(fw.tsum_at_fst, fw.tightest_tsum & lw.tightest_tsum);
4190 if (IsEmpty(cumul))
return;
4193 cumul += Delta(fw.tsum_at_fst, lw.tsum_at_lst);
4196 cumul &= fw.cumuls_to_lst + Delta(fw.tsum_at_lst, lw.tsum_at_lst);
4197 cumul &= lw.cumuls_to_lst;
4204 std::string DebugString()
const override {
return name_; }
4205 DimensionFilter(std::unique_ptr<DimensionChecker> checker,
4206 absl::string_view dimension_name)
4207 : checker_(std::move(checker)),
4208 name_(absl::StrCat(
"DimensionFilter(", dimension_name,
")")) {}
4210 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
4211 return checker_->Check();
4214 void Synchronize(
const Assignment*,
const Assignment*)
override {
4219 std::unique_ptr<DimensionChecker> checker_;
4220 const std::string name_;
4226 Solver* solver, std::unique_ptr<DimensionChecker> checker,
4227 absl::string_view dimension_name) {
4228 DimensionFilter* filter =
4229 new DimensionFilter(std::move(checker), dimension_name);
4234 PathState* path_state, std::vector<PathData> path_data)
4235 : path_state_(path_state), path_data_(
std::move(path_data)) {}
4238 for (
const int path : path_state_->ChangedPaths()) {
4239 path_data_[path].start_cumul.Relax();
4240 path_data_[path].end_cumul.Relax();
4241 path_data_[path].span.Relax();
4246 for (
const int path : path_state_->ChangedPaths()) {
4247 if (!path_data_[path].span.Exists())
continue;
4248 const PathData& data = path_data_[path];
4250 int64_t lb_span = data.
span.
Min();
4254 int64_t lb_span_tw = total_transit;
4258 if (!br.is_performed_min)
continue;
4259 if (br.start_max < end_min && start_max < br.end_min) {
4260 CapAddTo(br.duration_min, &lb_span_tw);
4261 start_max = std::min(start_max, br.start_max);
4262 end_min = std::max(end_min, br.end_min);
4265 lb_span = std::max({lb_span, lb_span_tw,
CapSub(end_min, start_max)});
4271 start_min = std::max(start_min,
CapSub(end_min, data.
span.
Max()));
4273 end_max = std::min(end_max,
CapAdd(start_max, data.
span.
Max()));
4274 int num_feasible_breaks = 0;
4276 if (start_min <= br.start_max && br.end_min <= end_max) {
4277 break_start_min = std::min(break_start_min, br.start_min);
4278 break_end_max = std::max(break_end_max, br.end_max);
4279 ++num_feasible_breaks;
4286 for (
const auto& [max_interbreak, min_break_duration] :
4292 if (max_interbreak == 0) {
4293 if (total_transit > 0)
return false;
4296 int64_t min_num_breaks =
4297 std::max<int64_t>(0, (total_transit - 1) / max_interbreak);
4298 if (lb_span > max_interbreak) {
4299 min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
4301 if (min_num_breaks > num_feasible_breaks)
return false;
4304 CapAdd(total_transit,
CapProd(min_num_breaks, min_break_duration)));
4305 if (min_num_breaks > 0) {
4314 if (!data.
span.
SetMin(lb_span))
return false;
4316 start_max = std::min(start_max,
CapSub(end_max, lb_span));
4318 end_min = std::max(end_min,
CapAdd(start_min, lb_span));
4328 LightVehicleBreaksFilter(std::unique_ptr<LightVehicleBreaksChecker> checker,
4329 absl::string_view dimension_name)
4330 : checker_(
std::move(checker)),
4331 name_(
absl::StrCat(
"LightVehicleBreaksFilter(", dimension_name,
")")) {}
4333 std::string DebugString()
const override {
return name_; }
4335 void Relax(
const Assignment*,
const Assignment*)
override {
4339 bool Accept(
const Assignment*,
const Assignment*, int64_t, int64_t)
override {
4340 return checker_->Check();
4343 void Synchronize(
const Assignment*,
const Assignment*)
override {
4348 std::unique_ptr<LightVehicleBreaksChecker> checker_;
4349 const std::string name_;
4355 Solver* solver, std::unique_ptr<LightVehicleBreaksChecker> checker,
4356 absl::string_view dimension_name) {
4357 LightVehicleBreaksFilter* filter =
4358 new LightVehicleBreaksFilter(std::move(checker), dimension_name);
4364 tree_location_.clear();
4366 for (
auto& layer : tree_layers_) layer.clear();
4371 const int begin_index = tree_location_.size();
4372 const int end_index = elements_.size();
4373 DCHECK_LE(begin_index, end_index);
4374 if (begin_index >= end_index)
return;
4381 const int old_node_size = nodes_.size();
4382 for (
int i = begin_index; i < end_index; ++i) {
4383 nodes_.push_back({.pivot_height = elements_[i].height, .pivot_index = -1});
4385 std::sort(nodes_.begin() + old_node_size, nodes_.end());
4386 nodes_.erase(std::unique(nodes_.begin() + old_node_size, nodes_.end()),
4391 const int new_node_size = nodes_.size();
4392 tree_location_.resize(end_index, {.node_begin = old_node_size,
4393 .node_end = new_node_size,
4394 .sequence_first = begin_index});
4398 const int num_layers =
4400 if (tree_layers_.size() <= num_layers) tree_layers_.resize(num_layers);
4401 for (
int l = 0; l < num_layers; ++l) {
4402 tree_layers_[l].resize(end_index,
4403 {.prefix_sum = 0, .left_index = -1, .is_left = 0});
4408 const auto fill_subtree = [
this](
auto& fill_subtree,
int layer,
4409 int node_begin,
int node_end,
4410 int range_begin,
int range_end) {
4411 DCHECK_LT(node_begin, node_end);
4412 DCHECK_LT(range_begin, range_end);
4415 for (
int i = range_begin; i < range_end; ++i) {
4416 sum += elements_[i].weight;
4417 tree_layers_[layer][i].prefix_sum = sum;
4419 if (node_begin + 1 == node_end)
return;
4424 const int node_mid = node_begin + (node_end - node_begin) / 2;
4425 const int64_t pivot_height = nodes_[node_mid].pivot_height;
4426 int pivot_index = range_begin;
4427 for (
int i = range_begin; i < range_end; ++i) {
4428 tree_layers_[layer][i].left_index = pivot_index;
4429 tree_layers_[layer][i].is_left = elements_[i].height < pivot_height;
4430 if (elements_[i].height < pivot_height) ++pivot_index;
4432 nodes_[node_mid].pivot_index = pivot_index;
4435 std::stable_partition(
4436 elements_.begin() + range_begin, elements_.begin() + range_end,
4437 [pivot_height](
const auto& el) { return el.height < pivot_height; });
4439 fill_subtree(fill_subtree, layer + 1, node_begin, node_mid, range_begin,
4441 fill_subtree(fill_subtree, layer + 1, node_mid, node_end, pivot_index,
4444 fill_subtree(fill_subtree, 0, old_node_size, new_node_size, begin_index,
4450 int end_index)
const {
4451 DCHECK_LE(begin_index, end_index);
4452 DCHECK_LE(end_index, tree_location_.size());
4453 DCHECK_EQ(tree_location_.size(), elements_.size());
4454 if (begin_index >= end_index)
return 0;
4455 auto [node_begin, node_end, sequence_first_index] =
4456 tree_location_[begin_index];
4457 DCHECK_EQ(tree_location_[end_index - 1].sequence_first,
4458 sequence_first_index);
4460 .range_first_index = begin_index,
4461 .range_last_index = end_index - 1,
4462 .range_first_is_node_first = begin_index == sequence_first_index};
4464 if (nodes_[node_end - 1].pivot_height < threshold_height)
return 0;
4467 int64_t min_height_of_current_node = nodes_[node_begin].pivot_height;
4468 for (
int l = 0; !range.Empty(); ++l) {
4469 const ElementInfo* elements = tree_layers_[l].data();
4470 if (threshold_height <= min_height_of_current_node) {
4473 sum += range.Sum(elements);
4475 }
else if (node_begin + 1 == node_end) {
4480 const int node_mid = node_begin + (node_end - node_begin) / 2;
4481 const auto [pivot_height, pivot_index] = nodes_[node_mid];
4482 const ElementRange right = range.RightSubRange(elements, pivot_index);
4483 if (threshold_height < pivot_height) {
4486 if (!right.Empty()) sum += right.Sum(tree_layers_[l + 1].data());
4488 range = range.LeftSubRange(elements);
4489 node_end = node_mid;
4493 node_begin = node_mid;
4494 min_height_of_current_node = pivot_height;
4501 const PathState* path_state, std::vector<int64_t> force_start_min,
4502 std::vector<int64_t> force_end_min, std::vector<int> force_class,
4503 std::vector<
const std::function<int64_t(int64_t)>*> force_per_class,
4504 std::vector<int> distance_class,
4505 std::vector<
const std::function<int64_t(int64_t, int64_t)>*>
4507 std::vector<EnergyCost> path_energy_cost,
4508 std::vector<bool> path_has_cost_when_empty)
4509 : path_state_(path_state),
4510 force_start_min_(
std::move(force_start_min)),
4511 force_end_min_(
std::move(force_end_min)),
4512 force_class_(
std::move(force_class)),
4513 distance_class_(
std::move(distance_class)),
4514 force_per_class_(
std::move(force_per_class)),
4515 distance_per_class_(
std::move(distance_per_class)),
4516 path_energy_cost_(
std::move(path_energy_cost)),
4517 path_has_cost_when_empty_(
std::move(path_has_cost_when_empty)),
4518 maximum_range_query_size_(4 * path_state_->NumNodes()),
4519 force_rmq_index_of_node_(path_state_->NumNodes()),
4520 threshold_query_index_of_node_(path_state_->NumNodes()) {
4521 const int num_nodes = path_state_->NumNodes();
4522 cached_force_.resize(num_nodes);
4523 cached_distance_.resize(num_nodes);
4524 FullCacheAndPrecompute();
4525 committed_total_cost_ = 0;
4526 committed_path_cost_.assign(path_state_->NumPaths(), 0);
4527 const int num_paths = path_state_->NumPaths();
4528 for (
int path = 0; path < num_paths; ++path) {
4529 committed_path_cost_[path] = ComputePathCost(path);
4530 CapAddTo(committed_path_cost_[path], &committed_total_cost_);
4532 accepted_total_cost_ = committed_total_cost_;
4536 if (path_state_->IsInvalid())
return true;
4537 accepted_total_cost_ = committed_total_cost_;
4538 for (
const int path : path_state_->ChangedPaths()) {
4539 accepted_total_cost_ =
4540 CapSub(accepted_total_cost_, committed_path_cost_[path]);
4541 CapAddTo(ComputePathCost(path), &accepted_total_cost_);
4542 if (accepted_total_cost_ ==
kint64max)
return false;
4547void PathEnergyCostChecker::CacheAndPrecomputeRangeQueriesOfPath(
int path) {
4550 const auto& force_evaluator = *force_per_class_[force_class_[path]];
4551 const auto& distance_evaluator = *distance_per_class_[distance_class_[path]];
4552 int force_index = force_rmq_.
TableSize();
4553 int threshold_index = energy_query_.
TreeSize();
4554 int64_t total_force = 0;
4556 const int start_node = path_state_->
Start(path);
4557 int prev_node = start_node;
4559 for (
const int node : path_state_->
Nodes(path)) {
4560 if (prev_node != node) {
4561 const int64_t distance = distance_evaluator(prev_node, node);
4562 cached_distance_[prev_node] = distance;
4563 energy_query_.
PushBack(total_force, total_force * distance);
4564 distance_query_.
PushBack(total_force, distance);
4567 threshold_query_index_of_node_[node] = threshold_index++;
4569 force_rmq_index_of_node_[node] = force_index++;
4570 const int64_t force = force_evaluator(node);
4571 cached_force_[node] = force;
4572 total_force += force;
4574 force_rmq_.MakeTableFromNewElements();
4575 energy_query_.MakeTreeFromNewElements();
4576 distance_query_.MakeTreeFromNewElements();
4579void PathEnergyCostChecker::IncrementalCacheAndPrecompute() {
4580 for (
const int path : path_state_->ChangedPaths()) {
4581 CacheAndPrecomputeRangeQueriesOfPath(path);
4585void PathEnergyCostChecker::FullCacheAndPrecompute() {
4588 const int num_paths = path_state_->NumPaths();
4589 for (
int path = 0; path < num_paths; ++path) {
4590 CacheAndPrecomputeRangeQueriesOfPath(path);
4595 int change_size = path_state_->ChangedPaths().size();
4596 for (
const int path : path_state_->ChangedPaths()) {
4597 for (
const auto chain : path_state_->Chains(path)) {
4598 change_size += chain.NumNodes();
4600 committed_total_cost_ =
4601 CapSub(committed_total_cost_, committed_path_cost_[path]);
4602 committed_path_cost_[path] = ComputePathCost(path);
4603 CapAddTo(committed_path_cost_[path], &committed_total_cost_);
4606 const int current_layer_size = force_rmq_.TableSize();
4607 if (current_layer_size + change_size <= maximum_range_query_size_) {
4608 IncrementalCacheAndPrecompute();
4610 FullCacheAndPrecompute();
4614int64_t PathEnergyCostChecker::ComputePathCost(int64_t path)
const {
4615 const int path_force_class = force_class_[path];
4616 const auto& force_evaluator = *force_per_class_[path_force_class];
4619 int64_t total_force = force_start_min_[path];
4620 int64_t min_force = total_force;
4621 int num_path_nodes = 0;
4622 int prev_node = path_state_->
Start(path);
4623 for (
const auto chain : path_state_->
Chains(path)) {
4624 num_path_nodes += chain.NumNodes();
4626 if (chain.First() != prev_node) {
4627 const int64_t force_to_node = force_evaluator(prev_node);
4628 CapAddTo(force_to_node, &total_force);
4629 min_force = std::min(min_force, total_force);
4630 prev_node = chain.First();
4634 const int chain_path = path_state_->
Path(chain.First());
4635 const int chain_force_class =
4636 chain_path < 0 ? -1 : force_class_[chain_path];
4637 const bool force_is_cached = chain_force_class == path_force_class;
4638 if (force_is_cached && chain.NumNodes() >= 2) {
4639 const int first_index = force_rmq_index_of_node_[chain.First()];
4640 const int last_index = force_rmq_index_of_node_[chain.Last()];
4642 const int64_t first_total_force = force_rmq_.
array()[first_index];
4643 const int64_t last_total_force = force_rmq_.
array()[last_index];
4644 const int64_t min_total_force =
4647 min_force = std::min(min_force,
4648 total_force - first_total_force + min_total_force);
4649 CapAddTo(last_total_force - first_total_force, &total_force);
4650 prev_node = chain.Last();
4652 for (
const int node : chain.WithoutFirstNode()) {
4653 const int64_t force = force_is_cached ? cached_force_[prev_node]
4654 : force_evaluator(prev_node);
4656 min_force = std::min(min_force, total_force);
4661 if (num_path_nodes == 2 && !path_has_cost_when_empty_[path])
return 0;
4667 total_force = std::max<int64_t>(
4668 {0,
CapOpp(min_force),
CapSub(force_end_min_[path], total_force)});
4669 CapAddTo(force_start_min_[path], &total_force);
4672 const int path_distance_class = distance_class_[path];
4673 const auto& distance_evaluator = *distance_per_class_[path_distance_class];
4674 const EnergyCost& cost = path_energy_cost_[path];
4675 int64_t energy_below = 0;
4676 int64_t energy_above = 0;
4677 prev_node = path_state_->Start(path);
4678 for (
const auto chain : path_state_->Chains(path)) {
4680 if (chain.First() != prev_node) {
4681 const int64_t distance = distance_evaluator(prev_node, chain.First());
4682 CapAddTo(force_evaluator(prev_node), &total_force);
4685 const int64_t force_above =
4686 std::max<int64_t>(0,
CapSub(total_force, cost.threshold));
4688 prev_node = chain.First();
4693 const int chain_path = path_state_->Path(chain.First());
4694 const int chain_force_class =
4695 chain_path < 0 ? -1 : force_class_[chain_path];
4696 const int chain_distance_class =
4697 chain_path < 0 ? -1 : distance_class_[chain_path];
4698 const bool force_is_cached = chain_force_class == path_force_class;
4699 const bool distance_is_cached = chain_distance_class == path_distance_class;
4701 if (force_is_cached && distance_is_cached && chain.NumNodes() >= 2) {
4702 const int first_index = threshold_query_index_of_node_[chain.First()];
4703 const int last_index = threshold_query_index_of_node_[chain.Last()];
4705 const int64_t zero_total_energy = energy_query_.RangeSumWithThreshold(
4707 const int64_t total_distance = distance_query_.RangeSumWithThreshold(
4716 const int64_t zero_total_force_first =
4717 force_rmq_.array()[force_rmq_index_of_node_[chain.First()]];
4718 const int64_t zero_threshold =
4719 CapSub(cost.threshold,
CapSub(total_force, zero_total_force_first));
4723 const int64_t zero_high_energy = energy_query_.RangeSumWithThreshold(
4724 zero_threshold, first_index, last_index);
4725 const int64_t zero_high_distance = distance_query_.RangeSumWithThreshold(
4726 zero_threshold, first_index, last_index);
4730 const int64_t zero_energy_above =
4731 CapSub(zero_high_energy,
CapProd(zero_high_distance, zero_threshold));
4737 CapAddTo(zero_energy_above, &energy_above);
4740 CapSub(cost.threshold, zero_threshold))),
4744 const int64_t zero_total_force_last =
4745 force_rmq_.array()[force_rmq_index_of_node_[chain.Last()]];
4748 prev_node = chain.Last();
4750 for (
const int node : chain.WithoutFirstNode()) {
4751 const int64_t force = force_is_cached ? cached_force_[prev_node]
4752 : force_evaluator(prev_node);
4753 const int64_t distance = distance_is_cached
4754 ? cached_distance_[prev_node]
4755 : distance_evaluator(prev_node, node);
4759 const int64_t force_above =
4760 std::max<int64_t>(0,
CapSub(total_force, cost.threshold));
4767 return CapAdd(
CapProd(energy_below, cost.cost_per_unit_below_threshold),
4768 CapProd(energy_above, cost.cost_per_unit_above_threshold));
4775 std::string DebugString()
const override {
return name_; }
4776 PathEnergyCostFilter(std::unique_ptr<PathEnergyCostChecker> checker,
4777 absl::string_view energy_name)
4778 : checker_(std::move(checker)),
4779 name_(absl::StrCat(
"PathEnergyCostFilter(", energy_name,
")")) {}
4781 bool Accept(
const Assignment*,
const Assignment*, int64_t objective_min,
4782 int64_t objective_max)
override {
4783 if (objective_max >
kint64max / 2)
return true;
4784 if (!checker_->Check())
return false;
4785 const int64_t cost = checker_->AcceptedCost();
4786 return objective_min <= cost && cost <= objective_max;
4789 void Synchronize(
const Assignment*,
const Assignment*)
override {
4793 int64_t GetSynchronizedObjectiveValue()
const override {
4794 return checker_->CommittedCost();
4796 int64_t GetAcceptedObjectiveValue()
const override {
4797 return checker_->AcceptedCost();
4801 std::unique_ptr<PathEnergyCostChecker> checker_;
4802 const std::string name_;
4808 Solver* solver, std::unique_ptr<PathEnergyCostChecker> checker,
4809 absl::string_view dimension_name) {
4810 PathEnergyCostFilter* filter =
4811 new PathEnergyCostFilter(std::move(checker), dimension_name);
const std::vector< E > & elements() const
AssignmentContainer< IntVar, IntVarElement > IntContainer
const IntContainer & IntVarContainer() const
Generic path-based filter class.
static const int64_t kUnassigned
BasePathFilter(const std::vector< IntVar * > &nexts, int next_domain_size, const PathsMetadata &paths_metadata)
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max) override
void OnSynchronize(const Assignment *delta) override
int64_t Start(int i) const
DimensionChecker(const PathState *path_state, std::vector< Interval > path_capacity, std::vector< int > path_class, std::vector< std::function< Interval(int64_t, int64_t)> > demand_per_path_class, std::vector< Interval > node_capacity, int min_range_size_for_riq=kOptimalMinRangeSizeForRIQ)
absl::Span< const int64_t > CommittedTravels(int path) const
Returns a const view of the travels of the path, in the committed state.
absl::Span< Interval > MutableCumuls(int path)
Returns a mutable view of the cumul mins of the path, in the current state.
int NumNodes(int path) const
Returns the number of nodes of the path, in the current state.
absl::Span< const int > Nodes(int path) const
Returns a const view of the nodes of the path, in the current state.
absl::Span< Interval > MutableTransits(int path)
Returns a mutable view of the transits of the path, in the current state.
Interval & MutableSpan(int path)
absl::Span< int64_t > MutableTravelSums(int path)
Returns a mutable view of the travel sums of the path in the current state.
absl::Span< int64_t > MutableTravels(int path)
Returns a mutable view of the travels of the path, in the current state.
std::vector< VehicleBreak > & MutableVehicleBreaks(int path)
absl::Span< const int > CommittedNodes(int path) const
Returns a const view of the nodes of the path, in the committed state.
absl::Span< const int64_t > TravelSums(int path) const
Returns a const view of the travel sums of the path, in the current state.
const RoutingDimension * dimension() const
virtual int64_t Min() const =0
virtual int64_t Max() const =0
IntVarLocalSearchFilter(const std::vector< IntVar * > &vars)
bool FindIndex(IntVar *const var, int64_t *index) const
int64_t Value(int index) const
bool IsVarSynced(int index) const
IntVar * Var() override
Creates a variable from the expression.
LightVehicleBreaksChecker(PathState *path_state, std::vector< PathData > path_data)
const RoutingDimension * dimension() const
bool SetMin(int64_t new_min) const
bool SetMax(int64_t new_max) const
PathEnergyCostChecker(const PathState *path_state, std::vector< int64_t > force_start_min, std::vector< int64_t > force_end_min, std::vector< int > force_class, std::vector< const std::function< int64_t(int64_t)> * > force_per_class, std::vector< int > distance_class, std::vector< const std::function< int64_t(int64_t, int64_t)> * > distance_per_class, std::vector< EnergyCost > path_energy_cost, std::vector< bool > path_has_cost_when_empty)
A ChainRange is a range of Chains, committed or not.
void ChangeLoops(absl::Span< const int > new_loops)
Describes the nodes that are newly loops in this change.
const std::vector< int > & ChangedLoops() const
Returns the set of loops that were added by the change.
void Reset()
Sets all paths to start->end, all other nodes to kUnassigned.
const std::vector< int > & ChangedPaths() const
void Commit()
Set the current state G1 as committed. See class comment for details.
ChainRange Chains(int path) const
Returns the current range of chains of path.
static constexpr int kUnassigned
State-dependent accessors.
void Revert()
Erase incremental changes. See class comment for details.
NodeRange Nodes(int path) const
Returns the current range of nodes of path.
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
static constexpr int kLoop
void ChangePath(int path, absl::Span< const ChainBounds > chains)
State modifiers.
int Start(int path) const
Returns the start of a path.
absl::Span< int64_t > MutablePostVisits(int path)
Returns a mutable view of the post-visits of the path in the current state.
void ChangePathSize(int path, int new_num_nodes)
Turns new nodes into a new path, allocating pre-post visit values for it.
absl::Span< int64_t > MutablePreVisits(int path)
Returns a mutable view of the pre-visits of the path, in the current state.
const std::vector< T > & array() const
Returns the concatenated sequence of all elements in all arrays.
T RangeMinimum(int begin, int end) const
int TableSize() const
Returns the number of elements in sparse tables, excluding new elements.
For the time being, Solver is neither MT_SAFE nor MT_HOT.
void MakeTreeFromNewElements()
void PushBack(int64_t height, int64_t weight)
Adds an element at index this->Size().
int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index, int end_index) const
int TreeSize() const
Returns the total number of elements in trees.
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 MapUtilMappedT< Collection > & FindWithDefault(const Collection &collection, const KeyType &key, const MapUtilMappedT< Collection > &value)
For infeasible and unbounded see Not checked if options check_solutions_if_inf_or_unbounded and the If options first_solution_only is false
problem is infeasible or unbounded (default).
std::function< int64_t(const Model &)> Value(IntegerVariable v)
This checks that the variable is fixed.
In SWIG mode, we don't want anything besides these top-level includes.
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
int64_t CapAdd(int64_t x, int64_t y)
bool FillDimensionValuesFromRoutingDimension(int path, int64_t capacity, int64_t span_upper_bound, absl::Span< const DimensionValues::Interval > cumul_of_node, absl::Span< const DimensionValues::Interval > slack_of_node, absl::AnyInvocable< int64_t(int64_t, int64_t) const > evaluator, DimensionValues &dimension_values)
IntVarLocalSearchFilter * MakeRouteConstraintFilter(const RoutingModel &routing_model)
Returns a filter tracking route constraints.
void CapAddTo(int64_t x, int64_t *y)
LocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const PathState *path_state, const std::vector< PickupDeliveryPair > &pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
int64_t ComputeBestVehicleToResourceAssignment(absl::Span< const int > vehicles, const util_intops::StrongVector< RoutingModel::ResourceClassIndex, std::vector< int > > &resource_indices_per_class, const util_intops::StrongVector< RoutingModel::ResourceClassIndex, absl::flat_hash_set< int > > &ignored_resources_per_class, std::function< const std::vector< int64_t > *(int)> vehicle_to_resource_class_assignment_costs, std::vector< int > *resource_indices)
DimensionSchedulingStatus
IntVarLocalSearchFilter * MakeNodeDisjunctionFilter(const RoutingModel &routing_model, bool filter_cost)
Returns a filter ensuring that node disjunction constraints are enforced.
int64_t CapSub(int64_t x, int64_t y)
LocalSearchFilter * MakePathEnergyCostFilter(Solver *solver, std::unique_ptr< PathEnergyCostChecker > checker, absl::string_view dimension_name)
LinearExpr operator-(LinearExpr lhs, const LinearExpr &rhs)
void FillPrePostVisitValues(int path, const DimensionValues &dimension_values, absl::AnyInvocable< int64_t(int64_t, int64_t) const > pre_travel_evaluator, absl::AnyInvocable< int64_t(int64_t, int64_t) const > post_travel_evaluator, PrePostVisitValues &visit_values)
void CapSubFrom(int64_t amount, int64_t *target)
Updates *target with CapSub(*target, amount).
IntVarLocalSearchFilter * MakeCumulBoundsPropagatorFilter(const RoutingDimension &dimension)
Returns a filter handling dimension cumul bounds.
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
ClosedInterval::Iterator end(ClosedInterval interval)
LocalSearchFilter * MakeLightVehicleBreaksFilter(Solver *solver, std::unique_ptr< LightVehicleBreaksChecker > checker, absl::string_view dimension_name)
IntVarLocalSearchFilter * MakePathCumulFilter(const RoutingDimension &dimension, bool propagate_own_objective_value, bool filter_objective_cost, bool may_use_optimizers)
Returns a filter handling dimension costs and constraints.
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
bool ComputeVehicleToResourceClassAssignmentCosts(int v, double solve_duration_ratio, const RoutingModel::ResourceGroup &resource_group, const util_intops::StrongVector< RoutingModel::ResourceClassIndex, absl::flat_hash_set< int > > &ignored_resources_per_class, 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)
LinearExpr operator+(LinearExpr lhs, const LinearExpr &rhs)
int64_t CapProd(int64_t x, int64_t y)
LocalSearchFilter * MakeVehicleVarFilter(const RoutingModel &routing_model, const PathState *path_state)
Returns a filter checking that vehicle variable domains are respected.
static const int kUnassigned
--— Routing model --—
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, bool use_chain_cumul_filter, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
LocalSearchFilter * MakeResourceAssignmentFilter(LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, bool propagate_own_objective_value, bool filter_objective_cost)
bool PropagateLightweightVehicleBreaks(int path, DimensionValues &dimension_values, absl::Span< const std::pair< int64_t, int64_t > > interbreaks)
int MostSignificantBitPosition32(uint32_t n)
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
IntVarLocalSearchFilter * MakeGlobalLPCumulFilter(GlobalDimensionCumulOptimizer *lp_optimizer, GlobalDimensionCumulOptimizer *mp_optimizer, bool filter_objective_cost)
Returns a filter checking global linear constraints and costs.
IntVarLocalSearchFilter * MakeActiveNodeGroupFilter(const RoutingModel &routing_model)
int64_t CapOpp(int64_t v)
Note(user): -kint64min != kint64max, but kint64max == ~kint64min.
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, absl::string_view dimension_name)
trees with all degrees equal to
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.")
Implementation of local search filters for routing models.
std::vector< VehicleBreak > vehicle_breaks
LocalSearchState::Variable start_cumul
LocalSearchState::Variable end_cumul
std::vector< InterbreakLimit > interbreak_limits
LocalSearchState::Variable total_transit
LocalSearchState::Variable span
static const int64_t kint64max
static const int64_t kint64min