Google OR-Tools: ortools/constraint_solver/routing_filters.cc Source File
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/functional/any_invocable.h"
40#include "absl/strings/str_cat.h"
41#include "absl/strings/string_view.h"
42#include "absl/types/span.h"
59ABSL_FLAG(bool, routing_strong_debug_checks, false,
60 "Run stronger checks in debug; these stronger tests might change "
61 "the complexity of the code in particular.");
68class RouteConstraintFilter : public BasePathFilter {
70 explicit RouteConstraintFilter(const RoutingModel& routing_model)
71 : BasePathFilter(routing_model.Nexts(),
72 routing_model.Size() + routing_model.vehicles(),
73 routing_model.GetPathsMetadata()),
74 routing_model_(routing_model),
77 current_vehicle_costs_(routing_model.vehicles(), 0) {
78 start_to_vehicle_.resize(Size(), -1);
79 vehicle_to_start_.resize(routing_model.vehicles());
80 for (int v = 0; v < routing_model.vehicles(); v++) {
81 const int64_t start = routing_model.Start(v);
82 start_to_vehicle_[start] = v;
83 vehicle_to_start_[v] = start;
86 ~RouteConstraintFilter() override = default;
87 std::string DebugString() const override { return "RouteConstraintFilter"; }
88 int64_t GetSynchronizedObjectiveValue() const override {
89 return current_vehicle_cost_;
91 int64_t GetAcceptedObjectiveValue() const override {
92 return lns_detected() ? 0 : delta_vehicle_cost_;
96 void OnSynchronizePathFromStart(int64_t start) override {
101 node = Value(node);
104 std::optional<int64_t> route_cost = routing_model_.GetRouteCost(route_);
105 DCHECK(route_cost.has_value());
106 current_vehicle_costs_[start_to_vehicle_[start]] = route_cost.value();
108 void OnAfterSynchronizePaths() override {
109 current_vehicle_cost_ = 0;
110 for (int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
111 const int64_t start = vehicle_to_start_[vehicle];
112 DCHECK_EQ(vehicle, start_to_vehicle_[start]);
113 if (!IsVarSynced(start)) {
116 CapAddTo(current_vehicle_costs_[vehicle], ¤t_vehicle_cost_);
119 bool InitializeAcceptPath() override {
120 delta_vehicle_cost_ = current_vehicle_cost_;
123 bool AcceptPath(int64_t path_start, int64_t ,
126 CapSub(delta_vehicle_cost_,
127 current_vehicle_costs_[start_to_vehicle_[path_start]]);
129 int64_t node = path_start;
135 std::optional<int64_t> route_cost = routing_model_.GetRouteCost(route_);
136 if (!route_cost.has_value()) {
139 CapAddTo(route_cost.value(), &delta_vehicle_cost_);
142 bool FinalizeAcceptPath(int64_t ,
143 int64_t objective_max) override {
144 return delta_vehicle_cost_ <= objective_max;
147 const RoutingModel& routing_model_;
148 int64_t current_vehicle_cost_;
149 int64_t delta_vehicle_cost_;
150 std::vector<int64_t> current_vehicle_costs_;
151 std::vector<int64_t> vehicle_to_start_;
152 std::vector<int> start_to_vehicle_;
153 std::vector<int64_t> route_;
168class MaxActiveVehiclesFilter : public IntVarLocalSearchFilter {
170 explicit MaxActiveVehiclesFilter(const RoutingModel& routing_model)
171 : IntVarLocalSearchFilter(routing_model.Nexts()),
172 routing_model_(routing_model),
173 is_active_(routing_model.vehicles(), false),
175 ~MaxActiveVehiclesFilter() override = default;
176 std::string DebugString() const override { return "MaxActiveVehiclesFilter"; }
177 bool Accept(const Assignment* delta, const Assignment* ,
178 int64_t , int64_t ) override {
181 int current_active_vehicles = active_vehicles_;
182 for (const IntVarElement& new_element : container.elements()) {
183 IntVar* const var = new_element.Var();
185 if (FindIndex(var, &index) && routing_model_.IsStart(index)) {
186 if (new_element.Min() != new_element.Max()) {
190 const int vehicle = routing_model_.VehicleIndex(index);
192 (new_element.Min() != routing_model_.End(vehicle));
193 if (is_active && !is_active_[vehicle]) {
194 ++current_active_vehicles;
195 } else if (!is_active && is_active_[vehicle]) {
196 --current_active_vehicles;
200 return current_active_vehicles <=
201 routing_model_.GetMaximumNumberOfActiveVehicles();
205 void OnSynchronize(const Assignment* ) override {
207 for (int i = 0; i < routing_model_.vehicles(); ++i) {
208 const int index = routing_model_.Start(i);
209 if (IsVarSynced(index) && Value(index) != routing_model_.End(i)) {
210 is_active_[i] = true;
213 is_active_[i] = false;
218 const RoutingModel& routing_model_;
219 std::vector<bool> is_active_;
232class SameActivityGroupManager {
234 explicit SameActivityGroupManager(const RoutingModel& routing_model)
235 : routing_model_(routing_model) {}
236 int NumberOfGroups() const {
237 return routing_model_.GetSameActivityGroupsCount();
239 absl::Span<const int> GetGroupsFromNode(int node) const {
240 return absl::MakeConstSpan(routing_model_.GetSameActivityGroups())
243 const std::vector<int>& GetGroupNodes(int group) const {
244 return routing_model_.GetSameActivityIndicesOfGroup(group);
247 bool CheckGroup(int group, int active, int unknown,
248 const CommittableArray<bool>& ,
249 const CommittableArray<bool>& ) const {
250 const int group_size = GetGroupNodes(group).size();
253 if (active == 0) return true;
254 if (active <= group_size && group_size <= active + unknown) {
261 const RoutingModel& routing_model_;
264class OrderedActivityGroupManager {
266 explicit OrderedActivityGroupManager(const RoutingModel& routing_model)
267 : routing_model_(routing_model),
268 groups_(routing_model.GetOrderedActivityGroups()),
269 group_bounds_(routing_model.GetOrderedActivityGroups().size(), {0, 0}),
270 disjunction_is_active_(routing_model.GetNumberOfDisjunctions(), false),
271 disjunction_is_inactive_(routing_model.GetNumberOfDisjunctions(),
273 group_nodes_.resize(groups_.size());
274 node_groups_.resize(routing_model.Size());
275 disjunction_groups_.resize(routing_model.GetNumberOfDisjunctions());
276 for (int group = 0; group < groups_.size(); ++group) {
277 absl::flat_hash_map<RoutingModel::DisjunctionIndex, std::vector<int>>
279 for (int rank = 0; rank < groups_[group].size(); ++rank) {
280 disjunction_to_ranks[groups_[group][rank]].push_back(rank);
284 routing_model.GetDisjunctionNodeIndices(disjunction_index)) {
285 node_groups_[node].push_back(group);
286 group_nodes_[group].push_back(node);
288 disjunction_groups_[disjunction_index].push_back({
290 .sorted_ranks = disjunction_to_ranks[disjunction_index],
293 group_bounds_.Set(group, std::make_pair(0, groups_[group].size() - 1));
297 int NumberOfGroups() const { return groups_.size(); }
298 absl::Span<const int> GetGroupsFromNode(int node) const {
299 return node_groups_[node];
301 const std::vector<int>& GetGroupNodes(int group) const {
302 return group_nodes_[group];
306 disjunction_is_active_.Revert();
307 disjunction_is_inactive_.Revert();
308 touched_disjunctions_.clear();
310 bool CheckGroup(int group, int active, int unknown,
311 CommittableArray<bool>& node_is_active,
312 CommittableArray<bool>& node_is_unknown) {
313 if (active == 0) return true;
314 auto& [min_rank, max_rank] = group_bounds_.GetMutable(group);
315 for (int rank = min_rank; rank <= max_rank; ++rank) {
318 if (IsInactive(disjunction_index, node_is_active, node_is_unknown)) {
319 disjunction_is_inactive_.Set(disjunction_index.value(), true);
320 touched_disjunctions_.push_back(disjunction_index);
324 for (int rank = max_rank; rank >= min_rank; --rank) {
327 if (IsActive(disjunction_index, node_is_active, node_is_unknown)) {
328 disjunction_is_active_.Set(disjunction_index.value(), true);
329 touched_disjunctions_.push_back(disjunction_index);
333 while (!touched_disjunctions_.empty()) {
335 touched_disjunctions_.back();
336 touched_disjunctions_.pop_back();
337 if (!Propagate(disjunction_index, node_is_active, node_is_unknown)) {
344 CommittableArray<bool>& node_is_active,
345 CommittableArray<bool>& node_is_unknown) {
346 for (const auto& [group_index, ranks] :
347 disjunction_groups_[disjunction_index]) {
348 const std::vector<RoutingModel::DisjunctionIndex>& group =
350 auto& [min_rank, max_rank] = group_bounds_.GetMutable(group_index);
351 if (max_rank < min_rank) continue;
352 if (IsActive(disjunction_index, node_is_active, node_is_unknown)) {
355 int i = ranks.size() - 1;
356 while (i >= 0 && ranks[i] > max_rank) --i;
357 if (i < 0 || ranks[i] < min_rank) continue;
359 while (rank != ranks[i]) {
361 if (IsInactive(current, node_is_active, node_is_unknown)) {
364 if (!IsActive(current, node_is_active, node_is_unknown)) {
365 disjunction_is_active_.Set(current.value(), true);
366 touched_disjunctions_.push_back(current);
374 int i = 0;
375 while (i < ranks.size() && ranks[i] < min_rank) ++i;
376 if (i >= ranks.size() || ranks[i] > max_rank) continue;
378 while (rank != ranks[i]) {
380 if (IsActive(current, node_is_active, node_is_unknown)) {
383 if (!IsInactive(current, node_is_active, node_is_unknown)) {
384 disjunction_is_inactive_.Set(current.value(), true);
385 touched_disjunctions_.push_back(current);
397 const CommittableArray<bool>& node_is_active,
398 const CommittableArray<bool>& node_is_unknown) const {
399 if (disjunction_is_inactive_.Get(disjunction_index.value())) return true;
403 routing_model_.GetDisjunctionNodeIndices(disjunction_index)) {
404 if (node_is_unknown.Get(node)) {
406 } else if (node_is_active.Get(node)) {
411 routing_model_.GetDisjunctionMaxCardinality(disjunction_index) -
415 const CommittableArray<bool>& node_is_active,
416 const CommittableArray<bool>& node_is_unknown) const {
417 if (disjunction_is_active_.Get(disjunction_index.value())) return true;
420 routing_model_.GetDisjunctionNodeIndices(disjunction_index)) {
421 if (!node_is_unknown.Get(node) && node_is_active.Get(node)) {
426 routing_model_.GetDisjunctionMaxCardinality(disjunction_index);
429 const RoutingModel& routing_model_;
430 const std::vector<std::vector<RoutingModel::DisjunctionIndex>>& groups_;
431 std::vector<std::vector<int>> group_nodes_;
432 std::vector<std::vector<int>> node_groups_;
433 struct DisjunctionGroupInfo {
435 std::vector<int> sorted_ranks;
438 std::vector<DisjunctionGroupInfo>>
440 CommittableArray<std::pair<int, int>> group_bounds_;
441 std::vector<RoutingModel::DisjunctionIndex> touched_disjunctions_;
442 CommittableArray<bool> disjunction_is_active_;
443 CommittableArray<bool> disjunction_is_inactive_;
446template <typename GroupAccessor>
449 explicit ActiveNodeGroupFilter(const RoutingModel& routing_model)
450 : IntVarLocalSearchFilter(routing_model.Nexts()),
451 group_accessor_(routing_model),
452 active_count_per_group_(group_accessor_.NumberOfGroups(),
453 {.active = 0, .unknown = 0}),
454 node_is_active_(routing_model.Nexts().size(), false),
455 node_is_unknown_(routing_model.Nexts().size(), false) {}
457 bool Accept(const Assignment* delta, const Assignment* ,
458 int64_t , int64_t ) override {
459 active_count_per_group_.Revert();
461 node_is_unknown_.Revert();
465 for (const IntVarElement& new_element : container.elements()) {
466 IntVar* const var = new_element.Var();
468 if (!FindIndex(var, &index)) continue;
469 for (const int group : group_accessor_.GetGroupsFromNode(index)) {
470 ActivityCounts counts = active_count_per_group_.Get(group);
472 if (node_is_unknown_.Get(index)) --counts.unknown;
473 if (node_is_active_.Get(index)) --counts.active;
474 if (new_element.Min() != new_element.Max()) {
476 } else if (new_element.Min() != index) {
479 active_count_per_group_.Set(group, counts);
483 for (const IntVarElement& new_element : container.elements()) {
484 IntVar* const var = new_element.Var();
486 if (!FindIndex(var, &index)) continue;
487 node_is_unknown_.Set(index, new_element.Min() != new_element.Max());
488 node_is_active_.Set(index, new_element.Min() == new_element.Max() &&
489 new_element.Min() != index);
491 for (const int group : active_count_per_group_.ChangedIndices()) {
492 const ActivityCounts counts = active_count_per_group_.Get(group);
493 if (!group_accessor_.CheckGroup(group, counts.active, counts.unknown,
494 node_is_active_, node_is_unknown_)) {
500 std::string DebugString() const override { return "ActiveNodeGroupFilter"; }
503 void OnSynchronize(const Assignment* ) override {
504 const int num_groups = group_accessor_.NumberOfGroups();
505 for (int group = 0; group < num_groups; ++group) {
506 ActivityCounts counts = {.active = 0, .unknown = 0};
507 for (int node : group_accessor_.GetGroupNodes(group)) {
509 const bool is_active = (Value(node) != node);
510 node_is_active_.Set(node, is_active);
511 node_is_unknown_.Set(node, false);
512 counts.active += is_active ? 1 : 0;
515 node_is_unknown_.Set(node, true);
516 node_is_active_.Set(node, false);
519 active_count_per_group_.Set(group, counts);
521 active_count_per_group_.Commit();
523 node_is_unknown_.Commit();
526 GroupAccessor group_accessor_;
531 CommittableArray<ActivityCounts> active_count_per_group_;
534 CommittableArray<bool> node_is_active_;
537 CommittableArray<bool> node_is_unknown_;
557class NodeDisjunctionFilter : public IntVarLocalSearchFilter {
559 explicit NodeDisjunctionFilter(const RoutingModel& routing_model,
561 : IntVarLocalSearchFilter(routing_model.Nexts()),
562 routing_model_(routing_model),
563 count_per_disjunction_(routing_model.GetNumberOfDisjunctions(),
564 {.active = 0, .inactive = 0}),
565 synchronized_objective_value_(std::numeric_limits<int64_t>::min()),
566 accepted_objective_value_(std::numeric_limits<int64_t>::min()),
567 filter_cost_(filter_cost),
568 has_mandatory_disjunctions_(routing_model.HasMandatoryDisjunctions()) {}
572 bool Accept(const Assignment* delta, const Assignment* ,
573 int64_t , int64_t objective_max) override {
574 count_per_disjunction_.Revert();
575 bool lns_detected = false;
577 for (const IntVarElement& element : delta->IntVarContainer().elements()) {
579 if (!FindIndex(element.Var(), &node)) continue;
580 lns_detected |= element.Min() != element.Max();
582 const bool is_var_synced = IsVarSynced(node);
583 const bool was_active = is_var_synced && Value(node) != node;
584 const bool is_active = node < element.Min() || element.Max() < node;
585 ActivityCount contribution_delta = {.active = 0, .inactive = 0};
587 contribution_delta.active -= was_active;
588 contribution_delta.inactive -= !was_active;
590 contribution_delta.active += is_active;
591 contribution_delta.inactive += !is_active;
593 if (contribution_delta.active == 0 && contribution_delta.inactive == 0) {
597 for (const Disjunction disjunction :
598 routing_model_.GetDisjunctionIndices(node)) {
600 count_per_disjunction_.Get(disjunction.value());
601 new_count.active += contribution_delta.active;
602 new_count.inactive += contribution_delta.inactive;
603 count_per_disjunction_.Set(disjunction.value(), new_count);
607 for (const int index : count_per_disjunction_.ChangedIndices()) {
608 if (count_per_disjunction_.Get(index).active >
609 routing_model_.GetDisjunctionMaxCardinality(Disjunction(index))) {
613 if (lns_detected || (!filter_cost_ && !has_mandatory_disjunctions_)) {
614 accepted_objective_value_ = 0;
618 accepted_objective_value_ = synchronized_objective_value_;
619 for (const int index : count_per_disjunction_.ChangedIndices()) {
622 count_per_disjunction_.GetCommitted(index).inactive;
623 const int new_inactives = count_per_disjunction_.Get(index).inactive;
624 if (old_inactives == new_inactives) continue;
626 const Disjunction disjunction(index);
627 const int64_t penalty = routing_model_.GetDisjunctionPenalty(disjunction);
628 if (penalty == 0) continue;
632 routing_model_.GetDisjunctionNodeIndices(disjunction).size() -
633 routing_model_.GetDisjunctionMaxCardinality(disjunction);
634 int new_violation = std::max(0, new_inactives - max_inactives);
635 int old_violation = std::max(0, old_inactives - max_inactives);
637 if (penalty < 0 && new_violation > 0) return false;
638 if (routing_model_.GetDisjunctionPenaltyCostBehavior(disjunction) ==
640 new_violation = std::min(1, new_violation);
641 old_violation = std::min(1, old_violation);
643 CapAddTo(CapProd(penalty, (new_violation - old_violation)),
644 &accepted_objective_value_);
647 return accepted_objective_value_ <= objective_max;
649 std::string DebugString() const override { return "NodeDisjunctionFilter"; }
650 int64_t GetSynchronizedObjectiveValue() const override {
651 return synchronized_objective_value_;
653 int64_t GetAcceptedObjectiveValue() const override {
654 return accepted_objective_value_;
658 void OnSynchronize(const Assignment* ) override {
659 synchronized_objective_value_ = 0;
660 count_per_disjunction_.Revert();
661 const int num_disjunctions = routing_model_.GetNumberOfDisjunctions();
662 for (Disjunction disjunction(0); disjunction < num_disjunctions;
665 ActivityCount count = {.active = 0, .inactive = 0};
666 const auto& nodes = routing_model_.GetDisjunctionNodeIndices(disjunction);
667 for (const int64_t node : nodes) {
668 if (!IsVarSynced(node)) continue;
669 const int is_active = Value(node) != node;
670 count.active += is_active;
671 count.inactive += !is_active;
673 count_per_disjunction_.Set(disjunction.value(), count);
675 if (!filter_cost_) continue;
676 const int64_t penalty = routing_model_.GetDisjunctionPenalty(disjunction);
678 routing_model_.GetDisjunctionMaxCardinality(disjunction);
679 int violation = count.inactive - (nodes.size() - max_actives);
680 if (violation > 0 && penalty > 0) {
681 if (routing_model_.GetDisjunctionPenaltyCostBehavior(disjunction) ==
683 violation = std::min(1, violation);
685 CapAddTo(CapProd(penalty, violation), &synchronized_objective_value_);
688 count_per_disjunction_.Commit();
689 accepted_objective_value_ = synchronized_objective_value_;
692 const RoutingModel& routing_model_;
697 CommittableArray<ActivityCount> count_per_disjunction_;
698 int64_t synchronized_objective_value_;
699 int64_t accepted_objective_value_;
701 const bool has_mandatory_disjunctions_;
706 const RoutingModel& routing_model, bool filter_cost) {
717 paths_metadata_(paths_metadata),
718 node_path_starts_(next_domain_size, kUnassigned),
719 new_synchronized_unperformed_nodes_(nexts.size()),
728 int64_t objective_min, int64_t objective_max) {
730 for (const int touched : delta_touched_) {
735 delta_touched_.reserve(container.Size());
742 for (int64_t touched_path : touched_paths_.PositionsSetAtLeastOnce()) {
745 touched_paths_.ResetAllToFalse();
747 const auto update_touched_path_chain_start_end = [this](int64_t index) {
748 const int64_t start = node_path_starts_[index];
750 touched_paths_.Set(start);
752 int64_t& chain_start = touched_path_chain_start_ends_[start].first;
753 if (chain_start == kUnassigned || paths_metadata_.IsStart(index) ||
754 ranks_[index] < ranks_[chain_start]) {
758 int64_t& chain_end = touched_path_chain_start_ends_[start].second;
759 if (chain_end == kUnassigned || paths_metadata_.IsEnd(index) ||
760 ranks_[index] > ranks_[chain_end]) {
766 IntVar* const var = new_element.Var();
769 if (!new_element.Bound()) {
774 new_nexts_[index] = new_element.Value();
775 delta_touched_.push_back(index);
776 update_touched_path_chain_start_end(index);
777 update_touched_path_chain_start_end(new_nexts_[index]);
781 if (!InitializeAcceptPath()) return false;
782 for (const int64_t touched_start : touched_paths_.PositionsSetAtLeastOnce()) {
783 const std::pair<int64_t, int64_t> start_end =
784 touched_path_chain_start_ends_[touched_start];
785 if (!AcceptPath(touched_start, start_end.first, start_end.second)) {
791 return FinalizeAcceptPath(objective_min, objective_max);
794void BasePathFilter::ComputePathStarts(std::vector<int64_t>* path_starts,
795 std::vector<int>* index_to_path) {
797 const int nexts_size = Size();
798 index_to_path->assign(nexts_size, kUnassigned);
800 for (int i = 0; i < nexts_size; ++i) {
804 const int next = Value(i);
810 for (int i = 0; i < nexts_size; ++i) {
812 (*index_to_path)[i] = path_starts->size();
813 path_starts->push_back(i);
818void BasePathFilter::SynchronizeFullAssignment() {
819 for (int64_t index = 0; index < Size(); index++) {
821 node_path_starts_[index] != kUnassigned) {
823 new_synchronized_unperformed_nodes_.Set(index);
827 node_path_starts_.assign(node_path_starts_.size(), kUnassigned);
829 const int nexts_size = Size();
830 for (int path = 0; path < NumPaths(); ++path) {
831 const int64_t start = Start(path);
832 node_path_starts_[start] = start;
834 int64_t next = Value(start);
835 while (next < nexts_size) {
837 node_path_starts_[node] = start;
839 next = Value(node);
841 node_path_starts_[next] = start;
843 node_path_starts_[End(path)] = start;
845 for (const int touched : delta_touched_) {
849 OnBeforeSynchronizePaths(true);
851 OnAfterSynchronizePaths();
855 new_synchronized_unperformed_nodes_.ResetAllToFalse();
856 if (delta == nullptr || delta->Empty() ||
857 absl::c_all_of(ranks_, [](int rank) { return rank == kUnassigned; })) {
858 SynchronizeFullAssignment();
862 touched_paths_.ResetAllToFalse();
865 if (FindIndex(new_element.Var(), &index)) {
866 const int64_t start = node_path_starts_[index];
868 touched_paths_.Set(start);
869 if (Value(index) == index) {
871 DCHECK_LT(index, new_nexts_.size());
872 new_synchronized_unperformed_nodes_.Set(index);
878 for (const int touched : delta_touched_) {
882 OnBeforeSynchronizePaths(false);
883 for (const int64_t touched_start : touched_paths_.PositionsSetAtLeastOnce()) {
884 int64_t node = touched_start;
885 while (node < Size()) {
886 node_path_starts_[node] = touched_start;
887 node = Value(node);
889 node_path_starts_[node] = touched_start;
890 UpdatePathRanksFromStart(touched_start);
891 OnSynchronizePathFromStart(touched_start);
896void BasePathFilter::UpdateAllRanks() {
897 ranks_.assign(ranks_.size(), kUnassigned);
898 for (int r = 0; r < NumPaths(); ++r) {
899 const int64_t start = Start(r);
901 UpdatePathRanksFromStart(start);
902 OnSynchronizePathFromStart(start);
906void BasePathFilter::UpdatePathRanksFromStart(int start) {
909 while (node < Size()) {
912 node = Value(node);
919class VehicleAmortizedCostFilter : public BasePathFilter {
921 explicit VehicleAmortizedCostFilter(const RoutingModel& routing_model);
922 ~VehicleAmortizedCostFilter() override = default;
923 std::string DebugString() const override {
924 return "VehicleAmortizedCostFilter";
926 int64_t GetSynchronizedObjectiveValue() const override {
927 return current_vehicle_cost_;
929 int64_t GetAcceptedObjectiveValue() const override {
930 return lns_detected() ? 0 : delta_vehicle_cost_;
934 void OnSynchronizePathFromStart(int64_t start) override;
935 void OnAfterSynchronizePaths() override;
936 bool InitializeAcceptPath() override;
937 bool AcceptPath(int64_t path_start, int64_t chain_start,
938 int64_t chain_end) override;
939 bool FinalizeAcceptPath(int64_t objective_min,
940 int64_t objective_max) override;
942 int64_t current_vehicle_cost_;
943 int64_t delta_vehicle_cost_;
944 std::vector<int> current_route_lengths_;
945 std::vector<int64_t> start_to_end_;
946 std::vector<int> start_to_vehicle_;
947 std::vector<int64_t> vehicle_to_start_;
948 const std::vector<int64_t>& linear_cost_factor_of_vehicle_;
949 const std::vector<int64_t>& quadratic_cost_factor_of_vehicle_;
952VehicleAmortizedCostFilter::VehicleAmortizedCostFilter(
955 routing_model.Size() + routing_model.vehicles(),
956 routing_model.GetPathsMetadata()),
959 current_route_lengths_(Size(), -1),
960 linear_cost_factor_of_vehicle_(
961 routing_model.GetAmortizedLinearCostFactorOfVehicles()),
962 quadratic_cost_factor_of_vehicle_(
963 routing_model.GetAmortizedQuadraticCostFactorOfVehicles()) {
964 start_to_end_.resize(Size(), -1);
965 start_to_vehicle_.resize(Size(), -1);
966 vehicle_to_start_.resize(routing_model.vehicles());
967 for (int v = 0; v < routing_model.vehicles(); v++) {
968 const int64_t start = routing_model.Start(v);
969 start_to_vehicle_[start] = v;
970 start_to_end_[start] = routing_model.End(v);
971 vehicle_to_start_[v] = start;
975void VehicleAmortizedCostFilter::OnSynchronizePathFromStart(int64_t start) {
976 const int64_t end = start_to_end_[start];
978 const int route_length = Rank(end) - 1;
979 CHECK_GE(route_length, 0);
980 current_route_lengths_[start] = route_length;
983void VehicleAmortizedCostFilter::OnAfterSynchronizePaths() {
984 current_vehicle_cost_ = 0;
985 for (int vehicle = 0; vehicle < vehicle_to_start_.size(); vehicle++) {
986 const int64_t start = vehicle_to_start_[vehicle];
987 DCHECK_EQ(vehicle, start_to_vehicle_[start]);
988 if (!IsVarSynced(start)) {
991 const int route_length = current_route_lengths_[start];
992 DCHECK_GE(route_length, 0);
999 const int64_t linear_cost_factor = linear_cost_factor_of_vehicle_[vehicle];
1000 const int64_t route_length_cost =
1001 CapProd(quadratic_cost_factor_of_vehicle_[vehicle],
1002 route_length * route_length);
1004 CapAddTo(CapSub(linear_cost_factor, route_length_cost),
1009bool VehicleAmortizedCostFilter::InitializeAcceptPath() {
1010 delta_vehicle_cost_ = current_vehicle_cost_;
1014bool VehicleAmortizedCostFilter::AcceptPath(int64_t path_start,
1018 const int previous_chain_nodes = Rank(chain_end) - 1 - Rank(chain_start);
1019 CHECK_GE(previous_chain_nodes, 0);
1021 int64_t node = GetNext(chain_start);
1022 while (node != chain_end) {
1027 const int previous_route_length = current_route_lengths_[path_start];
1028 CHECK_GE(previous_route_length, 0);
1029 const int new_route_length =
1030 previous_route_length - previous_chain_nodes + new_chain_nodes;
1032 const int vehicle = start_to_vehicle_[path_start];
1034 DCHECK_EQ(path_start, vehicle_to_start_[vehicle]);
1038 if (previous_route_length == 0) {
1040 CHECK_GT(new_route_length, 0);
1041 CapAddTo(linear_cost_factor_of_vehicle_[vehicle], &delta_vehicle_cost_);
1042 } else if (new_route_length == 0) {
1045 CapSub(delta_vehicle_cost_, linear_cost_factor_of_vehicle_[vehicle]);
1049 const int64_t quadratic_cost_factor =
1050 quadratic_cost_factor_of_vehicle_[vehicle];
1052 previous_route_length * previous_route_length),
1054 delta_vehicle_cost_ = CapSub(
1056 CapProd(quadratic_cost_factor, new_route_length * new_route_length));
1061bool VehicleAmortizedCostFilter::FinalizeAcceptPath(int64_t ,
1063 return delta_vehicle_cost_ <= objective_max;
1079class SameVehicleCostFilter : public BasePathFilter {
1081 explicit SameVehicleCostFilter(const RoutingModel& model)
1082 : BasePathFilter(model.Nexts(), model.Size() + model.vehicles(),
1083 model.GetPathsMetadata()),
1085 same_vehicle_costs_per_node_(model.Size()),
1086 nodes_per_vehicle_(model.GetNumberOfSoftSameVehicleConstraints()),
1087 new_nodes_per_vehicle_(model.GetNumberOfSoftSameVehicleConstraints()),
1088 current_vehicle_per_node_(model.Size()),
1090 for (int i = 0; i < model.GetNumberOfSoftSameVehicleConstraints(); ++i) {
1091 for (int node : model.GetSoftSameVehicleIndices(i)) {
1092 same_vehicle_costs_per_node_[node].push_back(i);
1095 start_to_vehicle_.resize(Size(), -1);
1096 for (int v = 0; v < model.vehicles(); v++) {
1097 const int64_t start = model.Start(v);
1098 start_to_vehicle_[start] = v;
1101 int64_t GetSynchronizedObjectiveValue() const override {
1104 int64_t GetAcceptedObjectiveValue() const override {
1105 return lns_detected() ? 0 : delta_cost_;
1107 std::string DebugString() const override { return "SameVehicleCostFilter"; }
1110 bool InitializeAcceptPath() override {
1111 delta_cost_ = current_cost_;
1112 for (int same_vehicle_cost_index : touched_) {
1113 new_nodes_per_vehicle_[same_vehicle_cost_index] =
1114 nodes_per_vehicle_[same_vehicle_cost_index];
1119 bool AcceptPath(int64_t path_start, int64_t chain_start,
1120 int64_t chain_end) override {
1121 const int64_t vehicle = start_to_vehicle_[path_start];
1123 if (chain_start == chain_end) return true;
1124 for (int64_t node = GetNext(chain_start); node != chain_end;
1126 if (vehicle == current_vehicle_per_node_[node]) continue;
1127 for (int same_vehicle_cost_index : same_vehicle_costs_per_node_[node]) {
1128 auto& new_nodes = new_nodes_per_vehicle_[same_vehicle_cost_index];
1130 new_nodes[current_vehicle_per_node_[node]]--;
1131 if (new_nodes[current_vehicle_per_node_[node]] == 0) {
1132 new_nodes.erase(current_vehicle_per_node_[node]);
1134 touched_.insert(same_vehicle_cost_index);
1139 bool FinalizeAcceptPath(int64_t ,
1140 int64_t objective_max) override {
1141 for (int same_vehicle_cost_index : touched_) {
1142 CapAddTo(CapSub(GetCost(same_vehicle_cost_index, new_nodes_per_vehicle_),
1143 GetCost(same_vehicle_cost_index, nodes_per_vehicle_)),
1146 return delta_cost_ <= objective_max;
1149 void OnAfterSynchronizePaths() override {
1152 for (int same_vehicle_cost_index = 0;
1153 same_vehicle_cost_index < nodes_per_vehicle_.size();
1154 ++same_vehicle_cost_index) {
1155 nodes_per_vehicle_[same_vehicle_cost_index].clear();
1156 touched_.insert(same_vehicle_cost_index);
1158 current_vehicle_per_node_.assign(model_.Size(), -1);
1159 for (int v = 0; v < model_.vehicles(); ++v) {
1160 int64_t node = GetNext(model_.Start(v));
1161 DCHECK(model_.IsEnd(node) || IsVarSynced(node));
1162 while (!model_.IsEnd(node)) {
1163 for (int same_vehicle_cost_index : same_vehicle_costs_per_node_[node]) {
1164 nodes_per_vehicle_[same_vehicle_cost_index][v]++;
1166 current_vehicle_per_node_[node] = v;
1170 for (int same_vehicle_cost_index = 0;
1171 same_vehicle_cost_index < nodes_per_vehicle_.size();
1172 ++same_vehicle_cost_index) {
1173 CapAddTo(GetCost(same_vehicle_cost_index, nodes_per_vehicle_),
1179 absl::Span<const absl::flat_hash_map<int, int>> nodes_per_vehicle) const {
1180 const int num_vehicles_used = nodes_per_vehicle[index].size();
1181 if (num_vehicles_used <= 1) return 0;
1185 const RoutingModel& model_;
1186 std::vector<int> start_to_vehicle_;
1187 std::vector<std::vector<int>> same_vehicle_costs_per_node_;
1188 std::vector<absl::flat_hash_map<int, int>> nodes_per_vehicle_;
1189 std::vector<absl::flat_hash_map<int, int>> new_nodes_per_vehicle_;
1190 absl::flat_hash_set<int> touched_;
1191 std::vector<int> current_vehicle_per_node_;
1206class TypeRegulationsFilter : public BasePathFilter {
1208 explicit TypeRegulationsFilter(const RoutingModel& model);
1209 ~TypeRegulationsFilter() override = default;
1210 std::string DebugString() const override { return "TypeRegulationsFilter"; }
1213 void OnSynchronizePathFromStart(int64_t start) override;
1214 bool AcceptPath(int64_t path_start, int64_t chain_start,
1215 int64_t chain_end) override;
1217 bool HardIncompatibilitiesRespected(int vehicle, int64_t chain_start,
1220 const RoutingModel& routing_model_;
1221 std::vector<int> start_to_vehicle_;
1224 std::vector<std::vector<int>> hard_incompatibility_type_counts_per_vehicle_;
1226 TypeIncompatibilityChecker temporal_incompatibility_checker_;
1227 TypeRequirementChecker requirement_checker_;
1230TypeRegulationsFilter::TypeRegulationsFilter(const RoutingModel& model)
1231 : BasePathFilter(model.Nexts(), model.Size() + model.vehicles(),
1232 model.GetPathsMetadata()),
1234 start_to_vehicle_(model.Size(), -1),
1235 temporal_incompatibility_checker_(model,
1236 false),
1237 requirement_checker_(model) {
1238 const int num_vehicles = model.vehicles();
1239 const bool has_hard_type_incompatibilities =
1240 model.HasHardTypeIncompatibilities();
1241 if (has_hard_type_incompatibilities) {
1242 hard_incompatibility_type_counts_per_vehicle_.resize(num_vehicles);
1244 const int num_visit_types = model.GetNumberOfVisitTypes();
1245 for (int vehicle = 0; vehicle < num_vehicles; vehicle++) {
1246 const int64_t start = model.Start(vehicle);
1247 start_to_vehicle_[start] = vehicle;
1248 if (has_hard_type_incompatibilities) {
1249 hard_incompatibility_type_counts_per_vehicle_[vehicle].resize(
1255void TypeRegulationsFilter::OnSynchronizePathFromStart(int64_t start) {
1256 if (!routing_model_.HasHardTypeIncompatibilities()) return;
1258 const int vehicle = start_to_vehicle_[start];
1260 std::vector<int>& type_counts =
1261 hard_incompatibility_type_counts_per_vehicle_[vehicle];
1262 std::fill(type_counts.begin(), type_counts.end(), 0);
1263 const int num_types = type_counts.size();
1267 DCHECK(IsVarSynced(node));
1268 const int type = routing_model_.GetVisitType(node);
1269 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
1270 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
1271 CHECK_LT(type, num_types);
1274 node = Value(node);
1278bool TypeRegulationsFilter::HardIncompatibilitiesRespected(int vehicle,
1281 if (!routing_model_.HasHardTypeIncompatibilities()) return true;
1283 const std::vector<int>& previous_type_counts =
1284 hard_incompatibility_type_counts_per_vehicle_[vehicle];
1286 absl::flat_hash_map< int, int> new_type_counts;
1287 absl::flat_hash_set<int> types_to_check;
1290 int64_t node = GetNext(chain_start);
1291 while (node != chain_end) {
1292 const int type = routing_model_.GetVisitType(node);
1293 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
1294 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
1295 DCHECK_LT(type, previous_type_counts.size());
1297 previous_type_counts[type]);
1300 types_to_check.insert(type);
1308 if (IsVarSynced(chain_start)) {
1309 node = Value(chain_start);
1310 while (node != chain_end) {
1311 const int type = routing_model_.GetVisitType(node);
1312 if (type >= 0 && routing_model_.GetVisitTypePolicy(node) !=
1313 RoutingModel::ADDED_TYPE_REMOVED_FROM_VEHICLE) {
1314 DCHECK_LT(type, previous_type_counts.size());
1316 previous_type_counts[type]);
1320 node = Value(node);
1325 for (int type : types_to_check) {
1326 for (int incompatible_type :
1327 routing_model_.GetHardTypeIncompatibilitiesOfType(type)) {
1329 previous_type_counts[incompatible_type]) > 0) {
1337bool TypeRegulationsFilter::AcceptPath(int64_t path_start, int64_t chain_start,
1339 const int vehicle = start_to_vehicle_[path_start];
1341 const auto next_accessor = [this](int64_t node) { return GetNext(node); };
1342 return HardIncompatibilitiesRespected(vehicle, chain_start, chain_end) &&
1343 temporal_incompatibility_checker_.CheckVehicle(vehicle,
1345 requirement_checker_.CheckVehicle(vehicle, next_accessor);
1363class ChainCumulFilter : public BasePathFilter {
1365 ChainCumulFilter(const RoutingModel& routing_model,
1366 const RoutingDimension& dimension);
1367 ~ChainCumulFilter() override = default;
1368 std::string DebugString() const override {
1369 return "ChainCumulFilter(" + name_ + ")";
1373 void OnSynchronizePathFromStart(int64_t start) override;
1374 bool AcceptPath(int64_t path_start, int64_t chain_start,
1375 int64_t chain_end) override;
1377 const std::vector<IntVar*> cumuls_;
1378 std::vector<int64_t> start_to_vehicle_;
1379 std::vector<int64_t> start_to_end_;
1380 std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1381 const std::vector<int64_t> vehicle_capacities_;
1382 std::vector<int64_t> current_path_cumul_mins_;
1383 std::vector<int64_t> current_max_of_path_end_cumul_mins_;
1384 std::vector<int64_t> old_nexts_;
1385 std::vector<int> old_vehicles_;
1386 std::vector<int64_t> current_transits_;
1390ChainCumulFilter::ChainCumulFilter(const RoutingModel& routing_model,
1391 const RoutingDimension& dimension)
1392 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(),
1393 routing_model.GetPathsMetadata()),
1394 cumuls_(dimension.cumuls()),
1395 evaluators_(routing_model.vehicles(), nullptr),
1396 vehicle_capacities_(dimension.vehicle_capacities()),
1397 current_path_cumul_mins_(dimension.cumuls().size(), 0),
1398 current_max_of_path_end_cumul_mins_(dimension.cumuls().size(), 0),
1399 old_nexts_(routing_model.Size(), kUnassigned),
1400 old_vehicles_(routing_model.Size(), kUnassigned),
1401 current_transits_(routing_model.Size(), 0),
1402 name_(dimension.name()) {
1403 start_to_vehicle_.resize(Size(), -1);
1404 start_to_end_.resize(Size(), -1);
1405 for (int i = 0; i < routing_model.vehicles(); ++i) {
1406 start_to_vehicle_[routing_model.Start(i)] = i;
1407 start_to_end_[routing_model.Start(i)] = routing_model.End(i);
1408 evaluators_[i] = &dimension.transit_evaluator(i);
1415void ChainCumulFilter::OnSynchronizePathFromStart(int64_t start) {
1416 const int vehicle = start_to_vehicle_[start];
1417 std::vector<int64_t> path_nodes;
1419 int64_t cumul = cumuls_[node]->Min();
1421 path_nodes.push_back(node);
1422 current_path_cumul_mins_[node] = cumul;
1423 const int64_t next = Value(node);
1424 if (next != old_nexts_[node] || vehicle != old_vehicles_[node]) {
1426 old_vehicles_[node] = vehicle;
1427 current_transits_[node] = (*evaluators_[vehicle])(node, next);
1429 CapAddTo(current_transits_[node], &cumul);
1430 cumul = std::max(cumuls_[next]->Min(), cumul);
1433 path_nodes.push_back(node);
1434 current_path_cumul_mins_[node] = cumul;
1435 int64_t max_cumuls = cumul;
1436 for (int i = path_nodes.size() - 1; i >= 0; --i) {
1437 const int64_t node = path_nodes[i];
1438 max_cumuls = std::max(max_cumuls, current_path_cumul_mins_[node]);
1439 current_max_of_path_end_cumul_mins_[node] = max_cumuls;
1444bool ChainCumulFilter::AcceptPath(int64_t path_start, int64_t chain_start,
1446 const int vehicle = start_to_vehicle_[path_start];
1447 const int64_t capacity = vehicle_capacities_[vehicle];
1448 int64_t node = chain_start;
1449 int64_t cumul = current_path_cumul_mins_[node];
1450 while (node != chain_end) {
1451 const int64_t next = GetNext(node);
1452 if (IsVarSynced(node) && next == Value(node) &&
1453 vehicle == old_vehicles_[node]) {
1454 CapAddTo(current_transits_[node], &cumul);
1456 CapAddTo((*evaluators_[vehicle])(node, next), &cumul);
1458 cumul = std::max(cumuls_[next]->Min(), cumul);
1459 if (cumul > capacity) return false;
1462 const int64_t end = start_to_end_[path_start];
1463 const int64_t end_cumul_delta =
1464 CapSub(current_path_cumul_mins_[end], current_path_cumul_mins_[node]);
1465 const int64_t after_chain_cumul_delta =
1466 CapSub(current_max_of_path_end_cumul_mins_[node],
1467 current_path_cumul_mins_[node]);
1468 return CapAdd(cumul, after_chain_cumul_delta) <= capacity &&
1469 CapAdd(cumul, end_cumul_delta) <= cumuls_[end]->Max();
1475 int path, int64_t capacity, int64_t span_upper_bound,
1476 absl::Span<const DimensionValues::Interval> cumul_of_node,
1477 absl::Span<const DimensionValues::Interval> slack_of_node,
1478 absl::AnyInvocable<int64_t(int64_t, int64_t) const> evaluator,
1482 const int num_nodes = dimension_values.NumNodes(path);
1483 absl::Span<const int> nodes = dimension_values.Nodes(path);
1484 absl::Span<Interval> cumuls = dimension_values.MutableCumuls(path);
1485 for (int r = 0; r < num_nodes; ++r) {
1486 const int node = nodes[r];
1487 Interval cumul = cumul_of_node[node];
1488 if (!cumul.DecreaseMax(capacity)) return false;
1495 absl::Span<int64_t> travels = dimension_values.MutableTravels(path);
1497 absl::Span<const int> cnodes = dimension_values.CommittedNodes(path);
1498 absl::Span<const int64_t> ctravels =
1506 const int i_limit = std::min(cnodes.size(), nodes.size());
1507 DCHECK(cnodes.empty() || cnodes[0] == nodes[0]);
1509 while (i < i_limit && cnodes[i] == nodes[i]) {
1510 travels[i - 1] = ctravels[i - 1];
1513 DCHECK(cnodes.empty() || cnodes.back() == nodes.back());
1515 const int delta = cnodes.size() - num_nodes;
1516 const int j_limit = i + std::max(0, -delta);
1517 while (j_limit <= j && nodes[j] == cnodes[j + delta]) {
1518 travels[j] = ctravels[j + delta];
1522 for (int r = i; r <= j; ++r) {
1523 const int64_t travel = evaluator(nodes[r - 1], nodes[r]);
1528 absl::Span<Interval> transits = dimension_values.MutableTransits(path);
1529 absl::Span<int64_t> travel_sums = dimension_values.MutableTravelSums(path);
1530 int64_t total_travel = 0;
1532 for (int r = 1; r < num_nodes; ++r) {
1533 const int64_t travel = travels[r - 1];
1534 CapAddTo(travel, &total_travel);
1535 travel_sums[r] = total_travel;
1536 Interval transit{.min = travel, .max = travel};
1537 transit.Add(slack_of_node[nodes[r - 1]]);
1538 transits[r - 1] = transit;
1540 if (travel_sums.back() > span_upper_bound) return false;
1541 dimension_values.MutableSpan(path) = {.min = travel_sums.back(),
1556 std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t) const>>
1558 std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t) const>>
1561 const int num_nodes = dimension_values.NumNodes(path);
1563 absl::Span<int64_t> pre_visits = visit_values.MutablePreVisits(path);
1564 absl::Span<int64_t> post_visits = visit_values.MutablePostVisits(path);
1565 absl::Span<const int> nodes = dimension_values.Nodes(path);
1566 if (pre_travel_evaluator.has_value()) {
1567 for (int i = 0; i < num_nodes - 1; ++i) {
1568 post_visits[i] = (*pre_travel_evaluator)(nodes[i], nodes[i + 1]);
1572 absl::c_fill(post_visits, 0);
1574 if (post_travel_evaluator.has_value()) {
1576 for (int i = 1; i < num_nodes; ++i) {
1577 pre_visits[i] = (*post_travel_evaluator)(nodes[i - 1], nodes[i]);
1586 absl::Span<const std::pair<int64_t, int64_t>> interbreaks) {
1589 const int64_t total_travel = dimension_values.TravelSums(path).back();
1593 int64_t lb_span_tw = total_travel;
1594 const absl::Span<Interval> cumuls = dimension_values.MutableCumuls(path);
1595 Interval& start = cumuls.front();
1596 Interval& end = cumuls.back();
1598 for (const VehicleBreak& br : dimension_values.MutableVehicleBreaks(path)) {
1601 if (br.is_performed.min == 0) continue;
1602 if (br.end.min <= start.max || end.min <= br.start.max) continue;
1604 CapAddTo(br.duration.min, &lb_span_tw);
1605 if (!start.DecreaseMax(br.start.max)) return false;
1606 if (!end.IncreaseMin(br.end.min)) return false;
1608 Interval& span = dimension_values.MutableSpan(path);
1609 if (!span.IncreaseMin(std::max(lb_span_tw, CapSub(end.min, start.max)))) {
1614 int64_t break_start_min = kint64max;
1615 int64_t break_end_max = kint64min;
1617 if (!start.IncreaseMin(CapSub(end.min, span.max))) return false;
1618 if (!end.DecreaseMax(CapAdd(start.max, span.max))) return false;
1620 int num_feasible_breaks = 0;
1621 for (const VehicleBreak& br : dimension_values.MutableVehicleBreaks(path)) {
1622 if (start.min <= br.start.max && br.end.min <= end.max) {
1623 break_start_min = std::min(break_start_min, br.start.min);
1624 break_end_max = std::max(break_end_max, br.end.max);
1632 for (const auto& [max_interbreak, min_break_duration] : interbreaks) {
1637 if (max_interbreak == 0) {
1638 if (total_travel > 0) return false;
1642 std::max<int64_t>(0, (total_travel - 1) / max_interbreak);
1643 if (span.min > max_interbreak) {
1644 min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
1646 if (min_num_breaks > num_feasible_breaks) return false;
1647 if (!span.IncreaseMin(CapAdd(
1648 total_travel, CapProd(min_num_breaks, min_break_duration)))) {
1651 if (min_num_breaks > 0) {
1652 if (!start.IncreaseMin(CapSub(break_start_min, max_interbreak))) {
1655 if (!end.DecreaseMax(CapAdd(break_end_max, max_interbreak))) {
1660 return start.DecreaseMax(CapSub(end.max, span.min)) &&
1666class PathCumulFilter : public BasePathFilter {
1668 PathCumulFilter(const RoutingModel& routing_model,
1669 const RoutingDimension& dimension,
1670 bool propagate_own_objective_value,
1671 bool filter_objective_cost, bool may_use_optimizers);
1672 ~PathCumulFilter() override = default;
1673 std::string DebugString() const override {
1674 return "PathCumulFilter(" + name_ + ")";
1676 int64_t GetSynchronizedObjectiveValue() const override {
1677 return propagate_own_objective_value_ ? synchronized_objective_value_ : 0;
1679 int64_t GetAcceptedObjectiveValue() const override {
1680 return lns_detected() || !propagate_own_objective_value_
1682 : accepted_objective_value_;
1684 bool UsesDimensionOptimizers() {
1685 if (!may_use_optimizers_) return false;
1686 for (int vehicle = 0; vehicle < routing_model_.vehicles(); ++vehicle) {
1687 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) return true;
1693 using Interval = DimensionValues::Interval;
1695 int64_t bound = -1;
1700 std::vector<Interval> ExtractInitialCumulIntervals();
1701 std::vector<Interval> ExtractInitialSlackIntervals();
1702 std::vector<std::vector<RoutingDimension::NodePrecedence>>
1703 ExtractNodeIndexToPrecedences() const;
1704 std::vector<SoftBound> ExtractCumulSoftUpperBounds() const;
1705 std::vector<SoftBound> ExtractCumulSoftLowerBounds() const;
1706 std::vector<const PiecewiseLinearFunction*> ExtractCumulPiecewiseLinearCosts()
1708 std::vector<const RoutingModel::TransitCallback2*> ExtractEvaluators() const;
1709 using VehicleBreak = DimensionValues::VehicleBreak;
1710 std::vector<std::vector<VehicleBreak>> ExtractInitialVehicleBreaks() const;
1712 bool InitializeAcceptPath() override {
1713 accepted_objective_value_ = synchronized_objective_value_;
1714 dimension_values_.Revert();
1716 global_span_cost_.Revert();
1717 location_of_node_.Revert();
1720 bool AcceptPath(int64_t path_start, int64_t chain_start,
1721 int64_t chain_end) override;
1722 bool FinalizeAcceptPath(int64_t objective_min,
1723 int64_t objective_max) override;
1725 void OnBeforeSynchronizePaths(bool synchronizing_all_paths) override;
1726 void OnSynchronizePathFromStart(int64_t start) override;
1727 void OnAfterSynchronizePaths() override;
1732 bool FillDimensionValues(int path);
1735 bool PropagateTransitsAndSpans(int path);
1737 bool PropagateTransitsWithForbiddenIntervals(int path);
1739 bool PropagateSpan(int path);
1741 bool PropagatePickupToDeliveryLimits(int path);
1743 bool PropagateVehicleBreaks(int path);
1746 bool FilterPrecedences() const { return !node_index_to_precedences_.empty(); }
1748 bool FilterGlobalSpanCost() const {
1749 return global_span_cost_coefficient_ != 0;
1752 bool FilterVehicleBreaks(int path) const {
1753 return dimension_.HasBreakConstraints() &&
1754 (!dimension_.GetBreakIntervalsOfVehicle(path).empty() ||
1755 !dimension_.GetBreakDistanceDurationOfVehicle(path).empty());
1758 bool FilterSoftSpanCost(int path) const {
1759 return dimension_.HasSoftSpanUpperBounds() &&
1760 dimension_.GetSoftSpanUpperBoundForVehicle(path).cost > 0;
1763 bool FilterSoftSpanQuadraticCost(int path) const {
1764 if (!dimension_.HasQuadraticCostSoftSpanUpperBounds()) return false;
1765 const auto [bound, cost] =
1766 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(path);
1770 bool FilterWithDimensionCumulOptimizerForVehicle(int vehicle) const {
1771 if (!may_use_optimizers_) return false;
1772 if (!cumul_piecewise_linear_costs_.empty()) return false;
1774 int num_linear_constraints = 0;
1775 if (dimension_.GetSpanCostCoefficientForVehicle(vehicle) > 0 ||
1776 dimension_.GetSlackCostCoefficientForVehicle(vehicle) > 0) {
1777 ++num_linear_constraints;
1779 if (FilterSoftSpanCost(vehicle)) ++num_linear_constraints;
1780 if (!cumul_soft_upper_bounds_.empty()) ++num_linear_constraints;
1781 if (!cumul_soft_lower_bounds_.empty()) ++num_linear_constraints;
1782 if (path_span_upper_bounds_[vehicle] < kint64max) {
1783 ++num_linear_constraints;
1785 const bool has_breaks = FilterVehicleBreaks(vehicle);
1786 if (has_breaks) ++num_linear_constraints;
1794 return num_linear_constraints >= 2 &&
1795 (has_breaks || filter_objective_cost_);
1799 const RoutingModel& routing_model_;
1800 const RoutingDimension& dimension_;
1801 const std::vector<Interval> initial_cumul_;
1802 const std::vector<Interval> initial_slack_;
1803 const std::vector<std::vector<VehicleBreak>> initial_vehicle_breaks_;
1805 const std::vector<const RoutingModel::TransitCallback2*> evaluators_;
1806 const std::vector<int64_t> path_capacities_;
1807 const std::vector<int64_t> path_span_upper_bounds_;
1808 const std::vector<int64_t> path_total_slack_cost_coefficients_;
1833 const int64_t global_span_cost_coefficient_;
1837 bool operator<(const ValuedPath& other) const {
1838 return std::tie(value, path) < std::tie(other.value, other.path);
1840 bool operator>(const ValuedPath& other) const {
1841 return std::tie(value, path) > std::tie(other.value, other.path);
1845 std::vector<ValuedPath> paths_by_decreasing_span_min_;
1847 std::vector<ValuedPath> paths_by_decreasing_end_min_;
1849 std::vector<ValuedPath> paths_by_increasing_start_max_;
1851 CommittableValue<int64_t> global_span_cost_;
1854 const std::vector<SoftBound> cumul_soft_upper_bounds_;
1856 const std::vector<SoftBound> cumul_soft_lower_bounds_;
1858 const std::vector<const PiecewiseLinearFunction*>
1859 cumul_piecewise_linear_costs_;
1861 const bool has_forbidden_intervals_;
1865 DimensionValues dimension_values_;
1866 PrePostVisitValues visits_;
1867 BreakPropagator break_propagator_;
1871 CommittableArray<int64_t> cost_of_path_;
1872 int64_t synchronized_objective_value_;
1873 int64_t accepted_objective_value_;
1883 CommittableArray<RankAndIndex> pickup_rank_and_alternative_index_of_pair_;
1888 const std::vector<std::vector<RoutingDimension::NodePrecedence>>
1889 node_index_to_precedences_;
1890 absl::flat_hash_map<std::pair<int, int>, int64_t> precedence_offsets_;
1897 CommittableArray<PathAndRank> location_of_node_;
1902 LocalDimensionCumulOptimizer* lp_optimizer_;
1903 LocalDimensionCumulOptimizer* mp_optimizer_;
1904 const std::function<int64_t(int64_t)> path_accessor_;
1905 const bool filter_objective_cost_;
1907 const bool may_use_optimizers_;
1908 const bool propagate_own_objective_value_;
1913std::vector<T> SumOfVectors(const std::vector<T>& v1,
1914 const std::vector<T>& v2) {
1915 DCHECK_EQ(v1.size(), v2.size());
1916 std::vector<T> sum(v1.size());
1917 absl::c_transform(v1, v2, sum.begin(), [](T a, T b) { return CapAdd(a, b); });
1922std::vector<PathCumulFilter::Interval>
1923PathCumulFilter::ExtractInitialCumulIntervals() {
1924 std::vector<Interval> intervals;
1925 intervals.reserve(dimension_.cumuls().size());
1926 for (const IntVar* cumul : dimension_.cumuls()) {
1927 intervals.push_back({cumul->Min(), cumul->Max()});
1932std::vector<PathCumulFilter::Interval>
1933PathCumulFilter::ExtractInitialSlackIntervals() {
1934 std::vector<Interval> intervals;
1935 intervals.reserve(dimension_.slacks().size());
1936 for (const IntVar* slack : dimension_.slacks()) {
1937 intervals.push_back({slack->Min(), slack->Max()});
1942std::vector<PathCumulFilter::SoftBound>
1943PathCumulFilter::ExtractCumulSoftUpperBounds() const {
1944 const int num_cumuls = dimension_.cumuls().size();
1945 std::vector<SoftBound> bounds(num_cumuls,
1946 {.bound = kint64max, .coefficient = 0});
1947 bool has_some_bound = false;
1948 for (int i = 0; i < num_cumuls; ++i) {
1949 if (!dimension_.HasCumulVarSoftUpperBound(i)) continue;
1950 const int64_t bound = dimension_.GetCumulVarSoftUpperBound(i);
1951 const int64_t coeff = dimension_.GetCumulVarSoftUpperBoundCoefficient(i);
1952 bounds[i] = {.bound = bound, .coefficient = coeff};
1955 if (!has_some_bound) bounds.clear();
1959std::vector<PathCumulFilter::SoftBound>
1960PathCumulFilter::ExtractCumulSoftLowerBounds() const {
1961 const int num_cumuls = dimension_.cumuls().size();
1962 std::vector<SoftBound> bounds(num_cumuls, {.bound = 0, .coefficient = 0});
1963 bool has_some_bound = false;
1964 for (int i = 0; i < num_cumuls; ++i) {
1965 if (!dimension_.HasCumulVarSoftLowerBound(i)) continue;
1966 const int64_t bound = dimension_.GetCumulVarSoftLowerBound(i);
1967 const int64_t coeff = dimension_.GetCumulVarSoftLowerBoundCoefficient(i);
1968 bounds[i] = {.bound = bound, .coefficient = coeff};
1969 has_some_bound |= bound > 0 && coeff != 0;
1971 if (!has_some_bound) bounds.clear();
1975std::vector<const PiecewiseLinearFunction*>
1976PathCumulFilter::ExtractCumulPiecewiseLinearCosts() const {
1977 const int num_cumuls = dimension_.cumuls().size();
1978 std::vector<const PiecewiseLinearFunction*> costs(num_cumuls, nullptr);
1979 bool has_some_cost = false;
1980 for (int i = 0; i < dimension_.cumuls().size(); ++i) {
1981 if (!dimension_.HasCumulVarPiecewiseLinearCost(i)) continue;
1982 const PiecewiseLinearFunction* const cost =
1983 dimension_.GetCumulVarPiecewiseLinearCost(i);
1984 if (cost == nullptr) continue;
1986 costs[i] = cost;
1988 if (!has_some_cost) costs.clear();
1992std::vector<const RoutingModel::TransitCallback2*>
1993PathCumulFilter::ExtractEvaluators() const {
1994 const int num_paths = NumPaths();
1995 std::vector<const RoutingModel::TransitCallback2*> evaluators(num_paths);
1996 for (int i = 0; i < num_paths; ++i) {
1997 evaluators[i] = &dimension_.transit_evaluator(i);
2002std::vector<std::vector<RoutingDimension::NodePrecedence>>
2003PathCumulFilter::ExtractNodeIndexToPrecedences() const {
2004 std::vector<std::vector<RoutingDimension::NodePrecedence>>
2005 node_index_to_precedences;
2006 const std::vector<RoutingDimension::NodePrecedence>& node_precedences =
2007 dimension_.GetNodePrecedences();
2008 if (!node_precedences.empty()) {
2009 node_index_to_precedences.resize(initial_cumul_.size());
2010 for (const auto& node_precedence : node_precedences) {
2011 node_index_to_precedences[node_precedence.first_node].push_back(
2013 node_index_to_precedences[node_precedence.second_node].push_back(
2017 return node_index_to_precedences;
2020std::vector<std::vector<DimensionValues::VehicleBreak>>
2021PathCumulFilter::ExtractInitialVehicleBreaks() const {
2022 const int num_vehicles = routing_model_.vehicles();
2023 std::vector<std::vector<VehicleBreak>> vehicle_breaks(num_vehicles);
2024 if (!dimension_.HasBreakConstraints()) return vehicle_breaks;
2025 for (int v = 0; v < num_vehicles; ++v) {
2026 for (const IntervalVar* br : dimension_.GetBreakIntervalsOfVehicle(v)) {
2027 vehicle_breaks[v].push_back(
2028 {.start = {.min = br->StartMin(), .max = br->StartMax()},
2029 .end = {.min = br->EndMin(), .max = br->EndMax()},
2030 .duration = {.min = br->DurationMin(), .max = br->DurationMax()},
2031 .is_performed = {.min = br->MustBePerformed(),
2032 .max = br->MayBePerformed()}});
2038PathCumulFilter::PathCumulFilter(const RoutingModel& routing_model,
2039 const RoutingDimension& dimension,
2040 bool propagate_own_objective_value,
2041 bool filter_objective_cost,
2043 : BasePathFilter(routing_model.Nexts(), dimension.cumuls().size(),
2044 routing_model.GetPathsMetadata()),
2045 routing_model_(routing_model),
2047 initial_cumul_(ExtractInitialCumulIntervals()),
2048 initial_slack_(ExtractInitialSlackIntervals()),
2049 initial_vehicle_breaks_(ExtractInitialVehicleBreaks()),
2050 evaluators_(ExtractEvaluators()),
2052 path_capacities_(dimension.vehicle_capacities()),
2053 path_span_upper_bounds_(dimension.vehicle_span_upper_bounds()),
2054 path_total_slack_cost_coefficients_(
2055 SumOfVectors(dimension.vehicle_span_cost_coefficients(),
2056 dimension.vehicle_slack_cost_coefficients())),
2057 global_span_cost_coefficient_(dimension.global_span_cost_coefficient()),
2059 cumul_soft_upper_bounds_(ExtractCumulSoftUpperBounds()),
2060 cumul_soft_lower_bounds_(ExtractCumulSoftLowerBounds()),
2061 cumul_piecewise_linear_costs_(ExtractCumulPiecewiseLinearCosts()),
2062 has_forbidden_intervals_(
2063 absl::c_any_of(dimension.forbidden_intervals(),
2064 [](const SortedDisjointIntervalList& intervals) {
2065 return intervals.NumIntervals() > 0;
2068 dimension_values_(routing_model.vehicles(), dimension.cumuls().size()),
2069 visits_(routing_model.vehicles(), dimension.cumuls().size()),
2070 break_propagator_(dimension.cumuls().size()),
2071 cost_of_path_(NumPaths(), 0),
2072 synchronized_objective_value_(0),
2073 accepted_objective_value_(0),
2074 pickup_rank_and_alternative_index_of_pair_(
2075 routing_model_.GetPickupAndDeliveryPairs().size(), {-1, -1}),
2076 node_index_to_precedences_(ExtractNodeIndexToPrecedences()),
2077 location_of_node_(dimension.cumuls().size(), {-1, -1}),
2079 lp_optimizer_(routing_model.GetMutableLocalCumulLPOptimizer(dimension)),
2080 mp_optimizer_(routing_model.GetMutableLocalCumulMPOptimizer(dimension)),
2081 path_accessor_([this](int64_t node) { return GetNext(node); }),
2082 filter_objective_cost_(filter_objective_cost),
2083 may_use_optimizers_(may_use_optimizers),
2084 propagate_own_objective_value_(propagate_own_objective_value) {
2085 for (int node = 0; node < node_index_to_precedences_.size(); ++node) {
2086 for (const auto [first_node, second_node, offset,
2087 unused_performed_constraint] :
2088 node_index_to_precedences_[node]) {
2090 &precedence_offsets_, {first_node, second_node}, offset);
2091 current_offset = std::max(current_offset, offset);
2095 for (int vehicle = 0; vehicle < routing_model.vehicles(); vehicle++) {
2096 if (FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
2097 DCHECK_NE(lp_optimizer_, nullptr);
2098 DCHECK_NE(mp_optimizer_, nullptr);
2104bool PathCumulFilter::PropagateTransitsAndSpans(int path) {
2105 if (has_forbidden_intervals_) {
2106 return PropagateSpan(path) &&
2107 PropagateTransitsWithForbiddenIntervals(path) && PropagateSpan(path);
2113bool PathCumulFilter::PropagateTransitsWithForbiddenIntervals(int path) {
2114 DCHECK_LT(0, dimension_values_.NumNodes(path));
2115 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
2116 absl::Span<const Interval> transits = dimension_values_.Transits(path);
2117 absl::Span<const int> nodes = dimension_values_.Nodes(path);
2118 const int num_nodes = dimension_values_.NumNodes(path);
2119 auto reduce_to_allowed_interval = [this](Interval& interval,
2121 DCHECK(!interval.IsEmpty());
2122 interval.min = dimension_.GetFirstPossibleGreaterOrEqualValueForNode(
2124 if (interval.IsEmpty()) return false;
2126 dimension_.GetLastPossibleLessOrEqualValueForNode(node, interval.max);
2127 return !interval.IsEmpty();
2130 if (!reduce_to_allowed_interval(cumuls[0], nodes[0])) return false;
2131 Interval cumul = cumuls[0];
2132 for (int r = 1; r < num_nodes; ++r) {
2133 cumul.Add(transits[r - 1]);
2134 if (!cumul.IntersectWith(cumuls[r])) return false;
2135 if (!reduce_to_allowed_interval(cumul, nodes[r])) return false;
2139 for (int r = num_nodes - 2; r >= 0; --r) {
2140 cumul.Subtract(transits[r]);
2141 if (!cumul.IntersectWith(cumuls[r])) return false;
2142 if (!reduce_to_allowed_interval(cumul, nodes[r])) return false;
2148bool PathCumulFilter::PropagateSpan(int path) {
2149 absl::Span<const int64_t> travel_sums = dimension_values_.TravelSums(path);
2150 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
2153 Interval start = cumuls.front();
2154 Interval end = cumuls.back();
2155 Interval span = dimension_values_.Span(path);
2156 if (!span.IncreaseMin(travel_sums.back())) return false;
2160 if (!end.DecreaseMax(CapAdd(start.max, span.max))) return false;
2161 if (!start.IncreaseMin(CapSub(end.min, span.max))) return false;
2162 if (!span.IncreaseMin(CapSub(end.min, start.max))) return false;
2165 if (!span.DecreaseMax(CapSub(end.max, start.min))) return false;
2166 if (!end.IncreaseMin(CapAdd(start.min, span.min))) return false;
2167 if (!start.DecreaseMax(CapSub(end.max, span.min))) return false;
2170 cumuls.back() = end;
2171 dimension_values_.MutableSpan(path) = span;
2175bool PathCumulFilter::FillDimensionValues(int path) {
2178 dimension_values_.PushNode(node);
2180 const int next = GetNext(node);
2181 dimension_values_.PushNode(next);
2184 dimension_values_.MakePathFromNewNodes(path);
2186 path, path_capacities_[path], path_span_upper_bounds_[path],
2187 initial_cumul_, initial_slack_, *evaluators_[path],
2191 dimension_values_.MutableVehicleBreaks(path) = initial_vehicle_breaks_[path];
2195bool PathCumulFilter::PropagatePickupToDeliveryLimits(int path) {
2196 const int num_nodes = dimension_values_.NumNodes(path);
2197 absl::Span<const int> nodes = dimension_values_.Nodes(path);
2198 absl::Span<const int64_t> travel_sums = dimension_values_.TravelSums(path);
2199 absl::Span<Interval> cumuls = dimension_values_.MutableCumuls(path);
2206 pickup_rank_and_alternative_index_of_pair_.Revert();
2207 for (int rank = 1; rank < num_nodes - 1; ++rank) {
2208 const int node = nodes[rank];
2209 const std::optional<RoutingModel::PickupDeliveryPosition> pickup_pos =
2210 routing_model_.GetPickupPosition(node);
2212 if (pickup_pos.has_value()) {
2213 const auto [pair_index, pickup_alternative_index] = *pickup_pos;
2214 pickup_rank_and_alternative_index_of_pair_.Set(
2215 pair_index, {.rank = rank, .index = pickup_alternative_index});
2222 const std::optional<RoutingModel::PickupDeliveryPosition> delivery_pos =
2223 routing_model_.GetDeliveryPosition(node);
2224 if (!delivery_pos.has_value()) continue;
2225 const auto [pair_index, delivery_alt_index] = *delivery_pos;
2226 const auto [pickup_rank, pickup_alt_index] =
2227 pickup_rank_and_alternative_index_of_pair_.Get(pair_index);
2228 if (pickup_rank == -1) continue;
2230 const int64_t limit = dimension_.GetPickupToDeliveryLimitForPair(
2231 pair_index, pickup_alt_index, delivery_alt_index);
2232 if (limit == kint64max) continue;
2236 const int64_t total_travel =
2237 CapSub(travel_sums[rank], travel_sums[pickup_rank]);
2238 if (total_travel > limit) return false;
2241 if (!cumuls[rank].DecreaseMax(CapAdd(cumuls[pickup_rank].max, limit))) {
2244 if (!cumuls[pickup_rank].IncreaseMin(CapSub(cumuls[rank].min, limit))) {
2251bool PathCumulFilter::PropagateVehicleBreaks(int path) {
2254 dimension_.GetBreakDistanceDurationOfVehicle(path));
2257bool PathCumulFilter::AcceptPath(int64_t path_start, int64_t ,
2259 const int path = GetPath(path_start);
2260 if (!FillDimensionValues(path)) return false;
2265 if (!PropagateTransitsAndSpans(path)) return false;
2266 if (dimension_.HasPickupToDeliveryLimits()) {
2267 if (!PropagatePickupToDeliveryLimits(path)) return false;
2269 if (!PropagateTransitsAndSpans(path)) return false;
2271 if (FilterVehicleBreaks(path)) {
2274 const auto& interbreaks =
2275 dimension_.GetBreakDistanceDurationOfVehicle(path);
2276 if (!PropagateVehicleBreaks(path) ||
2277 break_propagator_.PropagateInterbreak(path, dimension_values_,
2279 BreakPropagator::PropagationResult::kInfeasible ||
2280 !PropagateTransitsAndSpans(path)) {
2285 auto any_invocable = [this](int evaluator_index)
2286 -> std::optional<absl::AnyInvocable<int64_t(int64_t, int64_t) const>> {
2290 : dimension_.model()->TransitCallback(evaluator_index);
2291 if (evaluator == nullptr) return std::nullopt;
2296 any_invocable(dimension_.GetPreTravelEvaluatorOfVehicle(path)),
2297 any_invocable(dimension_.GetPostTravelEvaluatorOfVehicle(path)),
2300 BreakPropagator::PropagationResult result = BreakPropagator::kChanged;
2302 while (result == BreakPropagator::kChanged && num_iterations-- > 0) {
2304 break_propagator_.FastPropagations(path, dimension_values_, visits_);
2305 if (result == BreakPropagator::kChanged) {
2306 if (!PropagateVehicleBreaks(path) ||
2307 break_propagator_.PropagateInterbreak(path, dimension_values_,
2309 BreakPropagator::PropagationResult::kInfeasible ||
2310 !PropagateTransitsAndSpans(path)) {
2315 if (result == BreakPropagator::kInfeasible) return false;
2320 if (!filter_objective_cost_) return true;
2322 CapSubFrom(cost_of_path_.Get(path), &accepted_objective_value_);
2323 if (routing_model_.IsEnd(GetNext(path_start)) &&
2324 !routing_model_.IsVehicleUsedWhenEmpty(path)) {
2325 cost_of_path_.Set(path, 0);
2329 const absl::Span<const Interval> cumuls = dimension_values_.Cumuls(path);
2330 const absl::Span<const int> nodes = dimension_values_.Nodes(path);
2331 const Interval span = dimension_values_.Span(path);
2332 const int64_t total_travel = dimension_values_.TravelSums(path).back();
2333 const int64_t min_total_slack = CapSub(span.min, total_travel);
2335 int64_t new_path_cost = 0;
2337 CapAddTo(CapProd(path_total_slack_cost_coefficients_[path], min_total_slack),
2340 if (dimension_.HasSoftSpanUpperBounds()) {
2341 const auto [bound, cost] = dimension_.GetSoftSpanUpperBoundForVehicle(path);
2345 if (dimension_.HasQuadraticCostSoftSpanUpperBounds()) {
2346 const auto [bound, cost] =
2347 dimension_.GetQuadraticCostSoftSpanUpperBoundForVehicle(path);
2348 const int64_t violation = std::max<int64_t>(0, CapSub(span.min, bound));
2352 const int num_path_nodes = dimension_values_.NumNodes(path);
2353 if (!cumul_soft_lower_bounds_.empty()) {
2354 for (int r = 0; r < num_path_nodes; ++r) {
2355 const auto [bound, coef] = cumul_soft_lower_bounds_[nodes[r]];
2357 CapProd(coef, std::max<int64_t>(0, CapSub(bound, cumuls[r].max))),
2362 if (!cumul_soft_upper_bounds_.empty()) {
2363 for (int r = 0; r < num_path_nodes; ++r) {
2364 const auto [bound, coef] = cumul_soft_upper_bounds_[nodes[r]];
2366 CapProd(coef, std::max<int64_t>(0, CapSub(cumuls[r].min, bound))),
2373 if (!cumul_piecewise_linear_costs_.empty()) {
2374 for (int r = 0; r < num_path_nodes; ++r) {
2375 const PiecewiseLinearFunction* cost =
2376 cumul_piecewise_linear_costs_[nodes[r]];
2377 if (cost == nullptr) continue;
2378 CapAddTo(cost->Value(cumuls[r].min), &new_path_cost);
2382 CapAddTo(new_path_cost, &accepted_objective_value_);
2383 cost_of_path_.Set(path, new_path_cost);
2387bool PathCumulFilter::FinalizeAcceptPath(int64_t ,
2389 if (lns_detected()) return true;
2390 if (FilterPrecedences()) {
2396 for (const int path : dimension_values_.ChangedPaths()) {
2397 const absl::Span<const int64_t> travel_sums =
2398 dimension_values_.TravelSums(path);
2401 for (const int node : dimension_values_.Nodes(path)) {
2402 int64_t offset = std::numeric_limits<int64_t>::min();
2404 if (gtl::FindCopy(precedence_offsets_, std::pair<int, int>{node, prev},
2406 CapSub(travel_sums[rank], travel_sums[rank + 1]) < offset) {
2415 for (const int path : dimension_values_.ChangedPaths()) {
2416 for (const int node : dimension_values_.CommittedNodes(path)) {
2417 location_of_node_.Set(node, {.path = -1, .rank = -1});
2420 for (const int path : dimension_values_.ChangedPaths()) {
2421 const absl::Span<const int> nodes = dimension_values_.Nodes(path);
2422 const int num_nodes = nodes.size();
2423 for (int rank = 0; rank < num_nodes; ++rank) {
2424 location_of_node_.Set(nodes[rank], {.path = path, .rank = rank});
2428 for (const int path : dimension_values_.ChangedPaths()) {
2429 const absl::Span<const int> nodes = dimension_values_.Nodes(path);
2430 const int num_nodes = nodes.size();
2431 for (int rank = 0; rank < num_nodes; ++rank) {
2432 const int node = nodes[rank];
2433 for (const auto [first_node, second_node, offset,
2435 node_index_to_precedences_[node]) {
2436 const auto [path1, rank1] = location_of_node_.Get(first_node);
2437 const auto [path2, rank2] = location_of_node_.Get(second_node);
2438 if (path1 == -1 && !IsVarSynced(first_node)) continue;
2439 if (path2 == -1 && !IsVarSynced(second_node)) continue;
2440 switch (RoutingDimension::GetPrecedenceStatus(
2441 path1 == -1, path2 == -1, performed_constraint)) {
2442 case RoutingDimension::PrecedenceStatus::kActive:
2444 case RoutingDimension::PrecedenceStatus::kInactive:
2446 case RoutingDimension::PrecedenceStatus::kInvalid:
2449 DCHECK(node == first_node || node == second_node);
2450 DCHECK_EQ(first_node, dimension_values_.Nodes(path1)[rank1]);
2451 DCHECK_EQ(second_node, dimension_values_.Nodes(path2)[rank2]);
2454 if (path1 == path2 && rank2 < rank1) {
2455 absl::Span<const int64_t> travel_sums =
2456 dimension_values_.TravelSums(path);
2459 if (CapSub(travel_sums[rank2], travel_sums[rank1]) < offset) {
2464 if (CapAdd(dimension_values_.Cumuls(path1)[rank1].min, offset) >
2465 dimension_values_.Cumuls(path2)[rank2].max)
2471 if (filter_objective_cost_ && FilterGlobalSpanCost()) {
2472 int64_t global_end_min = kint64min;
2473 int64_t global_start_max = kint64max;
2474 int64_t global_span_min = 0;
2476 for (const int path : dimension_values_.ChangedPaths()) {
2477 if (dimension_values_.NumNodes(path) == 0) continue;
2478 if (dimension_values_.NumNodes(path) == 2 &&
2479 !routing_model_.IsVehicleUsedWhenEmpty(path)) {
2483 std::max(global_span_min, dimension_values_.Span(path).min);
2484 const int64_t end_min = dimension_values_.Cumuls(path).back().min;
2485 global_end_min = std::max(global_end_min, end_min);
2486 const int64_t start_max = dimension_values_.Cumuls(path).front().max;
2487 global_start_max = std::min(global_start_max, start_max);
2490 for (const auto& [span_min, path] : paths_by_decreasing_span_min_) {
2491 if (dimension_values_.PathHasChanged(path)) continue;
2492 global_span_min = std::max(global_span_min, span_min);
2495 for (const auto& [end_min, path] : paths_by_decreasing_end_min_) {
2496 if (dimension_values_.PathHasChanged(path)) continue;
2497 global_end_min = std::max(global_end_min, end_min);
2500 for (const auto& [start_max, path] : paths_by_increasing_start_max_) {
2501 if (dimension_values_.PathHasChanged(path)) continue;
2502 global_start_max = std::min(global_start_max, start_max);
2507 std::max(global_span_min, CapSub(global_end_min, global_start_max));
2508 const int64_t global_span_cost =
2509 CapProd(global_span_min, global_span_cost_coefficient_);
2510 CapSubFrom(global_span_cost_.Get(), &accepted_objective_value_);
2511 global_span_cost_.Set(global_span_cost);
2512 CapAddTo(global_span_cost, &accepted_objective_value_);
2516 if (may_use_optimizers_ && lp_optimizer_ != nullptr &&
2517 accepted_objective_value_ <= objective_max) {
2518 std::vector<int> paths_requiring_mp_optimizer;
2521 int solve_duration_shares = dimension_values_.ChangedPaths().size();
2522 for (const int vehicle : dimension_values_.ChangedPaths()) {
2523 if (!FilterWithDimensionCumulOptimizerForVehicle(vehicle)) {
2526 int64_t path_cost_with_lp = 0;
2528 lp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
2529 vehicle, 1.0 / solve_duration_shares,
2531 filter_objective_cost_ ? &path_cost_with_lp : nullptr);
2533 if (status == DimensionSchedulingStatus::INFEASIBLE) return false;
2535 if (filter_objective_cost_ &&
2536 (status == DimensionSchedulingStatus::OPTIMAL ||
2537 status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) &&
2538 path_cost_with_lp > cost_of_path_.Get(vehicle)) {
2539 CapSubFrom(cost_of_path_.Get(vehicle), &accepted_objective_value_);
2540 CapAddTo(path_cost_with_lp, &accepted_objective_value_);
2541 if (accepted_objective_value_ > objective_max) return false;
2542 cost_of_path_.Set(vehicle, path_cost_with_lp);
2545 DCHECK_NE(mp_optimizer_, nullptr);
2546 if (FilterVehicleBreaks(vehicle) ||
2547 (filter_objective_cost_ &&
2548 (FilterSoftSpanQuadraticCost(vehicle) ||
2549 status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY))) {
2550 paths_requiring_mp_optimizer.push_back(vehicle);
2554 DCHECK_LE(accepted_objective_value_, objective_max);
2556 solve_duration_shares = paths_requiring_mp_optimizer.size();
2557 for (const int vehicle : paths_requiring_mp_optimizer) {
2558 int64_t path_cost_with_mp = 0;
2560 mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
2561 vehicle, 1.0 / solve_duration_shares,
2563 filter_objective_cost_ ? &path_cost_with_mp : nullptr);
2565 if (status == DimensionSchedulingStatus::INFEASIBLE) {
2568 if (filter_objective_cost_ &&
2569 status == DimensionSchedulingStatus::OPTIMAL &&
2570 path_cost_with_mp > cost_of_path_.Get(vehicle)) {
2571 CapSubFrom(cost_of_path_.Get(vehicle), &accepted_objective_value_);
2572 CapAddTo(path_cost_with_mp, &accepted_objective_value_);
2573 if (accepted_objective_value_ > objective_max) return false;
2574 cost_of_path_.Set(vehicle, path_cost_with_mp);
2578 return accepted_objective_value_ <= objective_max;
2581void PathCumulFilter::OnBeforeSynchronizePaths(bool synchronizing_all_paths) {
2582 if (synchronizing_all_paths) {
2586 dimension_values_.Reset();
2587 cost_of_path_.SetAllAndCommit(0);
2588 location_of_node_.SetAllAndCommit({-1, -1});
2589 global_span_cost_.SetAndCommit(0);
2590 synchronized_objective_value_ = 0;
2593 accepted_objective_value_ = synchronized_objective_value_;
2594 if (HasAnySyncedPath()) {
2601void PathCumulFilter::OnSynchronizePathFromStart(int64_t start) {
2602 DCHECK(IsVarSynced(start));
2605 const int path = GetPath(start);
2606 const bool is_accepted = AcceptPath(start, start, End(path));
2610void PathCumulFilter::OnAfterSynchronizePaths() {
2611 if (filter_objective_cost_ && FilterGlobalSpanCost()) {
2614 paths_by_decreasing_span_min_.clear();
2615 paths_by_decreasing_end_min_.clear();
2616 paths_by_increasing_start_max_.clear();
2617 for (int path = 0; path < NumPaths(); ++path) {
2618 const int num_path_nodes = dimension_values_.Nodes(path).size();
2619 if (num_path_nodes == 0 ||
2621 !routing_model_.IsVehicleUsedWhenEmpty(path))) {
2624 paths_by_decreasing_span_min_.push_back(
2625 {.value = dimension_values_.Span(path).min, .path = path});
2626 paths_by_decreasing_end_min_.push_back(
2627 {.value = dimension_values_.Cumuls(path).back().min, .path = path});
2628 paths_by_increasing_start_max_.push_back(
2629 {.value = dimension_values_.Cumuls(path)[0].max, .path = path});
2631 absl::c_sort(paths_by_decreasing_span_min_, std::greater<ValuedPath>());
2632 absl::c_sort(paths_by_decreasing_end_min_, std::greater<ValuedPath>());
2633 absl::c_sort(paths_by_increasing_start_max_);
2637 dimension_values_.Commit();
2639 global_span_cost_.Commit();
2640 location_of_node_.Commit();
2641 synchronized_objective_value_ = accepted_objective_value_;
2647 bool propagate_own_objective_value,
2648 bool filter_objective_cost,
2649 bool may_use_optimizers) {
2652 new PathCumulFilter(model, dimension, propagate_own_objective_value,
2658bool DimensionHasCumulCost(const RoutingDimension& dimension) {
2659 if (dimension.global_span_cost_coefficient() != 0) return true;
2660 if (dimension.HasSoftSpanUpperBounds()) return true;
2661 if (dimension.HasQuadraticCostSoftSpanUpperBounds()) return true;
2662 if (absl::c_any_of(dimension.vehicle_span_cost_coefficients(),
2663 [](int64_t coefficient) { return coefficient != 0; })) {
2666 if (absl::c_any_of(dimension.vehicle_slack_cost_coefficients(),
2667 [](int64_t coefficient) { return coefficient != 0; })) {
2670 for (int i = 0; i < dimension.cumuls().size(); ++i) {
2671 if (dimension.HasCumulVarSoftUpperBound(i)) return true;
2672 if (dimension.HasCumulVarSoftLowerBound(i)) return true;
2673 if (dimension.HasCumulVarPiecewiseLinearCost(i)) return true;
2678bool DimensionHasPathCumulConstraint(const RoutingDimension& dimension) {
2679 if (dimension.HasBreakConstraints()) return true;
2680 if (dimension.HasPickupToDeliveryLimits()) return true;
2682 dimension.vehicle_span_upper_bounds(), [](int64_t upper_bound) {
2683 return upper_bound != std::numeric_limits<int64_t>::max();
2687 if (absl::c_any_of(dimension.slacks(),
2688 [](IntVar* slack) { return slack->Min() > 0; })) {
2691 const std::vector<IntVar*>& cumuls = dimension.cumuls();
2692 for (int i = 0; i < cumuls.size(); ++i) {
2693 IntVar* const cumul_var = cumuls[i];
2694 if (cumul_var->Min() > 0 &&
2695 cumul_var->Max() < std::numeric_limits<int64_t>::max() &&
2696 !dimension.model()->IsEnd(i)) {
2699 if (dimension.forbidden_intervals()[i].NumIntervals() > 0) return true;
2708 const std::vector<RoutingDimension*>& dimensions,
2709 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2715 const int num_vehicles = dimension->model()->vehicles();
2716 std::vector<Interval> path_capacity(num_vehicles);
2717 std::vector<int> path_class(num_vehicles);
2718 for (int v = 0; v < num_vehicles; ++v) {
2719 const auto& vehicle_capacities = dimension->vehicle_capacities();
2720 path_capacity[v] = {0, vehicle_capacities[v]};
2721 path_class[v] = dimension->vehicle_to_class(v);
2728 const int num_vehicle_classes =
2729 1 + *std::max_element(path_class.begin(), path_class.end());
2730 const int num_cumuls = dimension->cumuls().size();
2731 const int num_slacks = dimension->slacks().size();
2732 std::vector<std::function<Interval(int64_t, int64_t)>> transits(
2733 num_vehicle_classes, nullptr);
2734 for (int vehicle = 0; vehicle < num_vehicles; ++vehicle) {
2735 const int vehicle_class = path_class[vehicle];
2736 if (transits[vehicle_class] != nullptr) continue;
2737 const auto& unary_evaluator =
2738 dimension->GetUnaryTransitEvaluator(vehicle);
2739 if (unary_evaluator != nullptr) {
2740 transits[vehicle_class] = [&unary_evaluator, dimension, num_slacks](
2741 int64_t node, int64_t) -> Interval {
2742 if (node >= num_slacks) return {0, 0};
2743 const int64_t min_transit = unary_evaluator(node);
2744 const int64_t max_transit =
2745 CapAdd(min_transit, dimension->SlackVar(node)->Max());
2746 return {min_transit, max_transit};
2749 const auto& binary_evaluator =
2750 dimension->GetBinaryTransitEvaluator(vehicle);
2752 transits[vehicle_class] = [&binary_evaluator, dimension, num_slacks](
2753 int64_t node, int64_t next) -> Interval {
2754 if (node >= num_slacks) return {0, 0};
2755 const int64_t min_transit = binary_evaluator(node, next);
2756 const int64_t max_transit =
2757 CapAdd(min_transit, dimension->SlackVar(node)->Max());
2758 return {min_transit, max_transit};
2763 std::vector<Interval> node_capacity(num_cumuls);
2764 for (int node = 0; node < num_cumuls; ++node) {
2765 const IntVar* cumul = dimension->CumulVar(node);
2766 node_capacity[node] = {cumul->Min(), cumul->Max()};
2769 auto checker = std::make_unique<DimensionChecker>(
2770 path_state, std::move(path_capacity), std::move(path_class),
2771 std::move(transits), std::move(node_capacity));
2774 dimension->model()->solver(), std::move(checker), dimension->name());
2780 const std::vector<RoutingDimension*>& dimensions,
2782 bool use_chain_cumul_filter,
2783 std::vector<LocalSearchFilterManager::FilterEvent>* filters) {
2792 const int num_dimensions = dimensions.size();
2794 const bool has_dimension_optimizers =
2796 std::vector<bool> use_path_cumul_filter(num_dimensions);
2797 std::vector<bool> use_cumul_bounds_propagator_filter(num_dimensions);
2798 std::vector<bool> use_global_lp_filter(num_dimensions);
2799 std::vector<bool> use_resource_assignment_filter(num_dimensions);
2800 for (int d = 0; d < num_dimensions; d++) {
2802 const bool has_cumul_cost = DimensionHasCumulCost(dimension);
2803 use_path_cumul_filter[d] =
2804 has_cumul_cost || DimensionHasPathCumulConstraint(dimension);
2806 const int num_dimension_resource_groups =
2808 const bool can_use_cumul_bounds_propagator_filter =
2810 num_dimension_resource_groups == 0 &&
2811 (!filter_objective_cost || !has_cumul_cost);
2812 const bool has_precedences = !dimension.GetNodePrecedences().empty();
2813 use_global_lp_filter[d] =
2814 has_dimension_optimizers &&
2815 ((has_precedences && !can_use_cumul_bounds_propagator_filter) ||
2816 (filter_objective_cost &&
2818 num_dimension_resource_groups > 1);
2820 use_cumul_bounds_propagator_filter[d] =
2821 has_precedences && !use_global_lp_filter[d];
2823 use_resource_assignment_filter[d] =
2824 has_dimension_optimizers && num_dimension_resource_groups > 0;
2827 for (int d = 0; d < num_dimensions; d++) {
2834 const bool use_global_lp = use_global_lp_filter[d];
2835 const bool filter_resource_assignment = use_resource_assignment_filter[d];
2836 if (use_path_cumul_filter[d]) {
2837 PathCumulFilter* filter = model.solver()->RevAlloc(new PathCumulFilter(
2838 model, dimension, !use_global_lp &&
2839 !filter_resource_assignment,
2840 filter_objective_cost, has_dimension_optimizers));
2841 const int priority = filter->UsesDimensionOptimizers() ? 1 : 0;
2842 filters->push_back({filter, kAccept, priority});
2843 } else if (use_chain_cumul_filter) {
2845 {model.solver()->RevAlloc(new ChainCumulFilter(model, dimension)),
2849 if (use_cumul_bounds_propagator_filter[d]) {
2851 DCHECK(!filter_resource_assignment);
2878class PickupDeliveryFilter : public LocalSearchFilter {
2880 PickupDeliveryFilter(const PathState* path_state,
2881 absl::Span<const PickupDeliveryPair> pairs,
2882 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
2884 ~PickupDeliveryFilter() override = default;
2885 bool Accept(const Assignment* , const Assignment* ,
2886 int64_t , int64_t ) override;
2889 void Synchronize(const Assignment* ,
2890 const Assignment* ) override;
2891 std::string DebugString() const override { return "PickupDeliveryFilter"; }
2894 template <bool check_assigned_pairs>
2895 bool AcceptPathDispatch();
2896 template <bool check_assigned_pairs>
2897 bool AcceptPathDefault(int path);
2898 template <bool lifo, bool check_assigned_pairs>
2899 bool AcceptPathOrdered(int path);
2900 void AssignAllVisitedPairsAndLoopNodes();
2902 const PathState* const path_state_;
2909 PairInfo() : is_paired(false), pair_index(-1) {}
2910 PairInfo(bool is_paired, bool is_pickup, int pair_index)
2911 : is_paired(is_paired), is_pickup(is_pickup), pair_index(pair_index) {}
2913 std::vector<PairInfo> pair_info_of_node_;
2919 PairStatus() : pickup(false), delivery(false) {}
2921 CommittableArray<PairStatus> assigned_status_of_pair_;
2922 SparseBitset<int> pair_is_open_;
2923 CommittableValue<int> num_assigned_pairs_;
2924 std::deque<int> visited_deque_;
2925 const std::vector<RoutingModel::PickupAndDeliveryPolicy> vehicle_policies_;
2928PickupDeliveryFilter::PickupDeliveryFilter(
2929 const PathState* path_state, absl::Span<const PickupDeliveryPair> pairs,
2930 const std::vector<RoutingModel::PickupAndDeliveryPolicy>& vehicle_policies)
2931 : path_state_(path_state),
2932 pair_info_of_node_(path_state->NumNodes()),
2933 assigned_status_of_pair_(pairs.size(), {}),
2934 pair_is_open_(pairs.size()),
2936 vehicle_policies_(vehicle_policies) {
2937 for (int pair_index = 0; pair_index < pairs.size(); ++pair_index) {
2938 const auto& [pickups, deliveries] = pairs[pair_index];
2939 for (const int pickup : pickups) {
2940 pair_info_of_node_[pickup] =
2946 for (const int delivery : deliveries) {
2947 pair_info_of_node_[delivery] =
2956void PickupDeliveryFilter::Reset() {
2957 assigned_status_of_pair_.SetAllAndCommit({});
2958 num_assigned_pairs_.SetAndCommit(0);
2961void PickupDeliveryFilter::AssignAllVisitedPairsAndLoopNodes() {
2962 assigned_status_of_pair_.Revert();
2963 num_assigned_pairs_.Revert();
2964 int num_assigned_pairs = num_assigned_pairs_.Get();
2965 if (num_assigned_pairs == assigned_status_of_pair_.Size()) return;
2968 auto assign_node = [this](int node) -> bool {
2969 const auto [is_paired, is_pickup, pair_index] = pair_info_of_node_[node];
2970 if (!is_paired) return false;
2971 bool assigned_pair = false;
2972 PairStatus assigned_status = assigned_status_of_pair_.Get(pair_index);
2973 if (is_pickup && !assigned_status.pickup) {
2974 assigned_pair = assigned_status.delivery;
2975 assigned_status.pickup = true;
2976 assigned_status_of_pair_.Set(pair_index, assigned_status);
2978 if (!is_pickup && !assigned_status.delivery) {
2979 assigned_pair = assigned_status.pickup;
2980 assigned_status.delivery = true;
2981 assigned_status_of_pair_.Set(pair_index, assigned_status);
2985 for (const int path : path_state_->ChangedPaths()) {
2986 for (const int node : path_state_->Nodes(path)) {
2987 num_assigned_pairs += assign_node(node) ? 1 : 0;
2990 for (const int loop : path_state_->ChangedLoops()) {
2991 num_assigned_pairs += assign_node(loop) ? 1 : 0;
2993 num_assigned_pairs_.Set(num_assigned_pairs);
2996void PickupDeliveryFilter::Synchronize(const Assignment* ,
2998 AssignAllVisitedPairsAndLoopNodes();
2999 assigned_status_of_pair_.Commit();
3000 num_assigned_pairs_.Commit();
3003bool PickupDeliveryFilter::Accept(const Assignment* ,
3007 if (path_state_->IsInvalid()) return true;
3008 AssignAllVisitedPairsAndLoopNodes();
3009 const bool check_assigned_pairs =
3010 num_assigned_pairs_.Get() < assigned_status_of_pair_.Size();
3011 if (check_assigned_pairs) {
3012 return AcceptPathDispatch<true>();
3014 return AcceptPathDispatch<false>();
3018template <bool check_assigned_pairs>
3019bool PickupDeliveryFilter::AcceptPathDispatch() {
3020 for (const int path : path_state_->ChangedPaths()) {
3021 switch (vehicle_policies_[path]) {
3022 case RoutingModel::PICKUP_AND_DELIVERY_NO_ORDER:
3023 if (!AcceptPathDefault<check_assigned_pairs>(path)) return false;
3025 case RoutingModel::PICKUP_AND_DELIVERY_LIFO:
3026 if (!AcceptPathOrdered<true, check_assigned_pairs>(path)) return false;
3028 case RoutingModel::PICKUP_AND_DELIVERY_FIFO:
3029 if (!AcceptPathOrdered<false, check_assigned_pairs>(path)) return false;
3038template <bool check_assigned_pairs>
3039bool PickupDeliveryFilter::AcceptPathDefault(int path) {
3040 pair_is_open_.ResetAllToFalse();
3041 int num_opened_pairs = 0;
3042 for (const int node : path_state_->Nodes(path)) {
3043 const auto [is_paired, is_pickup, pair_index] = pair_info_of_node_[node];
3044 if (!is_paired) continue;
3045 if constexpr (check_assigned_pairs) {
3046 const PairStatus status = assigned_status_of_pair_.Get(pair_index);
3047 if (!status.pickup || !status.delivery) continue;
3050 pair_is_open_.Set(pair_index);
3053 if (!pair_is_open_[pair_index]) return false;
3054 pair_is_open_.Clear(pair_index);
3060 if (num_opened_pairs > 0) return false;
3061 pair_is_open_.NotifyAllClear();
3065template <bool lifo, bool check_assigned_pairs>
3066bool PickupDeliveryFilter::AcceptPathOrdered(int path) {
3068 for (const int node : path_state_->Nodes(path)) {
3069 const auto [is_paired, is_pickup, pair_index] = pair_info_of_node_[node];
3070 if (!is_paired) continue;
3071 if constexpr (check_assigned_pairs) {
3072 const PairStatus status = assigned_status_of_pair_.Get(pair_index);
3073 if (!status.pickup || !status.delivery) continue;
3076 visited_deque_.emplace_back(pair_index);
3078 if (visited_deque_.empty()) return false;
3080 const int last_pair_index = visited_deque_.back();
3081 if (last_pair_index != pair_index) return false;
3082 visited_deque_.pop_back();
3084 const int first_pair_index = visited_deque_.front();
3085 if (first_pair_index != pair_index) return false;
3086 visited_deque_.pop_front();
3090 return visited_deque_.empty();
3097 absl::Span<const PickupDeliveryPair> pairs,
3098 const std::vector<RoutingModel::PickupAndDeliveryPolicy>&
3101 new PickupDeliveryFilter(path_state, pairs, vehicle_policies));
3107class VehicleVarFilter : public LocalSearchFilter {
3109 VehicleVarFilter(const RoutingModel& routing_model,
3110 const PathState* path_state);
3111 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3112 int64_t objective_min, int64_t objective_max) override;
3113 void Synchronize(const Assignment* ,
3114 const Assignment* ) override;
3115 std::string DebugString() const override { return "VehicleVariableFilter"; }
3118 bool HasConstrainedVehicleVars() const;
3120 const PathState* path_state_;
3121 std::vector<IntVar*> vehicle_vars_;
3126VehicleVarFilter::VehicleVarFilter(const RoutingModel& routing_model,
3127 const PathState* path_state)
3128 : path_state_(path_state),
3129 vehicle_vars_(routing_model.VehicleVars()),
3130 num_vehicles_(routing_model.vehicles()),
3131 is_disabled_(!HasConstrainedVehicleVars()) {}
3133bool VehicleVarFilter::HasConstrainedVehicleVars() const {
3134 for (const IntVar* var : vehicle_vars_) {
3135 const int unconstrained_size = num_vehicles_ + ((var->Min() >= 0) ? 0 : 1);
3136 if (var->Size() != unconstrained_size) return true;
3141void VehicleVarFilter::Synchronize(const Assignment* ,
3143 is_disabled_ = !HasConstrainedVehicleVars();
3146bool VehicleVarFilter::Accept(const Assignment* ,
3150 if (is_disabled_) return true;
3151 for (const int path : path_state_->ChangedPaths()) {
3153 for (const PathState::Chain chain :
3154 path_state_->Chains(path).DropFirstChain().DropLastChain()) {
3155 for (const int node : chain) {
3156 if (!vehicle_vars_[node]->Contains(path)) return false;
3173class CumulBoundsPropagatorFilter : public IntVarLocalSearchFilter {
3175 explicit CumulBoundsPropagatorFilter(const RoutingDimension& dimension);
3176 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3177 int64_t objective_min, int64_t objective_max) override;
3178 std::string DebugString() const override {
3179 return "CumulBoundsPropagatorFilter(" + propagator_.dimension().name() +
3184 CumulBoundsPropagator propagator_;
3185 const int64_t cumul_offset_;
3186 SparseBitset<int64_t> delta_touched_;
3187 std::vector<int64_t> delta_nexts_;
3190CumulBoundsPropagatorFilter::CumulBoundsPropagatorFilter(
3191 const RoutingDimension& dimension)
3192 : IntVarLocalSearchFilter(dimension.model()->Nexts()),
3194 cumul_offset_(dimension.GetGlobalOptimizerOffset()),
3198bool CumulBoundsPropagatorFilter::Accept(const Assignment* delta,
3202 delta_touched_.ResetAllToFalse();
3203 for (const IntVarElement& delta_element :
3204 delta->IntVarContainer().elements()) {
3206 if (FindIndex(delta_element.Var(), &index)) {
3207 if (!delta_element.Bound()) {
3211 delta_touched_.Set(index);
3212 delta_nexts_[index] = delta_element.Value();
3215 const auto& next_accessor = [this](int64_t index) -> int64_t {
3216 return delta_touched_[index] ? delta_nexts_[index]
3217 : !IsVarSynced(index) ? -1
3221 return propagator_.PropagateCumulBounds(next_accessor, cumul_offset_);
3234class LPCumulFilter : public IntVarLocalSearchFilter {
3236 LPCumulFilter(const std::vector<IntVar*>& nexts,
3237 GlobalDimensionCumulOptimizer* optimizer,
3238 GlobalDimensionCumulOptimizer* mp_optimizer,
3239 bool filter_objective_cost);
3240 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3241 int64_t objective_min, int64_t objective_max) override;
3242 int64_t GetAcceptedObjectiveValue() const override;
3243 void OnSynchronize(const Assignment* delta) override;
3244 int64_t GetSynchronizedObjectiveValue() const override;
3245 std::string DebugString() const override {
3246 return "LPCumulFilter(" + lp_optimizer_.dimension()->name() + ")";
3250 GlobalDimensionCumulOptimizer& lp_optimizer_;
3251 GlobalDimensionCumulOptimizer& mp_optimizer_;
3252 const bool filter_objective_cost_;
3253 int64_t synchronized_cost_without_transit_;
3254 int64_t delta_cost_without_transit_;
3255 SparseBitset<int64_t> delta_touched_;
3256 std::vector<int64_t> delta_nexts_;
3259LPCumulFilter::LPCumulFilter(const std::vector<IntVar*>& nexts,
3260 GlobalDimensionCumulOptimizer* lp_optimizer,
3261 GlobalDimensionCumulOptimizer* mp_optimizer,
3262 bool filter_objective_cost)
3263 : IntVarLocalSearchFilter(nexts),
3264 lp_optimizer_(*lp_optimizer),
3265 mp_optimizer_(*mp_optimizer),
3266 filter_objective_cost_(filter_objective_cost),
3267 synchronized_cost_without_transit_(-1),
3268 delta_cost_without_transit_(-1),
3272bool LPCumulFilter::Accept(const Assignment* delta,
3274 int64_t , int64_t objective_max) {
3275 delta_touched_.ResetAllToFalse();
3276 for (const IntVarElement& delta_element :
3277 delta->IntVarContainer().elements()) {
3279 if (FindIndex(delta_element.Var(), &index)) {
3280 if (!delta_element.Bound()) {
3284 delta_touched_.Set(index);
3285 delta_nexts_[index] = delta_element.Value();
3288 const auto& next_accessor = [this](int64_t index) {
3289 return delta_touched_[index] ? delta_nexts_[index]
3290 : !IsVarSynced(index) ? -1
3294 if (!filter_objective_cost_) {
3296 delta_cost_without_transit_ = 0;
3298 next_accessor, {}, nullptr, nullptr, nullptr);
3299 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
3300 status = mp_optimizer_.ComputeCumuls(next_accessor, {}, nullptr, nullptr,
3303 DCHECK(status != DimensionSchedulingStatus::FEASIBLE)
3304 << "FEASIBLE without filtering objective cost should be OPTIMAL";
3305 return status == DimensionSchedulingStatus::OPTIMAL;
3309 lp_optimizer_.ComputeCumulCostWithoutFixedTransits(
3310 next_accessor, &delta_cost_without_transit_);
3312 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY ||
3313 status == DimensionSchedulingStatus::FEASIBLE) {
3316 status = mp_optimizer_.ComputeCumulCostWithoutFixedTransits(next_accessor,
3318 if (lp_status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY &&
3319 status == DimensionSchedulingStatus::OPTIMAL) {
3323 delta_cost_without_transit_ = mp_cost;
3324 } else if (lp_status == DimensionSchedulingStatus::FEASIBLE &&
3325 status != DimensionSchedulingStatus::INFEASIBLE) {
3328 delta_cost_without_transit_ =
3329 std::min(delta_cost_without_transit_, mp_cost);
3333 if (status == DimensionSchedulingStatus::INFEASIBLE) {
3334 delta_cost_without_transit_ = std::numeric_limits<int64_t>::max();
3337 return delta_cost_without_transit_ <= objective_max;
3340int64_t LPCumulFilter::GetAcceptedObjectiveValue() const {
3341 return delta_cost_without_transit_;
3344void LPCumulFilter::OnSynchronize(const Assignment* ) {
3347 const RoutingModel& model = *lp_optimizer_.dimension()->model();
3348 const auto& next_accessor = [this, &model](int64_t index) {
3349 return IsVarSynced(index) ? Value(index)
3350 : model.IsStart(index) ? model.End(model.VehicleIndex(index))
3354 if (!filter_objective_cost_) {
3355 synchronized_cost_without_transit_ = 0;
3359 ? lp_optimizer_.ComputeCumulCostWithoutFixedTransits(
3360 next_accessor, &synchronized_cost_without_transit_)
3361 : lp_optimizer_.ComputeCumuls(next_accessor, {}, nullptr, nullptr,
3363 if (status == DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY) {
3364 status = filter_objective_cost_
3365 ? mp_optimizer_.ComputeCumulCostWithoutFixedTransits(
3366 next_accessor, &synchronized_cost_without_transit_)
3367 : mp_optimizer_.ComputeCumuls(next_accessor, {}, nullptr,
3370 if (status == DimensionSchedulingStatus::INFEASIBLE) {
3373 synchronized_cost_without_transit_ = 0;
3377int64_t LPCumulFilter::GetSynchronizedObjectiveValue() const {
3378 return synchronized_cost_without_transit_;
3386 DCHECK_NE(lp_optimizer, nullptr);
3387 DCHECK_NE(mp_optimizer, nullptr);
3390 model.Nexts(), lp_optimizer, mp_optimizer, filter_objective_cost));
3395using ResourceGroup = RoutingModel::ResourceGroup;
3397class ResourceGroupAssignmentFilter : public BasePathFilter {
3399 ResourceGroupAssignmentFilter(const std::vector<IntVar*>& nexts,
3400 const ResourceGroup* resource_group,
3401 LocalDimensionCumulOptimizer* lp_optimizer,
3402 LocalDimensionCumulOptimizer* mp_optimizer,
3403 bool filter_objective_cost);
3404 bool InitializeAcceptPath() override;
3405 bool AcceptPath(int64_t path_start, int64_t, int64_t) override;
3406 bool FinalizeAcceptPath(int64_t objective_min,
3407 int64_t objective_max) override;
3408 void OnBeforeSynchronizePaths(bool synchronizing_all_paths) override;
3409 void OnSynchronizePathFromStart(int64_t start) override;
3410 void OnAfterSynchronizePaths() override;
3412 int64_t GetAcceptedObjectiveValue() const override {
3413 return lns_detected() ? 0 : delta_cost_without_transit_;
3415 int64_t GetSynchronizedObjectiveValue() const override {
3416 return synchronized_cost_without_transit_;
3418 std::string DebugString() const override {
3419 return "ResourceGroupAssignmentFilter(" + dimension_.name() + ")";
3423 using RCIndex = RoutingModel::ResourceClassIndex;
3425 bool VehicleRequiresResourceAssignment(
3426 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
3428 int64_t ComputeRouteCumulCostWithoutResourceAssignment(
3429 int vehicle, const std::function<int64_t(int64_t)>& next_accessor) const;
3431 const RoutingModel& model_;
3432 const RoutingDimension& dimension_;
3433 const ResourceGroup& resource_group_;
3434 LocalDimensionCumulOptimizer* lp_optimizer_;
3435 LocalDimensionCumulOptimizer* mp_optimizer_;
3436 const bool filter_objective_cost_;
3437 bool current_synch_failed_;
3438 int64_t synchronized_cost_without_transit_;
3439 int64_t delta_cost_without_transit_;
3440 std::vector<std::vector<int64_t>> vehicle_to_resource_class_assignment_costs_;
3441 std::vector<int> vehicles_requiring_resource_assignment_;
3442 std::vector<bool> vehicle_requires_resource_assignment_;
3443 std::vector<std::vector<int64_t>>
3444 delta_vehicle_to_resource_class_assignment_costs_;
3445 std::vector<int> delta_vehicles_requiring_resource_assignment_;
3446 std::vector<bool> delta_vehicle_requires_resource_assignment_;
3448 std::vector<int> bound_resource_index_of_vehicle_;
3449 util_intops::StrongVector<RCIndex, absl::flat_hash_set<int>>
3450 ignored_resources_per_class_;
3453ResourceGroupAssignmentFilter::ResourceGroupAssignmentFilter(
3454 const std::vector<IntVar*>& nexts, const ResourceGroup* resource_group,
3455 LocalDimensionCumulOptimizer* lp_optimizer,
3456 LocalDimensionCumulOptimizer* mp_optimizer, bool filter_objective_cost)
3457 : BasePathFilter(nexts, lp_optimizer->dimension()->cumuls().size(),
3458 lp_optimizer->dimension()->model()->GetPathsMetadata()),
3459 model_(*lp_optimizer->dimension()->model()),
3460 dimension_(*lp_optimizer->dimension()),
3461 resource_group_(*resource_group),
3462 lp_optimizer_(lp_optimizer),
3463 mp_optimizer_(mp_optimizer),
3464 filter_objective_cost_(filter_objective_cost),
3465 current_synch_failed_(false),
3466 synchronized_cost_without_transit_(-1),
3467 delta_cost_without_transit_(-1) {
3468 vehicle_to_resource_class_assignment_costs_.resize(model_.vehicles());
3469 delta_vehicle_to_resource_class_assignment_costs_.resize(model_.vehicles());
3472bool ResourceGroupAssignmentFilter::InitializeAcceptPath() {
3473 delta_vehicle_to_resource_class_assignment_costs_.assign(model_.vehicles(),
3475 if (current_synch_failed_) {
3480 int num_used_vehicles = 0;
3481 const int num_resources = resource_group_.Size();
3482 for (int v : resource_group_.GetVehiclesRequiringAResource()) {
3483 if (GetNext(model_.Start(v)) != model_.End(v) ||
3484 model_.IsVehicleUsedWhenEmpty(v)) {
3485 if (++num_used_vehicles > num_resources) {
3490 delta_vehicle_requires_resource_assignment_ =
3491 vehicle_requires_resource_assignment_;
3495bool ResourceGroupAssignmentFilter::AcceptPath(int64_t path_start, int64_t,
3497 if (current_synch_failed_) {
3500 const int vehicle = model_.VehicleIndex(path_start);
3501 bool is_infeasible = false;
3502 delta_vehicle_requires_resource_assignment_[vehicle] =
3503 VehicleRequiresResourceAssignment(
3504 vehicle, [this](int64_t n) { return GetNext(n); }, &is_infeasible);
3508bool ResourceGroupAssignmentFilter::FinalizeAcceptPath(
3509 int64_t , int64_t objective_max) {
3510 delta_cost_without_transit_ = 0;
3511 if (current_synch_failed_) {
3514 const auto& next_accessor = [this](int64_t index) { return GetNext(index); };
3515 delta_vehicles_requiring_resource_assignment_.clear();
3518 for (int v = 0; v < model_.vehicles(); ++v) {
3519 if (delta_vehicle_requires_resource_assignment_[v]) {
3520 delta_vehicles_requiring_resource_assignment_.push_back(v);
3524 int64_t start = model_.Start(v);
3525 if (PathStartTouched(start)) {
3527 ComputeRouteCumulCostWithoutResourceAssignment(v, next_accessor);
3531 } else if (IsVarSynced(start)) {
3532 DCHECK_EQ(vehicle_to_resource_class_assignment_costs_[v].size(), 1);
3533 route_cost = vehicle_to_resource_class_assignment_costs_[v][0];
3535 CapAddTo(route_cost, &delta_cost_without_transit_);
3536 if (delta_cost_without_transit_ > objective_max) {
3542 for (int64_t start : GetTouchedPathStarts()) {
3543 const int vehicle = model_.VehicleIndex(start);
3544 if (!delta_vehicle_requires_resource_assignment_[vehicle]) {
3549 vehicle, 1.0, resource_group_,
3550 ignored_resources_per_class_, next_accessor,
3551 dimension_.transit_evaluator(vehicle), filter_objective_cost_,
3552 lp_optimizer_, mp_optimizer_,
3553 &delta_vehicle_to_resource_class_assignment_costs_[vehicle],
3559 delta_vehicles_requiring_resource_assignment_,
3560 resource_group_.GetResourceIndicesPerClass(),
3561 ignored_resources_per_class_,
3564 return PathStartTouched(model_.Start(v))
3565 ? &delta_vehicle_to_resource_class_assignment_costs_[v]
3566 : &vehicle_to_resource_class_assignment_costs_[v];
3569 CapAddTo(assignment_cost, &delta_cost_without_transit_);
3570 return assignment_cost >= 0 && delta_cost_without_transit_ <= objective_max;
3573void ResourceGroupAssignmentFilter::OnBeforeSynchronizePaths(bool) {
3574 if (!HasAnySyncedPath()) {
3575 vehicle_to_resource_class_assignment_costs_.assign(model_.vehicles(), {});
3577 bound_resource_index_of_vehicle_.assign(model_.vehicles(), -1);
3578 vehicles_requiring_resource_assignment_.clear();
3579 vehicles_requiring_resource_assignment_.reserve(
3580 resource_group_.GetVehiclesRequiringAResource().size());
3581 vehicle_requires_resource_assignment_.assign(model_.vehicles(), false);
3582 ignored_resources_per_class_.assign(resource_group_.GetResourceClassesCount(),
3583 absl::flat_hash_set<int>());
3585 for (int v : resource_group_.GetVehiclesRequiringAResource()) {
3586 const int64_t start = model_.Start(v);
3587 if (!IsVarSynced(start)) {
3590 vehicle_requires_resource_assignment_[v] =
3591 VehicleRequiresResourceAssignment(
3592 v, [this](int64_t n) { return Value(n); }, ¤t_synch_failed_);
3593 if (vehicle_requires_resource_assignment_[v]) {
3594 vehicles_requiring_resource_assignment_.push_back(v);
3596 if (current_synch_failed_) {
3600 synchronized_cost_without_transit_ = 0;
3603void ResourceGroupAssignmentFilter::OnSynchronizePathFromStart(int64_t start) {
3604 if (current_synch_failed_) return;
3605 DCHECK(IsVarSynced(start));
3606 const int v = model_.VehicleIndex(start);
3607 const auto& next_accessor = [this](int64_t index) {
3608 return IsVarSynced(index) ? Value(index) : -1;
3610 if (!vehicle_requires_resource_assignment_[v]) {
3611 const int64_t route_cost =
3612 ComputeRouteCumulCostWithoutResourceAssignment(v, next_accessor);
3614 current_synch_failed_ = true;
3617 CapAddTo(route_cost, &synchronized_cost_without_transit_);
3618 vehicle_to_resource_class_assignment_costs_[v] = {route_cost};
3629 ignored_resources_per_class_, next_accessor,
3630 dimension_.transit_evaluator(v), filter_objective_cost_,
3631 lp_optimizer_, mp_optimizer_,
3632 &vehicle_to_resource_class_assignment_costs_[v], nullptr, nullptr)) {
3633 vehicle_to_resource_class_assignment_costs_[v].assign(
3634 resource_group_.GetResourceClassesCount(), -1);
3635 current_synch_failed_ = true;
3639void ResourceGroupAssignmentFilter::OnAfterSynchronizePaths() {
3640 if (current_synch_failed_) {
3641 synchronized_cost_without_transit_ = 0;
3644 if (!filter_objective_cost_) {
3645 DCHECK_EQ(synchronized_cost_without_transit_, 0);
3649 vehicles_requiring_resource_assignment_,
3650 resource_group_.GetResourceIndicesPerClass(),
3651 ignored_resources_per_class_,
3652 [this](int v) { return &vehicle_to_resource_class_assignment_costs_[v]; },
3654 if (assignment_cost < 0) {
3655 synchronized_cost_without_transit_ = 0;
3656 current_synch_failed_ = true;
3658 DCHECK_GE(synchronized_cost_without_transit_, 0);
3659 CapAddTo(assignment_cost, &synchronized_cost_without_transit_);
3663bool ResourceGroupAssignmentFilter::VehicleRequiresResourceAssignment(
3664 int vehicle, const std::function<int64_t(int64_t)>& next_accessor,
3667 if (!resource_group_.VehicleRequiresAResource(vehicle)) return false;
3668 const IntVar* res_var = model_.ResourceVar(vehicle, resource_group_.Index());
3669 if (!model_.IsVehicleUsedWhenEmpty(vehicle) &&
3670 next_accessor(model_.Start(vehicle)) == model_.End(vehicle)) {
3671 if (res_var->Bound() && res_var->Value() >= 0) {
3680 const int res = res_var->Value();
3685 bound_resource_index_of_vehicle_[vehicle] = res;
3686 const RCIndex rc = resource_group_.GetResourceClassIndex(res);
3687 ignored_resources_per_class_[rc].insert(res);
3696ResourceGroupAssignmentFilter::ComputeRouteCumulCostWithoutResourceAssignment(
3697 int vehicle, const std::function<int64_t(int64_t)>& next_accessor) const {
3698 if (next_accessor(model_.Start(vehicle)) == model_.End(vehicle) &&
3699 !model_.IsVehicleUsedWhenEmpty(vehicle)) {
3702 using Resource = RoutingModel::ResourceGroup::Resource;
3703 const Resource* resource = nullptr;
3704 if (resource_group_.VehicleRequiresAResource(vehicle)) {
3705 DCHECK_GE(bound_resource_index_of_vehicle_[vehicle], 0);
3707 &resource_group_.GetResource(bound_resource_index_of_vehicle_[vehicle]);
3711 lp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
3712 vehicle, 1.0, next_accessor, resource,
3713 filter_objective_cost_ ? &route_cost : nullptr);
3715 case DimensionSchedulingStatus::INFEASIBLE:
3717 case DimensionSchedulingStatus::RELAXED_OPTIMAL_ONLY:
3718 if (mp_optimizer_->ComputeRouteCumulCostWithoutFixedTransits(
3719 vehicle, 1.0, next_accessor, resource,
3720 filter_objective_cost_ ? &route_cost : nullptr) ==
3721 DimensionSchedulingStatus::INFEASIBLE) {
3726 DCHECK(status == DimensionSchedulingStatus::OPTIMAL ||
3727 status == DimensionSchedulingStatus::FEASIBLE);
3733class ResourceAssignmentFilter : public LocalSearchFilter {
3735 ResourceAssignmentFilter(const std::vector<IntVar*>& nexts,
3736 LocalDimensionCumulOptimizer* optimizer,
3737 LocalDimensionCumulOptimizer* mp_optimizer,
3738 bool propagate_own_objective_value,
3739 bool filter_objective_cost);
3740 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3741 int64_t objective_min, int64_t objective_max) override;
3742 void Synchronize(const Assignment* assignment,
3743 const Assignment* delta) override;
3745 int64_t GetAcceptedObjectiveValue() const override {
3746 return propagate_own_objective_value_ ? delta_cost_ : 0;
3748 int64_t GetSynchronizedObjectiveValue() const override {
3749 return propagate_own_objective_value_ ? synchronized_cost_ : 0;
3751 std::string DebugString() const override {
3752 return "ResourceAssignmentFilter(" + dimension_name_ + ")";
3756 std::vector<IntVarLocalSearchFilter*> resource_group_assignment_filters_;
3757 int64_t synchronized_cost_;
3759 const bool propagate_own_objective_value_;
3760 const std::string dimension_name_;
3763ResourceAssignmentFilter::ResourceAssignmentFilter(
3764 const std::vector<IntVar*>& nexts,
3765 LocalDimensionCumulOptimizer* lp_optimizer,
3766 LocalDimensionCumulOptimizer* mp_optimizer,
3767 bool propagate_own_objective_value, bool filter_objective_cost)
3768 : propagate_own_objective_value_(propagate_own_objective_value),
3769 dimension_name_(lp_optimizer->dimension()->name()) {
3770 const RoutingModel& model = *lp_optimizer->dimension()->model();
3771 for (const auto& resource_group : model.GetResourceGroups()) {
3772 resource_group_assignment_filters_.push_back(
3773 model.solver()->RevAlloc(new ResourceGroupAssignmentFilter(
3774 nexts, resource_group.get(), lp_optimizer, mp_optimizer,
3775 filter_objective_cost)));
3779bool ResourceAssignmentFilter::Accept(const Assignment* delta,
3780 const Assignment* deltadelta,
3784 for (LocalSearchFilter* group_filter : resource_group_assignment_filters_) {
3785 if (!group_filter->Accept(delta, deltadelta, objective_min,
3790 std::max(delta_cost_, group_filter->GetAcceptedObjectiveValue());
3791 DCHECK_LE(delta_cost_, objective_max)
3792 << "ResourceGroupAssignmentFilter should return false when the "
3793 "objective_max is exceeded.";
3798void ResourceAssignmentFilter::Synchronize(const Assignment* assignment,
3799 const Assignment* delta) {
3801 for (LocalSearchFilter* group_filter : resource_group_assignment_filters_) {
3802 group_filter->Synchronize(assignment, delta);
3803 synchronized_cost_ = std::max(
3804 synchronized_cost_, group_filter->GetSynchronizedObjectiveValue());
3813 bool propagate_own_objective_value, bool filter_objective_cost) {
3815 DCHECK_NE(lp_optimizer, nullptr);
3816 DCHECK_NE(mp_optimizer, nullptr);
3817 return model.solver()->RevAlloc(new ResourceAssignmentFilter(
3818 model.Nexts(), lp_optimizer, mp_optimizer, propagate_own_objective_value,
3836class CPFeasibilityFilter : public IntVarLocalSearchFilter {
3838 explicit CPFeasibilityFilter(RoutingModel* routing_model);
3839 ~CPFeasibilityFilter() override = default;
3840 std::string DebugString() const override { return "CPFeasibilityFilter"; }
3841 bool Accept(const Assignment* delta, const Assignment* deltadelta,
3842 int64_t objective_min, int64_t objective_max) override;
3843 void OnSynchronize(const Assignment* delta) override;
3846 void AddDeltaToAssignment(const Assignment* delta, Assignment* assignment);
3848 static const int64_t kUnassigned;
3849 const RoutingModel* const model_;
3851 Assignment* const assignment_;
3852 Assignment* const temp_assignment_;
3853 DecisionBuilder* const restore_;
3854 SearchLimit* const limit_;
3857const int64_t CPFeasibilityFilter::kUnassigned = -1;
3859CPFeasibilityFilter::CPFeasibilityFilter(RoutingModel* routing_model)
3860 : IntVarLocalSearchFilter(routing_model->Nexts()),
3862 solver_(routing_model->solver()),
3863 assignment_(solver_->MakeAssignment()),
3864 temp_assignment_(solver_->MakeAssignment()),
3865 restore_(solver_->MakeRestoreAssignment(temp_assignment_)),
3866 limit_(solver_->MakeCustomLimit(
3867 [routing_model]() { return routing_model->CheckLimit(); })) {
3868 assignment_->Add(routing_model->Nexts());
3871bool CPFeasibilityFilter::Accept(const Assignment* delta,
3875 temp_assignment_->Copy(assignment_);
3876 AddDeltaToAssignment(delta, temp_assignment_);
3878 return solver_->Solve(restore_, limit_);
3881void CPFeasibilityFilter::OnSynchronize(const Assignment* delta) {
3882 AddDeltaToAssignment(delta, assignment_);
3885void CPFeasibilityFilter::AddDeltaToAssignment(const Assignment* delta,
3886 Assignment* assignment) {
3890 Assignment::IntContainer* const container =
3891 assignment->MutableIntVarContainer();
3892 for (const IntVarElement& delta_element :
3893 delta->IntVarContainer().elements()) {
3894 IntVar* const var = delta_element.Var();
3895 int64_t index = kUnassigned;
3898 if (!FindIndex(var, &index)) continue;
3899 DCHECK_EQ(var, Var(index));
3900 const int64_t value = delta_element.Value();
3902 container->AddAtPosition(var, index)->SetValue(value);
3903 if (model_->IsStart(index)) {
3904 if (model_->IsEnd(value)) {
3906 container->MutableElement(index)->Deactivate();
3909 container->MutableElement(index)->Activate();
3923 std::vector<int> path_end)
3925 num_paths_(path_start.size()),
3926 num_nodes_threshold_(std::max(16, 4 * num_nodes_))
3928 DCHECK_EQ(path_start.size(), num_paths_);
3929 DCHECK_EQ(path_end.size(), num_paths_);
3930 for (int p = 0; p < num_paths_; ++p) {
3931 path_start_end_.push_back({path_start[p], path_end[p]});
3939 committed_index_.assign(num_nodes_, -1);
3940 committed_paths_.assign(num_nodes_, kUnassigned);
3941 committed_nodes_.assign(2 * num_paths_, -1);
3942 chains_.assign(num_paths_ + 1, {-1, -1});
3943 paths_.assign(num_paths_, {-1, -1});
3944 for (int path = 0; path < num_paths_; ++path) {
3945 const int index = 2 * path;
3946 const auto& [start, end] = path_start_end_[path];
3947 committed_index_[start] = index;
3948 committed_index_[end] = index + 1;
3950 committed_nodes_[index] = start;
3951 committed_nodes_[index + 1] = end;
3953 committed_paths_[start] = path;
3954 committed_paths_[end] = path;
3956 chains_[path] = {index, index + 2};
3957 paths_[path] = {path, path + 1};
3959 chains_[num_paths_] = {0, 0};
3962 for (int node = 0; node < num_nodes_; ++node) {
3963 if (committed_index_[node] != -1) continue;
3964 committed_index_[node] = committed_nodes_.size();
3970 const PathBounds bounds = paths_[path];
3972 chains_.data() + bounds.end_index,
3977 const PathBounds bounds = paths_[path];
3979 chains_.data() + bounds.end_index,
3984 changed_paths_.push_back(path);
3985 const int path_begin_index = chains_.size();
3986 chains_.insert(chains_.end(), chains.begin(), chains.end());
3987 const int path_end_index = chains_.size();
3988 paths_[path] = {path_begin_index, path_end_index};
3993 for (const int loop : new_loops) {
3996 if (Path(loop) == kLoop) continue;
4012 chains_.resize(num_paths_ + 1);
4013 for (const int path : changed_paths_) {
4020void PathState::CopyNewPathAtEndOfNodes(int path) {
4022 const PathBounds path_bounds = paths_[path];
4023 for (int i = path_bounds.begin_index; i < path_bounds.end_index; ++i) {
4024 const ChainBounds chain_bounds = chains_[i];
4025 committed_nodes_.insert(committed_nodes_.end(),
4026 committed_nodes_.data() + chain_bounds.begin_index,
4027 committed_nodes_.data() + chain_bounds.end_index);
4028 if (committed_paths_[committed_nodes_.back()] == path) continue;
4029 for (int i = chain_bounds.begin_index; i < chain_bounds.end_index; ++i) {
4030 const int node = committed_nodes_[i];
4031 committed_paths_[node] = path;
4038void PathState::IncrementalCommit() {
4039 const int new_nodes_begin = committed_nodes_.size();
4041 const int chain_begin = committed_nodes_.size();
4042 CopyNewPathAtEndOfNodes(path);
4043 const int chain_end = committed_nodes_.size();
4044 chains_[path] = {chain_begin, chain_end};
4047 const int new_nodes_end = committed_nodes_.size();
4048 for (int i = new_nodes_begin; i < new_nodes_end; ++i) {
4049 const int node = committed_nodes_[i];
4050 committed_index_[node] = i;
4055 committed_paths_[loop] = kLoop;
4061void PathState::FullCommit() {
4064 const int old_num_nodes = committed_nodes_.size();
4065 for (int path = 0; path < num_paths_; ++path) {
4066 const int new_path_begin = committed_nodes_.size() - old_num_nodes;
4067 CopyNewPathAtEndOfNodes(path);
4068 const int new_path_end = committed_nodes_.size() - old_num_nodes;
4069 chains_[path] = {new_path_begin, new_path_end};
4071 committed_nodes_.erase(committed_nodes_.begin(),
4072 committed_nodes_.begin() + old_num_nodes);
4075 constexpr int kUnindexed = -1;
4076 committed_index_.assign(num_nodes_, kUnindexed);
4078 for (const int node : committed_nodes_) {
4079 committed_index_[node] = index++;
4081 for (int node = 0; node < num_nodes_; ++node) {
4082 if (committed_index_[node] != kUnindexed) continue;
4083 committed_index_[node] = index++;
4084 committed_nodes_.push_back(node);
4087 committed_paths_[loop] = kLoop;
4097 std::string DebugString() const override { return "PathStateFilter"; }
4098 PathStateFilter(std::unique_ptr<PathState> path_state,
4099 const std::vector<IntVar*>& nexts);
4100 void Relax(const Assignment* delta, const Assignment* deltadelta) override;
4101 bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
4104 void Synchronize(const Assignment*, const Assignment*) override {};
4105 void Commit(const Assignment* assignment, const Assignment* delta) override;
4118 bool operator<(const IndexArc& other) const { return index < other.index; }
4125 void MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
4128 void MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
4130 const std::unique_ptr<PathState> path_state_;
4132 std::vector<int> variable_index_to_node_;
4136 std::vector<int> changed_paths_;
4137 std::vector<bool> path_has_changed_;
4138 std::vector<std::pair<int, int>> changed_arcs_;
4139 std::vector<int> changed_loops_;
4140 std::vector<TailHeadIndices> tail_head_indices_;
4141 std::vector<IndexArc> arcs_by_tail_index_;
4142 std::vector<IndexArc> arcs_by_head_index_;
4143 std::vector<int> next_arc_;
4144 std::vector<PathState::ChainBounds> path_chains_;
4147PathStateFilter::PathStateFilter(std::unique_ptr<PathState> path_state,
4148 const std::vector<IntVar*>& nexts)
4149 : path_state_(std::move(path_state)) {
4151 int min_index = std::numeric_limits<int>::max();
4152 int max_index = std::numeric_limits<int>::min();
4153 for (const IntVar* next : nexts) {
4154 const int index = next->index();
4155 min_index = std::min<int>(min_index, index);
4156 max_index = std::max<int>(max_index, index);
4158 variable_index_to_node_.resize(max_index - min_index + 1, -1);
4159 index_offset_ = min_index;
4162 for (int node = 0; node < nexts.size(); ++node) {
4163 const int index = nexts[node]->index() - index_offset_;
4164 variable_index_to_node_[index] = node;
4166 path_has_changed_.assign(path_state_->NumPaths(), false);
4172 for (const IntVarElement& var_value : delta->IntVarContainer().elements()) {
4173 if (var_value.Var() == nullptr) continue;
4174 const int index = var_value.Var()->index() - index_offset_;
4175 if (index < 0 || variable_index_to_node_.size() <= index) continue;
4176 const int node = variable_index_to_node_[index];
4177 if (node == -1) continue;
4179 changed_arcs_.emplace_back(node, var_value.Value());
4182 path_state_->SetInvalid();
4189void PathStateFilter::Reset() { path_state_->Reset(); }
4194void PathStateFilter::Commit(const Assignment* assignment,
4195 const Assignment* delta) {
4197 if (delta == nullptr || delta->Empty()) {
4198 Relax(assignment, nullptr);
4205void PathStateFilter::Revert() { path_state_->Revert(); }
4207void PathStateFilter::CutChains() {
4211 for (const int path : changed_paths_) path_has_changed_[path] = false;
4213 tail_head_indices_.clear();
4215 int num_changed_arcs = 0;
4216 for (const auto [node, next] : changed_arcs_) {
4217 const int node_index = path_state_->CommittedIndex(node);
4218 const int next_index = path_state_->CommittedIndex(next);
4219 const int node_path = path_state_->Path(node);
4221 (next_index != node_index + 1 || node_path < 0)) {
4222 tail_head_indices_.push_back({node_index, next_index});
4223 changed_arcs_[num_changed_arcs++] = {node, next};
4224 if (node_path >= 0 && !path_has_changed_[node_path]) {
4225 path_has_changed_[node_path] = true;
4226 changed_paths_.push_back(node_path);
4228 } else if (node == next && node_path != PathState::kLoop) {
4229 changed_loops_.push_back(node);
4232 changed_arcs_.resize(num_changed_arcs);
4234 path_state_->ChangeLoops(changed_loops_);
4235 if (tail_head_indices_.size() + changed_paths_.size() <= 8) {
4236 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm();
4238 MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm();
4243 MakeChainsFromChangedPathsAndArcsWithSelectionAlgorithm() {
4244 int num_visited_changed_arcs = 0;
4245 const int num_changed_arcs = tail_head_indices_.size();
4247 for (const int path : changed_paths_) {
4249 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
4250 int current_index = start_index;
4255 int selected_tail_index = std::numeric_limits<int>::max();
4256 for (int i = num_visited_changed_arcs; i < num_changed_arcs; ++i) {
4257 const int tail_index = tail_head_indices_[i].tail_index;
4258 if (current_index <= tail_index && tail_index < selected_tail_index) {
4259 selected_arc = i;
4260 selected_tail_index = tail_index;
4268 if (start_index <= current_index && current_index < end_index &&
4269 end_index <= selected_tail_index) {
4270 path_chains_.emplace_back(current_index, end_index);
4273 path_chains_.emplace_back(current_index, selected_tail_index + 1);
4274 current_index = tail_head_indices_[selected_arc].head_index;
4275 std::swap(tail_head_indices_[num_visited_changed_arcs],
4276 tail_head_indices_[selected_arc]);
4277 ++num_visited_changed_arcs;
4280 path_state_->ChangePath(path, path_chains_);
4284void PathStateFilter::MakeChainsFromChangedPathsAndArcsWithGenericAlgorithm() {
4300 for (const int path : changed_paths_) {
4301 const auto [start_index, end_index] = path_state_->CommittedPathRange(path);
4302 tail_head_indices_.push_back({end_index - 1, start_index});
4307 const int num_arc_indices = tail_head_indices_.size();
4308 arcs_by_tail_index_.resize(num_arc_indices);
4309 arcs_by_head_index_.resize(num_arc_indices);
4310 for (int i = 0; i < num_arc_indices; ++i) {
4311 arcs_by_tail_index_[i] = {tail_head_indices_[i].tail_index, i};
4312 arcs_by_head_index_[i] = {tail_head_indices_[i].head_index, i};
4314 std::sort(arcs_by_tail_index_.begin(), arcs_by_tail_index_.end());
4315 std::sort(arcs_by_head_index_.begin(), arcs_by_head_index_.end());
4317 next_arc_.resize(num_arc_indices);
4318 for (int i = 0; i < num_arc_indices; ++i) {
4319 next_arc_[arcs_by_head_index_[i].arc] = arcs_by_tail_index_[i].arc;
4325 const int first_fake_arc = num_arc_indices - changed_paths_.size();
4326 for (int fake_arc = first_fake_arc; fake_arc < num_arc_indices; ++fake_arc) {
4330 const int chain_begin = tail_head_indices_[arc].head_index;
4332 const int chain_end = tail_head_indices_[arc].tail_index + 1;
4333 path_chains_.emplace_back(chain_begin, chain_end);
4334 } while (arc != fake_arc);
4335 const int path = changed_paths_[fake_arc - first_fake_arc];
4336 path_state_->ChangePath(path, path_chains_);
4343 std::unique_ptr<PathState> path_state,
4344 const std::vector<IntVar*>& nexts) {
4345 PathStateFilter* filter = new PathStateFilter(std::move(path_state), nexts);
4346 return solver->RevAlloc(filter);
4350using EInterval = DimensionChecker::ExtendedInterval;
4352constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
4353constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();
4355EInterval operator&(const EInterval& i1, const EInterval& i2) {
4356 return {std::max(i1.num_negative_infinity == 0 ? i1.min : kint64min,
4357 i2.num_negative_infinity == 0 ? i2.min : kint64min),
4358 std::min(i1.num_positive_infinity == 0 ? i1.max : kint64max,
4359 i2.num_positive_infinity == 0 ? i2.max : kint64max),
4360 std::min(i1.num_negative_infinity, i2.num_negative_infinity),
4361 std::min(i1.num_positive_infinity, i2.num_positive_infinity)};
4364EInterval operator&=(EInterval& i1, const EInterval& i2) {
4369bool IsEmpty(const EInterval& interval) {
4370 const int64_t minimum_value =
4371 interval.num_negative_infinity == 0 ? interval.min : kint64min;
4372 const int64_t maximum_value =
4373 interval.num_positive_infinity == 0 ? interval.max : kint64max;
4374 return minimum_value > maximum_value;
4377EInterval operator+(const EInterval& i1, const EInterval& i2) {
4378 return {CapAdd(i1.min, i2.min), CapAdd(i1.max, i2.max),
4379 i1.num_negative_infinity + i2.num_negative_infinity,
4380 i1.num_positive_infinity + i2.num_positive_infinity};
4383EInterval& operator+=(EInterval& i1, const EInterval& i2) {
4388EInterval operator-(const EInterval& i1, const EInterval& i2) {
4389 return {CapSub(i1.min, i2.max), CapSub(i1.max, i2.min),
4390 i1.num_negative_infinity + i2.num_positive_infinity,
4391 i1.num_positive_infinity + i2.num_negative_infinity};
4396EInterval Delta(const EInterval& from, const EInterval& to) {
4398 to.num_negative_infinity - from.num_negative_infinity,
4399 to.num_positive_infinity - from.num_positive_infinity};
4402EInterval ToExtendedInterval(DimensionChecker::Interval interval) {
4403 const bool is_neg_infinity = interval.min == kint64min;
4404 const bool is_pos_infinity = interval.max == kint64max;
4405 return {is_neg_infinity ? 0 : interval.min,
4406 is_pos_infinity ? 0 : interval.max, is_neg_infinity ? 1 : 0,
4407 is_pos_infinity ? 1 : 0};
4410std::vector<EInterval> ToExtendedIntervals(
4411 absl::Span<const DimensionChecker::Interval> intervals) {
4412 std::vector<EInterval> extended_intervals;
4413 extended_intervals.reserve(intervals.size());
4414 for (const auto& interval : intervals) {
4415 extended_intervals.push_back(ToExtendedInterval(interval));
4417 return extended_intervals;
4422 const PathState* path_state, std::vector<Interval> path_capacity,
4423 std::vector<int> path_class,
4424 std::vector<std::function<Interval(int64_t, int64_t)>>
4426 std::vector<Interval> node_capacity, int min_range_size_for_riq)
4427 : path_state_(path_state),
4428 path_capacity_(ToExtendedIntervals(path_capacity)),
4429 path_class_(std::move(path_class)),
4430 demand_per_path_class_(std::move(demand_per_path_class)),
4431 node_capacity_(ToExtendedIntervals(node_capacity)),
4432 index_(path_state_->NumNodes(), 0),
4433 maximum_riq_layer_size_(std::max(
4434 16, 4 * path_state_->NumNodes())),
4435 min_range_size_for_riq_(min_range_size_for_riq) {
4436 const int num_nodes = path_state_->NumNodes();
4437 cached_demand_.resize(num_nodes);
4438 const int num_paths = path_state_->NumPaths();
4439 DCHECK_EQ(num_paths, path_capacity_.size());
4440 DCHECK_EQ(num_paths, path_class_.size());
4447 if (path_state_->IsInvalid()) return true;
4448 for (const int path : path_state_->ChangedPaths()) {
4449 const EInterval path_capacity = path_capacity_[path];
4450 const int path_class = path_class_[path];
4453 int prev_node = path_state_->Start(path);
4454 EInterval cumul = node_capacity_[prev_node] & path_capacity;
4455 if (IsEmpty(cumul)) return false;
4457 for (const auto chain : path_state_->Chains(path)) {
4458 const int first_node = chain.First();
4459 const int last_node = chain.Last();
4461 if (prev_node != first_node) {
4464 const EInterval demand = ToExtendedInterval(
4465 demand_per_path_class_[path_class](prev_node, first_node));
4468 cumul &= node_capacity_[first_node];
4469 if (IsEmpty(cumul)) return false;
4474 const int first_index = index_[first_node];
4475 const int last_index = index_[last_node];
4476 const int chain_path = path_state_->Path(first_node);
4477 const int chain_path_class =
4478 chain_path < 0 ? -1 : path_class_[chain_path];
4482 const bool chain_is_cached = chain_path_class == path_class;
4483 if (last_index - first_index > min_range_size_for_riq_ &&
4485 UpdateCumulUsingChainRIQ(first_index, last_index, path_capacity, cumul);
4486 if (IsEmpty(cumul)) return false;
4487 prev_node = chain.Last();
4489 for (const int node : chain.WithoutFirstNode()) {
4492 ? cached_demand_[prev_node]
4494 demand_per_path_class_[path_class](prev_node, node));
4496 cumul &= node_capacity_[node];
4508 const int current_layer_size = riq_[0].size();
4509 int change_size = path_state_->ChangedPaths().size();
4510 for (const int path : path_state_->ChangedPaths()) {
4511 for (const auto chain : path_state_->Chains(path)) {
4512 change_size += chain.NumNodes();
4515 if (current_layer_size + change_size <= maximum_riq_layer_size_) {
4522void DimensionChecker::IncrementalCommit() {
4523 for (const int path : path_state_->ChangedPaths()) {
4524 const int begin_index = riq_[0].size();
4525 AppendPathDemandsToSums(path);
4526 UpdateRIQStructure(begin_index, riq_[0].size());
4530void DimensionChecker::FullCommit() {
4532 for (auto& layer : riq_) layer.clear();
4534 const int num_paths = path_state_->NumPaths();
4535 for (int path = 0; path < num_paths; ++path) {
4536 const int begin_index = riq_[0].size();
4537 AppendPathDemandsToSums(path);
4538 UpdateRIQStructure(begin_index, riq_[0].size());
4542void DimensionChecker::AppendPathDemandsToSums(int path) {
4545 const int path_class = path_class_[path];
4546 EInterval demand_sum = {0, 0, 0, 0};
4547 int prev = path_state_->Start(path);
4548 int index = riq_[0].size();
4549 for (const int node : path_state_->Nodes(path)) {
4552 prev == node ? EInterval{0, 0, 0, 0}
4554 demand_per_path_class_[path_class](prev, node));
4556 cached_demand_[prev] = demand;
4560 riq_[0].push_back({.cumuls_to_fst = node_capacity_[node],
4561 .tightest_tsum = demand_sum,
4562 .cumuls_to_lst = node_capacity_[node],
4563 .tsum_at_fst = demand_sum,
4564 .tsum_at_lst = demand_sum});
4566 cached_demand_[path_state_->End(path)] = {0, 0, 0, 0};
4569void DimensionChecker::UpdateRIQStructure(int begin_index, int end_index) {
4574 for (int layer = 1, half_window = 1; layer <= max_layer;
4575 ++layer, half_window *= 2) {
4576 riq_[layer].resize(end_index);
4577 for (int i = begin_index + 2 * half_window - 1; i < end_index; ++i) {
4583 const RIQNode& fw = riq_[layer - 1][i - half_window];
4584 const RIQNode& lw = riq_[layer - 1][i];
4585 const EInterval lst_to_lst = Delta(fw.tsum_at_lst, lw.tsum_at_lst);
4586 const EInterval fst_to_fst = Delta(fw.tsum_at_fst, lw.tsum_at_fst);
4588 riq_[layer][i] = {
4589 .cumuls_to_fst = fw.cumuls_to_fst & lw.cumuls_to_fst - fst_to_fst,
4590 .tightest_tsum = fw.tightest_tsum & lw.tightest_tsum,
4591 .cumuls_to_lst = fw.cumuls_to_lst + lst_to_lst & lw.cumuls_to_lst,
4592 .tsum_at_fst = fw.tsum_at_fst,
4593 .tsum_at_lst = lw.tsum_at_lst};
4602void DimensionChecker::UpdateCumulUsingChainRIQ(
4603 int first_index, int last_index, const ExtendedInterval& path_capacity,
4604 ExtendedInterval& cumul) const {
4605 DCHECK_LE(0, first_index);
4606 DCHECK_LT(first_index, last_index);
4607 DCHECK_LT(last_index, riq_[0].size());
4609 const int window = 1 << layer;
4610 const RIQNode& fw = riq_[layer][first_index + window - 1];
4611 const RIQNode& lw = riq_[layer][last_index];
4614 cumul &= fw.cumuls_to_fst;
4615 cumul &= lw.cumuls_to_fst - Delta(fw.tsum_at_fst, lw.tsum_at_fst);
4617 Delta(fw.tsum_at_fst, fw.tightest_tsum & lw.tightest_tsum);
4620 if (IsEmpty(cumul)) return;
4623 cumul += Delta(fw.tsum_at_fst, lw.tsum_at_lst);
4626 cumul &= fw.cumuls_to_lst + Delta(fw.tsum_at_lst, lw.tsum_at_lst);
4627 cumul &= lw.cumuls_to_lst;
4634 std::string DebugString() const override { return name_; }
4635 DimensionFilter(std::unique_ptr<DimensionChecker> checker,
4636 absl::string_view dimension_name)
4637 : checker_(std::move(checker)),
4638 name_(absl::StrCat("DimensionFilter(", dimension_name, ")")) {}
4640 bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
4641 return checker_->Check();
4644 void Synchronize(const Assignment*, const Assignment*) override {
4649 std::unique_ptr<DimensionChecker> checker_;
4656 Solver* solver, std::unique_ptr<DimensionChecker> checker,
4657 absl::string_view dimension_name) {
4658 DimensionFilter* filter =
4659 new DimensionFilter(std::move(checker), dimension_name);
4660 return solver->RevAlloc(filter);
4664 PathState* path_state, std::vector<PathData> path_data)
4665 : path_state_(path_state), path_data_(std::move(path_data)) {}
4668 for (const int path : path_state_->ChangedPaths()) {
4669 path_data_[path].start_cumul.Relax();
4670 path_data_[path].end_cumul.Relax();
4676 for (const int path : path_state_->ChangedPaths()) {
4677 if (!path_data_[path].span.Exists()) continue;
4678 const PathData& data = path_data_[path];
4680 int64_t lb_span = data.span.Min();
4684 int64_t lb_span_tw = total_transit;
4688 if (!br.is_performed_min) continue;
4689 if (br.start_max < end_min && start_max < br.end_min) {
4690 CapAddTo(br.duration_min, &lb_span_tw);
4691 start_max = std::min(start_max, br.start_max);
4692 end_min = std::max(end_min, br.end_min);
4695 lb_span = std::max({lb_span, lb_span_tw, CapSub(end_min, start_max)});
4698 int64_t break_start_min = kint64max;
4699 int64_t break_end_max = kint64min;
4701 start_min = std::max(start_min, CapSub(end_min, data.span.Max()));
4703 end_max = std::min(end_max, CapAdd(start_max, data.span.Max()));
4704 int num_feasible_breaks = 0;
4706 if (start_min <= br.start_max && br.end_min <= end_max) {
4707 break_start_min = std::min(break_start_min, br.start_min);
4708 break_end_max = std::max(break_end_max, br.end_max);
4716 for (const auto& [max_interbreak, min_break_duration] :
4722 if (max_interbreak == 0) {
4723 if (total_transit > 0) return false;
4727 std::max<int64_t>(0, (total_transit - 1) / max_interbreak);
4728 if (lb_span > max_interbreak) {
4729 min_num_breaks = std::max<int64_t>(min_num_breaks, 1);
4731 if (min_num_breaks > num_feasible_breaks) return false;
4734 CapAdd(total_transit, CapProd(min_num_breaks, min_break_duration)));
4735 if (min_num_breaks > 0) {
4744 if (!data.span.SetMin(lb_span)) return false;
4746 start_max = std::min(start_max, CapSub(end_max, lb_span));
4748 end_min = std::max(end_min, CapAdd(start_min, lb_span));
4758 LightVehicleBreaksFilter(std::unique_ptr<LightVehicleBreaksChecker> checker,
4759 absl::string_view dimension_name)
4760 : checker_(std::move(checker)),
4761 name_(absl::StrCat("LightVehicleBreaksFilter(", dimension_name, ")")) {}
4763 std::string DebugString() const override { return name_; }
4765 void Relax(const Assignment*, const Assignment*) override {
4769 bool Accept(const Assignment*, const Assignment*, int64_t, int64_t) override {
4770 return checker_->Check();
4773 void Synchronize(const Assignment*, const Assignment*) override {
4778 std::unique_ptr<LightVehicleBreaksChecker> checker_;
4785 Solver* solver, std::unique_ptr<LightVehicleBreaksChecker> checker,
4786 absl::string_view dimension_name) {
4787 LightVehicleBreaksFilter* filter =
4788 new LightVehicleBreaksFilter(std::move(checker), dimension_name);
4789 return solver->RevAlloc(filter);
4801 const int begin_index = tree_location_.size();
4802 const int end_index = elements_.size();
4803 DCHECK_LE(begin_index, end_index);
4804 if (begin_index >= end_index) return;
4811 const int old_node_size = nodes_.size();
4812 for (int i = begin_index; i < end_index; ++i) {
4813 nodes_.push_back({.pivot_height = elements_[i].height, .pivot_index = -1});
4815 std::sort(nodes_.begin() + old_node_size, nodes_.end());
4816 nodes_.erase(std::unique(nodes_.begin() + old_node_size, nodes_.end()),
4821 const int new_node_size = nodes_.size();
4822 tree_location_.resize(end_index, {.node_begin = old_node_size,
4823 .node_end = new_node_size,
4824 .sequence_first = begin_index});
4830 if (tree_layers_.size() <= num_layers) tree_layers_.resize(num_layers);
4831 for (int l = 0; l < num_layers; ++l) {
4832 tree_layers_[l].resize(end_index,
4833 {.prefix_sum = 0, .left_index = -1, .is_left = 0});
4838 const auto fill_subtree = [this](auto& fill_subtree, int layer,
4839 int node_begin, int node_end,
4840 int range_begin, int range_end) {
4841 DCHECK_LT(node_begin, node_end);
4842 DCHECK_LT(range_begin, range_end);
4845 for (int i = range_begin; i < range_end; ++i) {
4846 sum += elements_[i].weight;
4847 tree_layers_[layer][i].prefix_sum = sum;
4849 if (node_begin + 1 == node_end) return;
4854 const int node_mid = node_begin + (node_end - node_begin) / 2;
4855 const int64_t pivot_height = nodes_[node_mid].pivot_height;
4856 int pivot_index = range_begin;
4857 for (int i = range_begin; i < range_end; ++i) {
4858 tree_layers_[layer][i].left_index = pivot_index;
4859 tree_layers_[layer][i].is_left = elements_[i].height < pivot_height;
4860 if (elements_[i].height < pivot_height) ++pivot_index;
4862 nodes_[node_mid].pivot_index = pivot_index;
4866 elements_.begin() + range_begin, elements_.begin() + range_end,
4867 [pivot_height](const auto& el) { return el.height < pivot_height; });
4869 fill_subtree(fill_subtree, layer + 1, node_begin, node_mid, range_begin,
4871 fill_subtree(fill_subtree, layer + 1, node_mid, node_end, pivot_index,
4874 fill_subtree(fill_subtree, 0, old_node_size, new_node_size, begin_index,
4881 DCHECK_LE(begin_index, end_index);
4882 DCHECK_LE(end_index, tree_location_.size());
4883 DCHECK_EQ(tree_location_.size(), elements_.size());
4884 if (begin_index >= end_index) return 0;
4885 auto [node_begin, node_end, sequence_first_index] =
4886 tree_location_[begin_index];
4887 DCHECK_EQ(tree_location_[end_index - 1].sequence_first,
4890 .range_first_index = begin_index,
4891 .range_last_index = end_index - 1,
4892 .range_first_is_node_first = begin_index == sequence_first_index};
4894 if (nodes_[node_end - 1].pivot_height < threshold_height) return 0;
4897 int64_t min_height_of_current_node = nodes_[node_begin].pivot_height;
4898 for (int l = 0; !range.Empty(); ++l) {
4899 const ElementInfo* elements = tree_layers_[l].data();
4900 if (threshold_height <= min_height_of_current_node) {
4903 sum += range.Sum(elements);
4905 } else if (node_begin + 1 == node_end) {
4910 const int node_mid = node_begin + (node_end - node_begin) / 2;
4911 const auto [pivot_height, pivot_index] = nodes_[node_mid];
4912 const ElementRange right = range.RightSubRange(elements, pivot_index);
4913 if (threshold_height < pivot_height) {
4916 if (!right.Empty()) sum += right.Sum(tree_layers_[l + 1].data());
4918 range = range.LeftSubRange(elements);
4931 const PathState* path_state, std::vector<int64_t> force_start_min,
4932 std::vector<int64_t> force_end_min, std::vector<int> force_class,
4933 std::vector<const std::function<int64_t(int64_t)>*> force_per_class,
4934 std::vector<int> distance_class,
4935 std::vector<const std::function<int64_t(int64_t, int64_t)>*>
4937 std::vector<EnergyCost> path_energy_cost,
4938 std::vector<bool> path_has_cost_when_empty)
4939 : path_state_(path_state),
4940 force_start_min_(std::move(force_start_min)),
4941 force_end_min_(std::move(force_end_min)),
4942 force_class_(std::move(force_class)),
4943 distance_class_(std::move(distance_class)),
4944 force_per_class_(std::move(force_per_class)),
4945 distance_per_class_(std::move(distance_per_class)),
4946 path_energy_cost_(std::move(path_energy_cost)),
4947 path_has_cost_when_empty_(std::move(path_has_cost_when_empty)),
4948 maximum_range_query_size_(4 * path_state_->NumNodes()),
4949 force_rmq_index_of_node_(path_state_->NumNodes()),
4950 threshold_query_index_of_node_(path_state_->NumNodes()) {
4951 const int num_nodes = path_state_->NumNodes();
4952 cached_force_.resize(num_nodes);
4953 cached_distance_.resize(num_nodes);
4954 FullCacheAndPrecompute();
4955 committed_total_cost_ = 0;
4956 committed_path_cost_.assign(path_state_->NumPaths(), 0);
4957 const int num_paths = path_state_->NumPaths();
4958 for (int path = 0; path < num_paths; ++path) {
4959 committed_path_cost_[path] = ComputePathCost(path);
4960 CapAddTo(committed_path_cost_[path], &committed_total_cost_);
4966 if (path_state_->IsInvalid()) return true;
4967 accepted_total_cost_ = committed_total_cost_;
4968 for (const int path : path_state_->ChangedPaths()) {
4970 CapSub(accepted_total_cost_, committed_path_cost_[path]);
4971 CapAddTo(ComputePathCost(path), &accepted_total_cost_);
4972 if (accepted_total_cost_ == kint64max) return false;
4977void PathEnergyCostChecker::CacheAndPrecomputeRangeQueriesOfPath(int path) {
4980 const auto& force_evaluator = *force_per_class_[force_class_[path]];
4981 const auto& distance_evaluator = *distance_per_class_[distance_class_[path]];
4982 int force_index = force_rmq_.TableSize();
4983 int threshold_index = energy_query_.TreeSize();
4986 const int start_node = path_state_->Start(path);
4987 int prev_node = start_node;
4989 for (const int node : path_state_->Nodes(path)) {
4991 const int64_t distance = distance_evaluator(prev_node, node);
4992 cached_distance_[prev_node] = distance;
4993 energy_query_.PushBack(total_force, total_force * distance);
4994 distance_query_.PushBack(total_force, distance);
4997 threshold_query_index_of_node_[node] = threshold_index++;
4998 force_rmq_.PushBack(total_force);
4999 force_rmq_index_of_node_[node] = force_index++;
5000 const int64_t force = force_evaluator(node);
5001 cached_force_[node] = force;
5004 force_rmq_.MakeTableFromNewElements();
5005 energy_query_.MakeTreeFromNewElements();
5006 distance_query_.MakeTreeFromNewElements();
5009void PathEnergyCostChecker::IncrementalCacheAndPrecompute() {
5010 for (const int path : path_state_->ChangedPaths()) {
5011 CacheAndPrecomputeRangeQueriesOfPath(path);
5015void PathEnergyCostChecker::FullCacheAndPrecompute() {
5018 const int num_paths = path_state_->NumPaths();
5019 for (int path = 0; path < num_paths; ++path) {
5020 CacheAndPrecomputeRangeQueriesOfPath(path);
5025 int change_size = path_state_->ChangedPaths().size();
5026 for (const int path : path_state_->ChangedPaths()) {
5027 for (const auto chain : path_state_->Chains(path)) {
5028 change_size += chain.NumNodes();
5031 CapSub(committed_total_cost_, committed_path_cost_[path]);
5032 committed_path_cost_[path] = ComputePathCost(path);
5033 CapAddTo(committed_path_cost_[path], &committed_total_cost_);
5036 const int current_layer_size = force_rmq_.TableSize();
5037 if (current_layer_size + change_size <= maximum_range_query_size_) {
5038 IncrementalCacheAndPrecompute();
5044int64_t PathEnergyCostChecker::ComputePathCost(int64_t path) const {
5045 const int path_force_class = force_class_[path];
5046 const auto& force_evaluator = *force_per_class_[path_force_class];
5049 int64_t total_force = force_start_min_[path];
5050 int64_t min_force = total_force;
5052 int prev_node = path_state_->Start(path);
5053 for (const auto chain : path_state_->Chains(path)) {
5054 num_path_nodes += chain.NumNodes();
5056 if (chain.First() != prev_node) {
5057 const int64_t force_to_node = force_evaluator(prev_node);
5058 CapAddTo(force_to_node, &total_force);
5059 min_force = std::min(min_force, total_force);
5060 prev_node = chain.First();
5064 const int chain_path = path_state_->Path(chain.First());
5065 const int chain_force_class =
5066 chain_path < 0 ? -1 : force_class_[chain_path];
5067 const bool force_is_cached = chain_force_class == path_force_class;
5068 if (force_is_cached && chain.NumNodes() >= 2) {
5069 const int first_index = force_rmq_index_of_node_[chain.First()];
5070 const int last_index = force_rmq_index_of_node_[chain.Last()];
5072 const int64_t first_total_force = force_rmq_.array()[first_index];
5073 const int64_t last_total_force = force_rmq_.array()[last_index];
5074 const int64_t min_total_force =
5075 force_rmq_.RangeMinimum(first_index, last_index);
5077 min_force = std::min(min_force,
5078 total_force - first_total_force + min_total_force);
5079 CapAddTo(last_total_force - first_total_force, &total_force);
5080 prev_node = chain.Last();
5082 for (const int node : chain.WithoutFirstNode()) {
5083 const int64_t force = force_is_cached ? cached_force_[prev_node]
5084 : force_evaluator(prev_node);
5085 CapAddTo(force, &total_force);
5086 min_force = std::min(min_force, total_force);
5091 if (num_path_nodes == 2 && !path_has_cost_when_empty_[path]) return 0;
5097 total_force = std::max<int64_t>(
5098 {0, CapOpp(min_force), CapSub(force_end_min_[path], total_force)});
5099 CapAddTo(force_start_min_[path], &total_force);
5102 const int path_distance_class = distance_class_[path];
5103 const auto& distance_evaluator = *distance_per_class_[path_distance_class];
5104 const EnergyCost& cost = path_energy_cost_[path];
5105 int64_t energy_below = 0;
5106 int64_t energy_above = 0;
5107 prev_node = path_state_->Start(path);
5108 for (const auto chain : path_state_->Chains(path)) {
5110 if (chain.First() != prev_node) {
5111 const int64_t distance = distance_evaluator(prev_node, chain.First());
5112 CapAddTo(force_evaluator(prev_node), &total_force);
5113 CapAddTo(CapProd(std::min(cost.threshold, total_force), distance),
5115 const int64_t force_above =
5116 std::max<int64_t>(0, CapSub(total_force, cost.threshold));
5117 CapAddTo(CapProd(force_above, distance), &energy_above);
5118 prev_node = chain.First();
5123 const int chain_path = path_state_->Path(chain.First());
5124 const int chain_force_class =
5125 chain_path < 0 ? -1 : force_class_[chain_path];
5126 const int chain_distance_class =
5127 chain_path < 0 ? -1 : distance_class_[chain_path];
5128 const bool force_is_cached = chain_force_class == path_force_class;
5129 const bool distance_is_cached = chain_distance_class == path_distance_class;
5131 if (force_is_cached && distance_is_cached && chain.NumNodes() >= 2) {
5132 const int first_index = threshold_query_index_of_node_[chain.First()];
5133 const int last_index = threshold_query_index_of_node_[chain.Last()];
5135 const int64_t zero_total_energy = energy_query_.RangeSumWithThreshold(
5136 kint64min, first_index, last_index);
5137 const int64_t total_distance = distance_query_.RangeSumWithThreshold(
5138 kint64min, first_index, last_index);
5146 const int64_t zero_total_force_first =
5147 force_rmq_.array()[force_rmq_index_of_node_[chain.First()]];
5148 const int64_t zero_threshold =
5149 CapSub(cost.threshold, CapSub(total_force, zero_total_force_first));
5153 const int64_t zero_high_energy = energy_query_.RangeSumWithThreshold(
5154 zero_threshold, first_index, last_index);
5155 const int64_t zero_high_distance = distance_query_.RangeSumWithThreshold(
5156 zero_threshold, first_index, last_index);
5160 const int64_t zero_energy_above =
5161 CapSub(zero_high_energy, CapProd(zero_high_distance, zero_threshold));
5167 CapAddTo(zero_energy_above, &energy_above);
5170 CapSub(cost.threshold, zero_threshold))),
5174 const int64_t zero_total_force_last =
5175 force_rmq_.array()[force_rmq_index_of_node_[chain.Last()]];
5176 CapAddTo(CapSub(zero_total_force_last, zero_total_force_first),
5178 prev_node = chain.Last();
5180 for (const int node : chain.WithoutFirstNode()) {
5181 const int64_t force = force_is_cached ? cached_force_[prev_node]
5182 : force_evaluator(prev_node);
5183 const int64_t distance = distance_is_cached
5184 ? cached_distance_[prev_node]
5185 : distance_evaluator(prev_node, node);
5186 CapAddTo(force, &total_force);
5187 CapAddTo(CapProd(std::min(cost.threshold, total_force), distance),
5189 const int64_t force_above =
5190 std::max<int64_t>(0, CapSub(total_force, cost.threshold));
5191 CapAddTo(CapProd(force_above, distance), &energy_above);
5197 return CapAdd(CapProd(energy_below, cost.cost_per_unit_below_threshold),
5198 CapProd(energy_above, cost.cost_per_unit_above_threshold));
5205 std::string DebugString() const override { return name_; }
5206 PathEnergyCostFilter(std::unique_ptr<PathEnergyCostChecker> checker,
5207 absl::string_view energy_name)
5208 : checker_(std::move(checker)),
5209 name_(absl::StrCat("PathEnergyCostFilter(", energy_name, ")")) {}
5211 bool Accept(const Assignment*, const Assignment*, int64_t objective_min,
5212 int64_t objective_max) override {
5213 if (objective_max > kint64max / 2) return true;
5214 if (!checker_->Check()) return false;
5215 const int64_t cost = checker_->AcceptedCost();
5216 return objective_min <= cost && cost <= objective_max;
5219 void Synchronize(const Assignment*, const Assignment*) override {
5223 int64_t GetSynchronizedObjectiveValue() const override {
5224 return checker_->CommittedCost();
5226 int64_t GetAcceptedObjectiveValue() const override {
5227 return checker_->AcceptedCost();
5231 std::unique_ptr<PathEnergyCostChecker> checker_;
5238 Solver* solver, std::unique_ptr<PathEnergyCostChecker> checker,
5239 absl::string_view dimension_name) {
5240 PathEnergyCostFilter* filter =
5241 new PathEnergyCostFilter(std::move(checker), dimension_name);
5242 return solver->RevAlloc(filter);
5249 const std::vector<std::vector<double>>& rows)
5250 : num_variables_(rows.empty() ? 0 : rows[0].size()),
5252 if (num_variables_ == 0) return;
5254 const int64_t num_block_rows = (num_rows_ + kBlockSize - 1) / kBlockSize;
5255 blocks_.resize(num_block_rows * num_variables_);
5256 const int64_t num_full_block_rows = num_rows_ / kBlockSize;
5257 for (int64_t br = 0; br < num_full_block_rows; ++br) {
5258 for (int64_t v = 0; v < num_variables_; ++v) {
5259 Block& block = blocks_[br * num_variables_ + v];
5260 for (int c = 0; c < kBlockSize; ++c) {
5261 block.coefficients[c] = rows[br * kBlockSize + c][v];
5268 if (num_full_block_rows == num_block_rows) return;
5269 DCHECK_EQ(num_full_block_rows + 1, num_block_rows);
5270 Block* last_block_row = &blocks_[num_full_block_rows * num_variables_];
5271 const int first_coefficient_to_pad =
5272 num_rows_ - num_full_block_rows * kBlockSize;
5273 for (int64_t v = 0; v < num_variables_; ++v) {
5274 Block& block = last_block_row[v];
5276 for (int c = 0; c < first_coefficient_to_pad; ++c) {
5277 const int64_t r = num_full_block_rows * kBlockSize + c;
5278 block.coefficients[c] = rows[r][v];
5281 for (int c = first_coefficient_to_pad; c < kBlockSize; ++c) {
5288 absl::Span<const double> values) const {
5289 DCHECK_EQ(values.size(), num_variables_);
5290 constexpr double kInfinity = std::numeric_limits<double>::infinity();
5291 if (num_rows_ == 0) return -kInfinity;
5292 if (num_variables_ == 0) return 0.0;
5293 DCHECK_EQ(blocks_.size() % num_variables_, 0);
5295 absl::c_fill(maximums.coefficients, -kInfinity);
5296 for (auto row = blocks_.begin(); row != blocks_.end();
5299 absl::c_fill(evaluations.coefficients, 0.0);
5300 for (int64_t v = 0; v < num_variables_; ++v) {
5301 evaluations.BlockMultiplyAdd(row[v], values[v]);
5303 maximums.MaximumWith(evaluations);
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)
Definition routing_filters.cc:713
bool Accept(const Assignment *delta, const Assignment *deltadelta, int64_t objective_min, int64_t objective_max) override
Definition routing_filters.cc:726
void OnSynchronize(const Assignment *delta) override
Definition routing_filters.cc:854
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)
Definition routing_filters.cc:4421
void Commit()
Definition routing_filters.cc:4507
bool Check() const
Definition routing_filters.cc:4446
absl::Span< const int64_t > CommittedTravels(int path) const
absl::Span< Interval > MutableCumuls(int path)
int NumNodes(int path) const
absl::Span< const int > Nodes(int path) const
absl::Span< Interval > MutableTransits(int path)
Interval & MutableSpan(int path)
absl::Span< int64_t > MutableTravelSums(int path)
absl::Span< int64_t > MutableTravels(int path)
std::vector< VehicleBreak > & MutableVehicleBreaks(int path)
absl::Span< const int > CommittedNodes(int path) const
absl::Span< const int64_t > TravelSums(int path) const
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.
void Relax() const
Definition routing_filters.cc:4667
LightVehicleBreaksChecker(PathState *path_state, std::vector< PathData > path_data)
Definition routing_filters.cc:4663
bool Check() const
Definition routing_filters.cc:4675
const RoutingDimension * dimension() const
bool SetMin(int64_t new_min) const
bool SetMax(int64_t new_max) const
double Evaluate(absl::Span< const double > values) const
Definition routing_filters.cc:5287
MaxLinearExpressionEvaluator(const std::vector< std::vector< double > > &rows)
Definition routing_filters.cc:5248
void Commit()
Definition routing_filters.cc:5024
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)
Definition routing_filters.cc:4930
bool Check()
Definition routing_filters.cc:4965
void ChangeLoops(absl::Span< const int > new_loops)
Definition routing_filters.cc:3992
const std::vector< int > & ChangedLoops() const
void Reset()
Definition routing_filters.cc:3936
const std::vector< int > & ChangedPaths() const
void Commit()
Definition routing_filters.cc:4001
ChainRange Chains(int path) const
Definition routing_filters.cc:3969
static constexpr int kUnassigned
void Revert()
Definition routing_filters.cc:4010
NodeRange Nodes(int path) const
Definition routing_filters.cc:3976
PathState(int num_nodes, std::vector< int > path_start, std::vector< int > path_end)
Definition routing_filters.cc:3922
static constexpr int kLoop
void ChangePath(int path, absl::Span< const ChainBounds > chains)
Definition routing_filters.cc:3983
int Start(int path) const
absl::Span< int64_t > MutablePostVisits(int path)
void ChangePathSize(int path, int new_num_nodes)
absl::Span< int64_t > MutablePreVisits(int path)
const std::vector< T > & array() const
T RangeMinimum(int begin, int end) const
bool HasBreakConstraints() const
Returns true if any break interval or break distance was defined.
const std::vector< NodePrecedence > & GetNodePrecedences() const
RoutingModel * model() const
Returns the model on which the dimension was created.
int64_t global_span_cost_coefficient() const
int64_t Size() const
Returns the number of next variables in the model.
int64_t Start(int vehicle) const
RoutingDisjunctionIndex DisjunctionIndex
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulLPOptimizer(const RoutingDimension &dimension) const
GlobalDimensionCumulOptimizer * GetMutableGlobalCumulMPOptimizer(const RoutingDimension &dimension) const
LocalDimensionCumulOptimizer * GetMutableLocalCumulMPOptimizer(const RoutingDimension &dimension) const
bool IsEnd(int64_t index) const
Returns true if 'index' represents the last node of a route.
int vehicles() const
Returns the number of vehicle routes in the model.
const std::vector< int > & GetDimensionResourceGroupIndices(const RoutingDimension *dimension) const
int64_t GetSoftSameVehicleCost(int index) const
Returns the cost of the soft same vehicle constraint of index 'index'.
LocalDimensionCumulOptimizer * GetMutableLocalCumulLPOptimizer(const RoutingDimension &dimension) const
const std::vector< operations_research::IntVar * > & Nexts() const
bool disable_scheduling_beware_this_may_degrade_performance() const
void MakeTreeFromNewElements()
Definition routing_filters.cc:4799
void PushBack(int64_t height, int64_t weight)
int64_t RangeSumWithThreshold(int64_t threshold_height, int begin_index, int end_index) const
Definition routing_filters.cc:4878
void Clear()
Definition routing_filters.cc:4792
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)
bool FindCopy(const Collection &collection, const Key &key, Value *const 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
std::function< int64_t(const Model &)> Value(IntegerVariable v)
LocalSearchFilter * MakePickupDeliveryFilter(const RoutingModel &routing_model, const PathState *path_state, absl::Span< const PickupDeliveryPair > pairs, const std::vector< RoutingModel::PickupAndDeliveryPolicy > &vehicle_policies)
Definition routing_filters.cc:3095
IntVarLocalSearchFilter * MakeVehicleAmortizedCostFilter(const RoutingModel &routing_model)
Returns a filter computing vehicle amortized costs.
Definition routing_filters.cc:1068
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)
Definition routing_filters.cc:1474
IntVarLocalSearchFilter * MakeRouteConstraintFilter(const RoutingModel &routing_model)
Returns a filter tracking route constraints.
Definition routing_filters.cc:158
void CapAddTo(int64_t x, int64_t *y)
static constexpr double kInfinity
IntVarLocalSearchFilter * MakeSameVehicleCostFilter(const RoutingModel &routing_model)
Returns a filter computing same vehicle costs.
Definition routing_filters.cc:1198
IntVarLocalSearchFilter * MakeMaxActiveVehiclesFilter(const RoutingModel &routing_model)
Returns a filter ensuring that max active vehicles constraints are enforced.
Definition routing_filters.cc:224
void AppendLightWeightDimensionFilters(const PathState *path_state, const std::vector< RoutingDimension * > &dimensions, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
Definition routing_filters.cc:2706
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.
Definition routing_filters.cc:705
int64_t CapSub(int64_t x, int64_t y)
LocalSearchFilter * MakePathEnergyCostFilter(Solver *solver, std::unique_ptr< PathEnergyCostChecker > checker, absl::string_view dimension_name)
Definition routing_filters.cc:5237
LinearExpr operator-(LinearExpr lhs, const LinearExpr &rhs)
void CapSubFrom(int64_t amount, int64_t *target)
IntVarLocalSearchFilter * MakeCumulBoundsPropagatorFilter(const RoutingDimension &dimension)
Returns a filter handling dimension cumul bounds.
Definition routing_filters.cc:3226
LocalSearchFilter * MakePathStateFilter(Solver *solver, std::unique_ptr< PathState > path_state, const std::vector< IntVar * > &nexts)
Definition routing_filters.cc:4342
ClosedInterval::Iterator end(ClosedInterval interval)
LocalSearchFilter * MakeLightVehicleBreaksFilter(Solver *solver, std::unique_ptr< LightVehicleBreaksChecker > checker, absl::string_view dimension_name)
Definition routing_filters.cc:4784
bool PropagateTransitAndSpan(int path, DimensionValues &dimension_values)
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.
Definition routing_filters.cc:2646
IntVarLocalSearchFilter * MakeCPFeasibilityFilter(RoutingModel *routing_model)
Returns a filter checking the current solution using CP propagation.
Definition routing_filters.cc:3917
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)
void FillPrePostVisitValues(int path, const DimensionValues &dimension_values, std::optional< absl::AnyInvocable< int64_t(int64_t, int64_t) const > > pre_travel_evaluator, std::optional< absl::AnyInvocable< int64_t(int64_t, int64_t) const > > post_travel_evaluator, PrePostVisitValues &visit_values)
Definition routing_filters.cc:1554
IntVarLocalSearchFilter * MakeOrderedActivityGroupFilter(const RoutingModel &routing_model)
Definition routing_filters.cc:548
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.
Definition routing_filters.cc:3165
static const int kUnassigned
void AppendDimensionCumulFilters(const std::vector< RoutingDimension * > &dimensions, const RoutingSearchParameters ¶meters, bool filter_objective_cost, bool use_chain_cumul_filter, std::vector< LocalSearchFilterManager::FilterEvent > *filters)
Definition routing_filters.cc:2779
LocalSearchFilter * MakeResourceAssignmentFilter(LocalDimensionCumulOptimizer *lp_optimizer, LocalDimensionCumulOptimizer *mp_optimizer, bool propagate_own_objective_value, bool filter_objective_cost)
Definition routing_filters.cc:3810
bool PropagateLightweightVehicleBreaks(int path, DimensionValues &dimension_values, absl::Span< const std::pair< int64_t, int64_t > > interbreaks)
Definition routing_filters.cc:1584
int MostSignificantBitPosition32(uint32_t n)
IntVarLocalSearchFilter * MakeTypeRegulationsFilter(const RoutingModel &routing_model)
Returns a filter ensuring type regulation constraints are enforced.
Definition routing_filters.cc:1350
IntVarLocalSearchFilter * MakeGlobalLPCumulFilter(GlobalDimensionCumulOptimizer *lp_optimizer, GlobalDimensionCumulOptimizer *mp_optimizer, bool filter_objective_cost)
Returns a filter checking global linear constraints and costs.
Definition routing_filters.cc:3383
IntVarLocalSearchFilter * MakeActiveNodeGroupFilter(const RoutingModel &routing_model)
Definition routing_filters.cc:542
int64_t CapOpp(int64_t v)
LocalSearchFilter * MakeDimensionFilter(Solver *solver, std::unique_ptr< DimensionChecker > checker, absl::string_view dimension_name)
Definition routing_filters.cc:4655
trees with all degrees equal to
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
ABSL_FLAG(bool, routing_strong_debug_checks, false, "Run stronger checks in debug; these stronger tests might change " "the complexity of the code in particular.")
std::vector< 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