32#include "absl/algorithm/container.h"
33#include "absl/cleanup/cleanup.h"
34#include "absl/container/flat_hash_map.h"
35#include "absl/container/flat_hash_set.h"
36#include "absl/flags/flag.h"
37#include "absl/log/check.h"
38#include "absl/log/log.h"
39#include "absl/log/vlog_is_on.h"
40#include "absl/numeric/bits.h"
41#include "absl/numeric/int128.h"
42#include "absl/random/distributions.h"
43#include "absl/strings/str_cat.h"
44#include "absl/types/span.h"
70ABSL_FLAG(
bool, cp_model_dump_routes_support_graphs,
false,
71 "DEBUG ONLY. When set to true, SolveCpModel() dumps the arcs with "
72 "non-zero LP values of the routes constraints, at decision level 0, "
73 "which are used to subsequently generate cuts. The values are "
74 "written as a SupportGraphProto in text format to "
75 "'FLAGS_cp_model_dump_prefix'support_graph_{counter}.pb.txt.");
94void ComputeMinLowerBoundOfSharedVariables(
95 const absl::flat_hash_map<IntegerVariable, IntegerValue>& new_lbs,
96 absl::flat_hash_map<IntegerVariable, IntegerValue>* current_lbs) {
97 if (new_lbs.empty()) current_lbs->clear();
98 for (
auto it = current_lbs->begin(); it != current_lbs->end();) {
99 const IntegerVariable var = it->first;
100 auto other_it = new_lbs.find(var);
101 if (other_it == new_lbs.end()) {
106 current_lbs->erase(it++);
109 it->second = std::min(it->second, other_it->second);
118 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
119 const std::vector<Literal>& literals,
Model* model)
123 binary_relation_repository_(
126 trail_(*model->GetOrCreate<
Trail>()),
131 in_subset_(num_nodes, false),
132 index_in_subset_(num_nodes, -1),
133 incoming_arc_indices_(num_nodes),
134 outgoing_arc_indices_(num_nodes),
135 reachable_(num_nodes, false),
136 next_reachable_(num_nodes, false),
137 node_var_lower_bounds_(num_nodes),
138 next_node_var_lower_bounds_(num_nodes),
140 model->GetOrCreate<
SatParameters>()->routing_cut_dp_effort()) {}
143 if (!VLOG_IS_ON(1))
return;
144 std::vector<std::pair<std::string, int64_t>> stats;
145 stats.push_back({
"RoutingDp/num_full_dp_calls", num_full_dp_calls_});
146 stats.push_back({
"RoutingDp/num_full_dp_skips", num_full_dp_skips_});
148 {
"RoutingDp/num_full_dp_early_abort", num_full_dp_early_abort_});
150 {
"RoutingDp/num_full_dp_work_abort", num_full_dp_work_abort_});
151 stats.push_back({
"RoutingDp/num_full_dp_rc_skip", num_full_dp_rc_skip_});
153 {
"RoutingDp/num_full_dp_unique_arc", num_full_dp_unique_arc_});
154 for (
const auto& [name, count] : num_by_type_) {
155 stats.push_back({absl::StrCat(
"RoutingDp/num_bounds_", name), count});
157 shared_stats_->AddStats(stats);
162void MinOutgoingFlowHelper::PrecomputeDataForSubset(
163 absl::Span<const int> subset) {
164 cannot_be_first_.clear();
165 cannot_be_last_.clear();
167 const int num_nodes = in_subset_.size();
168 in_subset_.assign(num_nodes,
false);
169 index_in_subset_.assign(num_nodes, -1);
170 for (
int i = 0;
i < subset.size(); ++
i) {
171 const int n = subset[
i];
172 in_subset_[n] =
true;
173 index_in_subset_[n] =
i;
176 has_incoming_arcs_from_outside_.assign(num_nodes,
false);
177 has_outgoing_arcs_to_outside_.assign(num_nodes,
false);
178 incoming_arcs_from_outside_.assign(num_nodes, {});
179 outgoing_arcs_to_outside_.assign(num_nodes, {});
181 for (
auto& v : incoming_arc_indices_) v.clear();
182 for (
auto& v : outgoing_arc_indices_) v.clear();
183 for (
int i = 0;
i < tails_.size(); ++
i) {
184 const int tail = tails_[
i];
185 const int head = heads_[
i];
188 if (tail == head)
continue;
189 const bool tail_in = in_subset_[tail];
190 const bool head_in = in_subset_[head];
192 if (tail_in && head_in) {
193 outgoing_arc_indices_[tail].push_back(
i);
194 incoming_arc_indices_[head].push_back(
i);
195 }
else if (tail_in && !head_in) {
196 outgoing_arcs_to_outside_[tail].Add(
i);
197 has_outgoing_arcs_to_outside_[tail] =
true;
198 }
else if (!tail_in && head_in) {
199 incoming_arcs_from_outside_[head].Add(
i);
200 has_incoming_arcs_from_outside_[head] =
true;
208 PrecomputeDataForSubset(subset);
209 DCHECK_EQ(helper.
num_nodes(), in_subset_.size());
210 DCHECK_EQ(helper.
num_arcs(), tails_.size());
212 int min_outgoing_flow = 1;
213 std::string best_name;
215 for (
const bool negate_expressions : {
false,
true}) {
216 for (
const bool use_incoming : {
true,
false}) {
218 const absl::Span<SpecialBinPackingHelper::ItemOrBin> objects =
219 RelaxIntoSpecialBinPackingProblem(subset, d, negate_expressions,
220 use_incoming, helper);
221 const int bound = bin_packing_helper_.ComputeMinNumberOfBins(
222 objects, objects_that_cannot_be_bin_and_reach_minimum_, info);
223 if (bound >= min_outgoing_flow) {
224 min_outgoing_flow = bound;
225 best_name = absl::StrCat((use_incoming ?
"in" :
"out"), info,
226 (negate_expressions ?
"_neg" :
""),
"_", d);
227 cannot_be_first_.clear();
228 cannot_be_last_.clear();
229 auto& vec = use_incoming ? cannot_be_first_ : cannot_be_last_;
230 for (
const int i : objects_that_cannot_be_bin_and_reach_minimum_) {
231 vec.push_back(objects[
i].node);
233 best_bound->
Update(bound,
"AutomaticDimension", cannot_be_first_,
240 if (min_outgoing_flow > 1) num_by_type_[best_name]++;
241 return min_outgoing_flow;
244absl::Span<SpecialBinPackingHelper::ItemOrBin>
245MinOutgoingFlowHelper::RelaxIntoSpecialBinPackingProblem(
246 absl::Span<const int> subset,
int dimension,
bool negate_expressions,
248 tmp_bin_packing_problem_.clear();
267 const int num_nodes = in_subset_.size();
268 std::vector<IntegerValue> min_lower_bound_of_others(num_nodes,
270 std::vector<IntegerValue> max_upper_bound_of_others(num_nodes,
274 const auto lb_when_first = [&helper,
this](
int n,
int dimension,
275 bool negate_expressions) {
277 if (negate_expressions) expr = expr.
Negated();
279 if (incoming_arcs_from_outside_[n].IsUnique()) {
280 const int a = incoming_arcs_from_outside_[n].Get();
283 if (negate_expressions) tail_expr = tail_expr.
Negated();
284 const IntegerValue offset =
292 const auto ub_when_last = [&helper,
this](
int n,
int dimension,
293 bool negate_expressions) {
295 if (negate_expressions) expr = expr.
Negated();
297 if (outgoing_arcs_to_outside_[n].IsUnique()) {
298 const int a = outgoing_arcs_to_outside_[n].Get();
299 AffineExpression head_expr =
301 if (negate_expressions) head_expr = head_expr.
Negated();
302 const IntegerValue offset =
311 for (
const bool forward_pass : {
true,
false}) {
314 const int size = subset.size();
315 for (
int i = 0;
i < size; ++
i) {
316 const int n = forward_pass ? subset[
i] : subset[size - 1 -
i];
322 min_lower_bound_of_others[n] =
323 std::min(min_lower_bound_of_others[n], local_min);
324 max_upper_bound_of_others[n] =
325 std::max(max_upper_bound_of_others[n], local_max);
328 if (has_incoming_arcs_from_outside_[n]) {
329 local_min = std::min(local_min,
330 lb_when_first(n, dimension, negate_expressions));
332 if (has_outgoing_arcs_to_outside_[n]) {
334 std::max(local_max, ub_when_last(n, dimension, negate_expressions));
339 for (
const int n : subset) {
340 const absl::Span<const int> arcs =
341 use_incoming ? incoming_arc_indices_[n] : outgoing_arc_indices_[n];
343 for (
const int a : arcs) {
345 a, dimension, negate_expressions));
348 SpecialBinPackingHelper::ItemOrBin obj;
356 const IntegerValue lb = lb_when_first(n, dimension, negate_expressions);
357 if (max_upper_bound_of_others[n] > lb) {
358 obj.capacity = max_upper_bound_of_others[n] - lb;
361 const IntegerValue ub = ub_when_last(n, dimension, negate_expressions);
362 if (ub > min_lower_bound_of_others[n]) {
363 obj.capacity = ub - min_lower_bound_of_others[n];
371 if ((use_incoming && !has_incoming_arcs_from_outside_[n]) ||
372 (!use_incoming && !has_outgoing_arcs_to_outside_[n])) {
375 }
else if (arcs.empty()) {
382 tmp_bin_packing_problem_.push_back(obj);
385 return absl::MakeSpan(tmp_bin_packing_problem_);
389 absl::Span<ItemOrBin> objects,
390 std::vector<int>& objects_that_cannot_be_bin_and_reach_minimum,
392 objects_that_cannot_be_bin_and_reach_minimum.clear();
393 if (objects.empty())
return 0;
395 IntegerValue sum_of_demands(0);
397 int64_t max_capacity = 0;
398 int max_num_items = 0;
399 bool all_demands_are_non_negative =
true;
401 sum_of_demands =
CapAddI(sum_of_demands, obj.demand);
404 if (obj.demand < 0) {
405 all_demands_are_non_negative =
false;
407 gcd = std::gcd(gcd, std::abs(obj.demand.value()));
410 max_capacity = std::max(max_capacity, obj.capacity.value());
420 absl::StrAppend(&info,
"_gcd");
428 const int result = ComputeMinNumberOfBinsInternal(
429 objects, objects_that_cannot_be_bin_and_reach_minimum);
437 if (result > 1 && all_demands_are_non_negative &&
438 max_bounded_subset_sum_exact_.ComplexityEstimate(
439 max_num_items, max_capacity) < dp_effort_ &&
442 const int new_result = ComputeMinNumberOfBinsInternal(
443 objects, objects_that_cannot_be_bin_and_reach_minimum);
444 CHECK_GE(new_result, result);
445 if (new_result > result) {
446 absl::StrAppend(&info,
"_dp");
457 absl::Span<ItemOrBin> objects) {
458 tmp_demands_.clear();
459 int64_t max_capacity = 0;
462 tmp_demands_.push_back(obj.demand.value());
465 max_capacity = std::max(max_capacity, obj.capacity.value());
468 const int64_t max_reachable =
469 max_bounded_subset_sum_exact_.MaxSubsetSum(tmp_demands_, max_capacity);
470 bool some_change =
false;
471 if (max_reachable < max_capacity) {
473 if (obj.capacity > max_reachable) {
475 obj.capacity = max_reachable;
482int SpecialBinPackingHelper::ComputeMinNumberOfBinsInternal(
483 absl::Span<ItemOrBin> objects,
484 std::vector<int>& objects_that_cannot_be_bin_and_reach_minimum) {
485 objects_that_cannot_be_bin_and_reach_minimum.clear();
502 std::stable_sort(objects.begin(), objects.end(),
503 [](
const ItemOrBin& a,
const ItemOrBin&
b) {
504 if (a.type != b.type) {
507 return a.type > b.type;
509 return a.capacity + a.demand >
b.capacity +
b.demand;
513 IntegerValue sum_of_demands(0);
514 for (
const ItemOrBin& obj : objects) {
515 sum_of_demands += obj.demand;
522 IntegerValue sum_of_capacity(0);
523 for (; num_bins < objects.size(); ++num_bins) {
526 const ItemOrBin& obj = objects[num_bins];
527 if (obj.type != MUST_BE_BIN && sum_of_demands <= sum_of_capacity) {
530 if (obj.type == MUST_BE_ITEM) {
535 DCHECK_GT(sum_of_demands, sum_of_capacity);
536 return objects.size() + 1;
538 sum_of_capacity =
CapAddI(sum_of_capacity, obj.capacity);
540 sum_of_demands -= obj.demand;
548 if (num_bins > 0 && num_bins != objects.size()) {
549 const int worst_used_bin = num_bins - 1;
550 if (objects[worst_used_bin].type == MUST_BE_BIN) {
553 for (
int i = num_bins;
i < objects.size(); ++
i) {
554 objects_that_cannot_be_bin_and_reach_minimum.push_back(
i);
558 CHECK_EQ(objects[worst_used_bin].type, ITEM_OR_BIN);
559 sum_of_demands += objects[worst_used_bin].demand;
560 sum_of_capacity -= objects[worst_used_bin].capacity;
561 const IntegerValue slack = sum_of_demands - sum_of_capacity;
562 CHECK_GE(slack, 0) << num_bins <<
" " << objects.size() <<
" "
563 << objects[worst_used_bin].type;
565 for (
int i = num_bins;
i < objects.size(); ++
i) {
566 if (objects[
i].type == MUST_BE_ITEM)
continue;
570 const IntegerValue new_demand = sum_of_demands - objects[
i].demand;
571 const IntegerValue new_capacity = sum_of_capacity + objects[
i].capacity;
572 if (new_demand > new_capacity) {
573 objects_that_cannot_be_bin_and_reach_minimum.push_back(
i);
583 int num_bins, absl::Span<const ItemOrBin> objects) {
584 if (num_bins >= objects.size())
return true;
585 CHECK_GE(num_bins, 1);
586 tmp_capacities_.resize(num_bins);
587 for (
int i = 0;
i < num_bins; ++
i) {
588 tmp_capacities_[
i] = objects[
i].capacity;
592 for (
const ItemOrBin object : objects.subspan(num_bins)) {
594 for (
int b = 0;
b < num_bins; ++
b) {
595 if (
object.demand <= tmp_capacities_[
b]) {
597 tmp_capacities_[
b] -=
object.demand;
601 if (!placed)
return false;
607 absl::Span<const int> subset) {
608 DCHECK_GE(subset.size(), 1);
609 DCHECK(absl::c_all_of(next_reachable_, [](
bool b) {
return !
b; }));
610 DCHECK(absl::c_all_of(node_var_lower_bounds_,
611 [](
const auto& m) {
return m.empty(); }));
612 DCHECK(absl::c_all_of(next_node_var_lower_bounds_,
613 [](
const auto& m) {
return m.empty(); }));
614 PrecomputeDataForSubset(subset);
618 reachable_ = in_subset_;
621 int longest_path_length = 1;
622 absl::flat_hash_map<IntegerVariable, IntegerValue> tmp_lbs;
623 while (longest_path_length < subset.size()) {
624 bool at_least_one_next_node_reachable =
false;
625 for (
const int head : subset) {
626 bool is_reachable =
false;
627 for (
const int incoming_arc_index : incoming_arc_indices_[head]) {
628 const int tail = tails_[incoming_arc_index];
629 const Literal lit = literals_[incoming_arc_index];
630 if (!reachable_[tail])
continue;
635 integer_trail_, root_level_bounds_, binary_relation_repository_,
636 implied_bounds_, lit, node_var_lower_bounds_[tail], &tmp_lbs)) {
643 next_node_var_lower_bounds_[head] = tmp_lbs;
646 ComputeMinLowerBoundOfSharedVariables(
647 tmp_lbs, &next_node_var_lower_bounds_[head]);
651 next_reachable_[head] = is_reachable;
652 if (next_reachable_[head]) at_least_one_next_node_reachable =
true;
654 if (!at_least_one_next_node_reachable) {
657 std::swap(reachable_, next_reachable_);
658 std::swap(node_var_lower_bounds_, next_node_var_lower_bounds_);
659 for (
const int n : subset) {
660 next_node_var_lower_bounds_[n].clear();
662 ++longest_path_length;
666 int max_longest_paths = 0;
667 for (
const int n : subset) {
668 if (reachable_[n]) ++max_longest_paths;
671 next_reachable_[n] =
false;
672 node_var_lower_bounds_[n].clear();
673 next_node_var_lower_bounds_[n].clear();
675 return GetMinOutgoingFlow(subset.size(), longest_path_length,
679int MinOutgoingFlowHelper::GetMinOutgoingFlow(
int subset_size,
680 int longest_path_length,
681 int max_longest_paths) {
682 if (max_longest_paths * longest_path_length < subset_size) {
686 DCHECK_GT(longest_path_length, 1);
689 return max_longest_paths +
691 subset_size - max_longest_paths * longest_path_length,
692 longest_path_length - 1);
702 bool operator==(
const Path& p)
const {
703 return node_set == p.node_set && last_node == p.last_node;
706 template <
typename H>
708 return H::combine(std::move(h), p.node_set, p.last_node);
715 absl::Span<const int> subset) {
716 DCHECK_GE(subset.size(), 1);
717 DCHECK_LE(subset.size(), 32);
718 PrecomputeDataForSubset(subset);
720 std::vector<int> longest_path_length_by_end_node(subset.size(), 1);
722 absl::flat_hash_map<IntegerVariable, IntegerValue> tmp_lbs;
723 absl::flat_hash_map<Path, absl::flat_hash_map<IntegerVariable, IntegerValue>>
725 std::vector<Path> paths;
726 std::vector<Path> next_paths;
727 for (
int i = 0;
i < subset.size(); ++
i) {
730 {.node_set =
static_cast<uint32_t
>(1 <<
i), .last_node = subset[
i]});
731 path_var_bounds[paths.back()] = {};
733 int longest_path_length = 1;
734 for (
int path_length = 1; path_length <= subset.size(); ++path_length) {
735 for (
const Path& path : paths) {
738 DCHECK(path_var_bounds.contains(path));
739 const absl::flat_hash_map<IntegerVariable, IntegerValue> path_bounds =
740 std::move(path_var_bounds.extract(path).mapped());
743 longest_path_length = path_length;
744 longest_path_length_by_end_node[index_in_subset_[path.last_node]] =
749 for (
const int outgoing_arc_index :
750 outgoing_arc_indices_[path.last_node]) {
751 const int head = heads_[outgoing_arc_index];
752 const int head_index_in_subset = index_in_subset_[head];
753 DCHECK_NE(head_index_in_subset, -1);
754 if (path.node_set & (1 << head_index_in_subset))
continue;
755 const Path new_path = {
756 .node_set = path.node_set | (1 << head_index_in_subset),
762 binary_relation_repository_, implied_bounds_,
763 literals_[outgoing_arc_index], path_bounds,
768 const auto [it, inserted] = path_var_bounds.insert({new_path, tmp_lbs});
771 next_paths.push_back(new_path);
775 ComputeMinLowerBoundOfSharedVariables(tmp_lbs, &it->second);
779 std::swap(paths, next_paths);
783 int max_longest_paths = 0;
784 for (
int i = 0;
i < subset.size(); ++
i) {
785 if (longest_path_length_by_end_node[
i] == longest_path_length) {
790 return GetMinOutgoingFlow(subset.size(), longest_path_length,
797 bool use_forward_direction) {
798 cannot_be_first_.clear();
799 cannot_be_last_.clear();
800 if (k >= subset.size())
return true;
801 if (subset.size() > 31)
return true;
806 if (special_node >= 0) {
807 tmp_subset_.assign(subset.begin(), subset.end());
809 for (
int i = 0;
i < tmp_subset_.size(); ++
i) {
810 if (tmp_subset_[
i] == special_node) {
812 std::swap(tmp_subset_[0], tmp_subset_[
i]);
819 subset = tmp_subset_;
822 PrecomputeDataForSubset(subset);
823 ++num_full_dp_calls_;
828 adjacency.
reserve(subset.size());
829 for (
int i = 0;
i < subset.size(); ++
i) {
831 const int node = subset[
i];
832 if (helper !=
nullptr) {
833 CHECK(use_forward_direction);
834 for (
int j = 0; j < subset.size(); ++j) {
835 if (
i == j)
continue;
841 if (use_forward_direction) {
842 for (
const int arc : outgoing_arc_indices_[node]) {
846 for (
const int arc : incoming_arc_indices_[node]) {
859 uint32_t last_nodes_set;
873 absl::flat_hash_map<IntegerVariable, IntegerValue> lbs;
880 absl::int128 sum_of_reduced_costs = 0;
883 const int size = subset.size();
884 const uint32_t final_mask = (1 << size) - 1;
886 const auto& can_be_last_set = use_forward_direction
887 ? has_outgoing_arcs_to_outside_
888 : has_incoming_arcs_from_outside_;
889 const auto& can_be_first_set = use_forward_direction
890 ? has_incoming_arcs_from_outside_
891 : has_outgoing_arcs_to_outside_;
893 uint32_t can_be_last_mask = 0;
894 for (
int i = 0;
i < subset.size(); ++
i) {
895 if (can_be_last_set[subset[
i]]) {
896 can_be_last_mask |= (1 <<
i);
902 int64_t allocated_memory_estimate = 0;
905 std::vector<State> states;
906 states.push_back(State());
909 const auto update_using_unique_arc = [manager,
this](UniqueArc unique_arc,
911 if (!unique_arc.IsUnique())
return true;
912 if (manager ==
nullptr)
return true;
914 ++num_full_dp_unique_arc_;
915 const Literal unique_lit = literals_[unique_arc.Get()];
918 ++num_full_dp_rc_skip_;
922 absl::flat_hash_map<IntegerVariable, IntegerValue> copy = state.lbs;
924 binary_relation_repository_, implied_bounds_,
925 unique_lit, copy, &state.lbs);
929 if (special_node >= 0) {
930 states.back().node_set = 1;
931 states.back().last_nodes_set = 1;
935 const UniqueArc unique_arc = use_forward_direction
936 ? incoming_arcs_from_outside_[subset[0]]
937 : outgoing_arcs_to_outside_[subset[0]];
938 if (!update_using_unique_arc(unique_arc, states.back())) {
943 while (!states.empty()) {
944 if (allocated_memory_estimate > 1e7) {
945 ++num_full_dp_work_abort_;
948 const State from_state = std::move(states.back());
952 const int num_routes = absl::popcount(from_state.last_nodes_set);
956 if (num_routes < k) {
957 const int num_extra = k - num_routes - 1;
958 for (
int i = 0;
i + num_extra < size; ++
i) {
959 if (from_state.node_set >>
i)
continue;
960 if (!can_be_first_set[subset[
i]])
continue;
965 const uint32_t head_mask = (1 <<
i);
966 to_state.node_set = from_state.node_set | head_mask;
967 to_state.last_nodes_set = from_state.last_nodes_set | head_mask;
968 to_state.lbs = from_state.lbs;
969 to_state.sum_of_reduced_costs = from_state.sum_of_reduced_costs;
971 const UniqueArc unique_arc =
972 use_forward_direction ? incoming_arcs_from_outside_[subset[
i]]
973 : outgoing_arcs_to_outside_[subset[
i]];
974 if (!update_using_unique_arc(unique_arc, to_state)) {
978 if (to_state.node_set == final_mask) {
979 ++num_full_dp_early_abort_;
982 states.push_back(std::move(to_state));
988 for (
int i = 0;
i < size; ++
i) {
989 const uint32_t tail_mask = 1 <<
i;
990 if ((from_state.last_nodes_set & tail_mask) == 0)
continue;
991 const int tail = subset[
i];
992 for (
const auto [head, literal] : adjacency[
i]) {
993 const uint32_t head_mask = (1 << index_in_subset_[head]);
994 if (from_state.node_set & head_mask)
continue;
1000 if (manager !=
nullptr) {
1001 DCHECK_EQ(helper,
nullptr);
1002 to_state.sum_of_reduced_costs =
1003 from_state.sum_of_reduced_costs +
1006 ++num_full_dp_rc_skip_;
1012 to_state.lbs = from_state.lbs;
1013 if (helper !=
nullptr) {
1015 integer_trail_, tail, head, from_state.lbs, &to_state.lbs)) {
1020 binary_relation_repository_,
1021 implied_bounds_, literal, from_state.lbs,
1027 to_state.node_set = from_state.node_set | head_mask;
1028 to_state.last_nodes_set = from_state.last_nodes_set | head_mask;
1029 to_state.last_nodes_set ^= tail_mask;
1030 allocated_memory_estimate += to_state.lbs.size();
1031 if (to_state.node_set == final_mask) {
1033 if (to_state.last_nodes_set & ~can_be_last_mask)
continue;
1037 bool infeasible =
false;
1038 int last_mask = to_state.last_nodes_set;
1039 for (
int j = 0; last_mask; j++, last_mask /= 2) {
1040 if ((last_mask & 1) == 0)
continue;
1042 const UniqueArc unique_arc =
1043 use_forward_direction ? outgoing_arcs_to_outside_[subset[j]]
1044 : incoming_arcs_from_outside_[subset[j]];
1045 if (!update_using_unique_arc(unique_arc, to_state)) {
1054 ++num_full_dp_early_abort_;
1057 states.push_back(std::move(to_state));
1073struct LocalRelation {
1074 IntegerValue tail_coeff = 0;
1075 IntegerValue head_coeff = 0;
1079 bool empty()
const {
return tail_coeff == 0 && head_coeff == 0; }
1080 bool operator==(
const LocalRelation& r)
const {
1081 return tail_coeff == r.tail_coeff && head_coeff == r.head_coeff &&
1082 lhs == r.lhs && rhs == r.rhs;
1086IntegerVariable UniqueSharedVariable(
const sat::Relation& r1,
1092 DCHECK_NE(X[0], X[1]);
1093 DCHECK_NE(Y[0], Y[1]);
1094 if (X[0] == Y[0] && X[1] != Y[1])
return X[0];
1095 if (X[0] == Y[1] && X[1] != Y[0])
return X[0];
1096 if (X[1] == Y[0] && X[0] != Y[1])
return X[1];
1097 if (X[1] == Y[1] && X[0] != Y[0])
return X[1];
1101class RouteRelationsBuilder {
1103 using HeadMinusTailBounds = RouteRelationsHelper::HeadMinusTailBounds;
1105 RouteRelationsBuilder(
1106 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
1107 absl::Span<const Literal> literals,
1108 absl::Span<const AffineExpression> flat_node_dim_expressions,
1109 const ConditionalLinear2Bounds& binary_relation_repository)
1110 : num_nodes_(num_nodes),
1111 num_arcs_(tails.size()),
1114 literals_(literals),
1115 binary_relation_repository_(binary_relation_repository) {
1116 if (!flat_node_dim_expressions.empty()) {
1117 DCHECK_EQ(flat_node_dim_expressions.size() % num_nodes, 0);
1118 num_dimensions_ = flat_node_dim_expressions.size() / num_nodes;
1119 flat_node_dim_expressions_.assign(flat_node_dim_expressions.begin(),
1120 flat_node_dim_expressions.end());
1124 int num_dimensions()
const {
return num_dimensions_; }
1126 std::vector<AffineExpression>& flat_node_dim_expressions() {
1127 return flat_node_dim_expressions_;
1130 std::vector<HeadMinusTailBounds>& flat_arc_dim_relations() {
1131 return flat_arc_dim_relations_;
1134 std::vector<IntegerValue>& flat_shortest_path_lbs() {
1135 return flat_shortest_path_lbs_;
1138 bool BuildNodeExpressions() {
1139 if (flat_node_dim_expressions_.empty()) {
1143 ComputeDimensionOfEachVariable();
1144 if (num_dimensions_ == 0)
return false;
1151 ComputeAdjacentRelationsPerNodeAndDimension();
1154 std::queue<std::pair<int, int>> node_dim_pairs =
1155 ComputeVarAssociationsFromSharedVariableOfAdjacentRelations();
1156 if (node_dim_pairs.empty())
return false;
1160 ComputeVarAssociationsFromRelationsWithSingleFreeVar(node_dim_pairs);
1165 void BuildArcRelations(Model* model) {
1168 ComputeArcRelations(model);
1175 void BuildShortestPaths(Model* model) {
1176 if (num_nodes_ >= 100)
return;
1178 auto& integer_trail = *model->GetOrCreate<IntegerTrail>();
1179 std::vector<IntegerValue> flat_trivial_lbs(num_nodes_ * num_nodes_);
1180 const auto trivial = [
this, &flat_trivial_lbs](
int i,
1181 int j) -> IntegerValue& {
1185 return flat_trivial_lbs[
i * num_nodes_ + j];
1187 flat_shortest_path_lbs_.resize(num_nodes_ * num_nodes_ * num_dimensions_,
1188 std::numeric_limits<IntegerValue>::max());
1190 for (
int dim = 0; dim < num_dimensions_; ++dim) {
1192 for (
int i = 1;
i < num_nodes_; ++
i) {
1193 const AffineExpression& i_expr = node_expression(i, dim);
1194 const IntegerValue i_ub = integer_trail.LevelZeroUpperBound(i_expr);
1195 for (
int j = 1; j < num_nodes_; ++j) {
1196 if (i == j)
continue;
1197 const AffineExpression& j_expr = node_expression(j, dim);
1198 trivial(i, j) = integer_trail.LevelZeroLowerBound(j_expr) - i_ub;
1203 const auto path = [
this](
int dim,
int i,
int j) -> IntegerValue& {
1204 return flat_shortest_path_lbs_[dim * num_nodes_ * num_nodes_ +
1205 i * num_nodes_ + j];
1207 for (
int arc = 0; arc < tails_.size(); ++arc) {
1208 const int tail = tails_[arc];
1209 const int head = heads_[arc];
1210 if (tail == 0 || head == 0 || tail == head)
continue;
1211 path(dim, tail, head) =
1212 std::max(trivial(tail, head),
1213 flat_arc_dim_relations_[arc * num_dimensions_ + dim].lhs);
1217 for (
int k = 1; k < num_nodes_; ++k) {
1218 for (
int i = 1;
i < num_nodes_; ++
i) {
1219 if (i == k)
continue;
1220 for (
int j = 1; j < num_nodes_; ++j) {
1221 if (j == k || j == i)
continue;
1223 std::min(path(dim, i, j),
1224 std::max(trivial(i, j),
1225 CapAddI(path(dim, i, k), path(dim, k, j))));
1233 AffineExpression& node_expression(
int node,
int dimension) {
1234 return flat_node_dim_expressions_[node * num_dimensions_ + dimension];
1237 void ProcessNewArcConstantRelation(
int arc_index,
int dimension,
1238 IntegerValue head_minus_tail) {
1239 HeadMinusTailBounds& relation =
1240 flat_arc_dim_relations_[arc_index * num_dimensions_ + dimension];
1241 relation.lhs = std::max(relation.lhs, head_minus_tail);
1242 relation.rhs = std::min(relation.rhs, head_minus_tail);
1245 void ProcessNewArcRelation(
int arc_index,
int dimension, LocalRelation r) {
1246 if (r.empty())
return;
1247 if (r.head_coeff < 0) {
1248 r.tail_coeff = -r.tail_coeff;
1249 r.head_coeff = -r.head_coeff;
1252 std::swap(r.lhs, r.rhs);
1254 if (r.head_coeff != 1 || r.tail_coeff != -1)
return;
1257 HeadMinusTailBounds& relation =
1258 flat_arc_dim_relations_[arc_index * num_dimensions_ + dimension];
1259 relation.lhs = std::max(relation.lhs, r.lhs);
1260 relation.rhs = std::min(relation.rhs, r.rhs);
1263 void ComputeDimensionOfEachVariable() {
1269 ConnectedComponentsFinder<IntegerVariable> cc_finder;
1270 for (
int i = 0;
i < num_arcs_; ++
i) {
1271 if (tails_[i] == heads_[i])
continue;
1272 num_arcs_per_literal_[literals_[
i]]++;
1273 for (
const int relation_index :
1274 binary_relation_repository_.IndicesOfRelationsEnforcedBy(
1276 const auto& r = binary_relation_repository_.relation(relation_index);
1277 DCHECK_NE(r.expr.vars[0], kNoIntegerVariable);
1278 DCHECK_NE(r.expr.vars[1], kNoIntegerVariable);
1283 const std::vector<std::vector<IntegerVariable>> connected_components =
1285 for (
int i = 0;
i < connected_components.size(); ++
i) {
1286 for (
const IntegerVariable var : connected_components[i]) {
1290 num_dimensions_ = connected_components.size();
1293 void ComputeAdjacentRelationsPerNodeAndDimension() {
1294 adjacent_relation_indices_ = std::vector<std::vector<std::vector<int>>>(
1295 num_dimensions_, std::vector<std::vector<int>>(num_nodes_));
1296 for (
int i = 0;
i < num_arcs_; ++
i) {
1297 if (tails_[i] == heads_[i])
continue;
1301 if (num_arcs_per_literal_[literals_[i]] > 1)
continue;
1302 for (
const int relation_index :
1303 binary_relation_repository_.IndicesOfRelationsEnforcedBy(
1305 const auto& r = binary_relation_repository_.relation(relation_index);
1306 DCHECK_NE(r.expr.vars[0], kNoIntegerVariable);
1307 DCHECK_NE(r.expr.vars[1], kNoIntegerVariable);
1310 adjacent_relation_indices_[dimension][tails_[
i]].push_back(
1312 adjacent_relation_indices_[dimension][heads_[
i]].push_back(
1320 std::queue<std::pair<int, int>>
1321 ComputeVarAssociationsFromSharedVariableOfAdjacentRelations() {
1322 flat_node_dim_expressions_ = std::vector<AffineExpression>(
1323 num_nodes_ * num_dimensions_, AffineExpression());
1324 std::queue<std::pair<int, int>> result;
1325 for (
int n = 0; n < num_nodes_; ++n) {
1326 for (
int d = 0; d < num_dimensions_; ++d) {
1332 for (
const int r1_index : adjacent_relation_indices_[d][n]) {
1333 const auto& r1 = binary_relation_repository_.relation(r1_index);
1334 for (
const int r2_index : adjacent_relation_indices_[d][n]) {
1335 if (r1_index == r2_index)
continue;
1336 const auto& r2 = binary_relation_repository_.relation(r2_index);
1337 const IntegerVariable shared_var = UniqueSharedVariable(r1, r2);
1338 if (shared_var == kNoIntegerVariable)
continue;
1340 AffineExpression& node_expr = node_expression(n, d);
1341 if (node_expr.IsConstant()) {
1342 result.push({n, d});
1343 }
else if (node_expr.var != shared_var) {
1344 VLOG(2) <<
"Several vars per node and dimension in route with "
1345 << num_nodes_ <<
" nodes and " << num_arcs_ <<
" arcs";
1348 node_expr = shared_var;
1356 void ComputeVarAssociationsFromRelationsWithSingleFreeVar(
1357 std::queue<std::pair<int, int>>& node_dim_pairs_to_process) {
1358 std::vector<std::vector<int>> adjacent_arcs_per_node(num_nodes_);
1359 for (
int i = 0;
i < num_arcs_; ++
i) {
1360 if (tails_[i] == heads_[i])
continue;
1361 adjacent_arcs_per_node[tails_[
i]].push_back(i);
1362 adjacent_arcs_per_node[heads_[
i]].push_back(i);
1364 while (!node_dim_pairs_to_process.empty()) {
1365 const auto [node, dimension] = node_dim_pairs_to_process.front();
1366 const AffineExpression node_expr = node_expression(node, dimension);
1367 DCHECK(!node_expr.IsConstant());
1368 node_dim_pairs_to_process.pop();
1369 for (
const int arc_index : adjacent_arcs_per_node[node]) {
1370 const int tail = tails_[arc_index];
1371 const int head = heads_[arc_index];
1372 DCHECK(node == tail || node == head);
1373 int other_node = node == tail ? head : tail;
1374 if (!node_expression(other_node, dimension).IsConstant()) {
1378 bool candidate_var_is_unique =
true;
1379 for (
const int relation_index :
1380 binary_relation_repository_.IndicesOfRelationsEnforcedBy(
1381 literals_[arc_index])) {
1382 const auto& r = binary_relation_repository_.relation(relation_index);
1383 DCHECK_NE(r.expr.vars[0], kNoIntegerVariable);
1384 DCHECK_NE(r.expr.vars[1], kNoIntegerVariable);
1387 if (var0 == node_expr.var) {
1388 if (candidate_var != kNoIntegerVariable && candidate_var != var1) {
1389 candidate_var_is_unique =
false;
1392 candidate_var = var1;
1394 if (var1 == node_expr.var) {
1395 if (candidate_var != kNoIntegerVariable && candidate_var != var0) {
1396 candidate_var_is_unique =
false;
1399 candidate_var = var0;
1402 if (candidate_var != kNoIntegerVariable && candidate_var_is_unique) {
1403 node_expression(other_node, dimension) = candidate_var;
1404 node_dim_pairs_to_process.push({other_node, dimension});
1410 void ComputeArcRelations(Model* model) {
1411 auto& binary_implication_graph =
1412 *model->GetOrCreate<BinaryImplicationGraph>();
1413 const auto& integer_encoder = *model->GetOrCreate<IntegerEncoder>();
1414 const auto& trail = *model->GetOrCreate<Trail>();
1415 const auto& integer_trail = *model->GetOrCreate<IntegerTrail>();
1416 const auto& root_level_bounds =
1417 *model->GetOrCreate<RootLevelLinear2Bounds>();
1418 const auto& implied_bounds = *model->GetOrCreate<ImpliedBounds>();
1419 DCHECK_EQ(trail.CurrentDecisionLevel(), 0);
1421 flat_arc_dim_relations_ = std::vector<HeadMinusTailBounds>(
1422 num_arcs_ * num_dimensions_, HeadMinusTailBounds());
1423 binary_implication_graph.ResetWorkDone();
1424 for (
int i = 0;
i < num_arcs_; ++
i) {
1425 const int tail = tails_[
i];
1426 const int head = heads_[
i];
1427 if (tail == head)
continue;
1430 const Literal arc_literal_singleton[1] = {literals_[
i]};
1431 absl::Span<const Literal> implied_literals =
1432 binary_implication_graph.WorkDone() < 1e8
1433 ? binary_implication_graph.GetAllImpliedLiterals(literals_[i])
1434 : absl::MakeSpan(arc_literal_singleton);
1438 absl::flat_hash_set<IntegerVariable> implied_views;
1439 absl::flat_hash_set<IntegerVariable> negated_implied_views;
1440 absl::flat_hash_map<IntegerVariable, IntegerValue> implied_lower_bounds;
1441 for (
const Literal implied : implied_literals) {
1442 implied_views.insert(integer_encoder.GetLiteralView(implied));
1443 negated_implied_views.insert(
1444 integer_encoder.GetLiteralView(implied.Negated()));
1445 for (
const auto& [var, lb] :
1446 integer_encoder.GetIntegerLiterals(implied)) {
1447 auto it = implied_lower_bounds.find(var);
1448 if (it == implied_lower_bounds.end()) {
1449 implied_lower_bounds[var] = lb;
1451 it->second = std::max(it->second, lb);
1457 auto get_bounds = [&](
const NodeExpression& expr) {
1458 if (expr.var != kNoIntegerVariable) {
1459 if (implied_views.contains(expr.var)) {
1460 IntegerValue constant_value = expr.coeff + expr.offset;
1461 return std::make_pair(constant_value, constant_value);
1463 if (implied_views.contains(
NegationOf(expr.var))) {
1464 IntegerValue constant_value = -expr.coeff + expr.offset;
1465 return std::make_pair(constant_value, constant_value);
1467 if (negated_implied_views.contains(expr.var) ||
1468 negated_implied_views.contains(
NegationOf(expr.var))) {
1469 return std::make_pair(expr.offset, expr.offset);
1472 const AffineExpression e(expr.var, expr.coeff, expr.offset);
1473 IntegerValue lb = integer_trail.LevelZeroLowerBound(e);
1474 auto it = implied_lower_bounds.find(e.var);
1475 if (it != implied_lower_bounds.end()) {
1476 lb = std::max(lb, e.ValueAt(it->second));
1478 IntegerValue ub = integer_trail.LevelZeroUpperBound(e);
1479 it = implied_lower_bounds.find(
NegationOf(e.var));
1480 if (it != implied_lower_bounds.end()) {
1481 ub = std::min(ub, e.ValueAt(-it->second));
1483 return std::make_pair(lb, ub);
1487 auto to_constant = [&](NodeExpression& expr) {
1488 if (expr.var == kNoIntegerVariable)
return true;
1489 if (integer_trail.IsFixed(expr.var)) {
1490 expr.offset += integer_trail.FixedValue(expr.var) * expr.coeff;
1495 const auto [min, max] = get_bounds(expr);
1497 expr = NodeExpression(kNoIntegerVariable, 0, min);
1502 for (
int dimension = 0; dimension < num_dimensions_; ++dimension) {
1503 NodeExpression tail_expr(node_expression(tail, dimension));
1504 NodeExpression head_expr(node_expression(head, dimension));
1505 NodeExpression tail_const_expr = tail_expr;
1506 NodeExpression head_const_expr = head_expr;
1507 const bool tail_const = to_constant(tail_const_expr);
1508 const bool head_const = to_constant(head_const_expr);
1509 if (tail_const && head_const) {
1510 ProcessNewArcConstantRelation(
1511 i, dimension, head_const_expr.offset - tail_const_expr.offset);
1514 for (
const Literal implied_lit : implied_literals) {
1515 if (tail_const || head_const) {
1516 const IntegerVariable var =
1517 head_const ? tail_expr.
var : head_expr.var;
1518 DCHECK(var != kNoIntegerVariable);
1519 auto [lb, ub] = implied_bounds.GetImpliedBounds(implied_lit, var);
1520 if (lb != kMinIntegerValue || ub != kMaxIntegerValue) {
1521 lb = std::max(lb, integer_trail.LevelZeroLowerBound(var));
1522 ub = std::min(ub, integer_trail.LevelZeroUpperBound(var));
1524 .enforcement = implied_lit,
1525 .expr = LinearExpression2(var, kNoIntegerVariable, 1, 0),
1529 if (var == tail_const_expr.var) {
1530 std::swap(r.expr.vars[0], r.expr.vars[1]);
1531 std::swap(r.expr.coeffs[0], r.expr.coeffs[1]);
1533 ComputeArcRelation(i, dimension, tail_const_expr, head_const_expr,
1537 if (tail_expr.
var != kNoIntegerVariable &&
1538 head_expr.var != kNoIntegerVariable) {
1539 for (
const int relation_index :
1540 binary_relation_repository_.IndicesOfRelationsEnforcedBy(
1542 Relation r = binary_relation_repository_.relation(relation_index);
1543 r.expr.MakeVariablesPositive();
1547 if (r.expr.vars[0] == head_expr.var ||
1548 r.expr.vars[1] == tail_expr.
var) {
1549 std::swap(r.expr.vars[0], r.expr.vars[1]);
1550 std::swap(r.expr.coeffs[0], r.expr.coeffs[1]);
1554 if (!((tail_expr.
var == r.expr.vars[0] &&
1555 head_expr.var == r.expr.vars[1]) ||
1556 (tail_expr.
var == r.expr.vars[1] &&
1557 head_expr.var == r.expr.vars[0]))) {
1560 ComputeArcRelation(i, dimension, tail_expr, head_expr, r,
1567 if (!tail_expr.IsEmpty() && !head_expr.IsEmpty()) {
1568 for (
const auto& [expr, lb, ub] :
1569 root_level_bounds.GetAllBoundsContainingVariables(
1570 tail_expr.
var, head_expr.var)) {
1571 DCHECK_EQ(expr.vars[0], tail_expr.
var);
1572 DCHECK_EQ(expr.vars[1], head_expr.var);
1573 ComputeArcRelation(i, dimension, tail_expr, head_expr,
1574 Relation{Literal(kNoLiteralIndex), expr, lb, ub},
1584 std::pair<IntegerValue, IntegerValue> bounds;
1585 if (head_expr.var == tail_expr.
var) {
1586 const NodeExpression offset_expr(head_expr.var,
1587 head_expr.coeff - tail_expr.
coeff,
1588 head_expr.offset - tail_expr.offset);
1589 bounds = get_bounds(offset_expr);
1591 const auto [tail_min, tail_max] = get_bounds(tail_expr);
1592 const auto [head_min, head_max] = get_bounds(head_expr);
1593 bounds = {head_min - tail_max, head_max - tail_min};
1595 ProcessNewArcRelation(i, dimension,
1598 .lhs = bounds.first,
1599 .rhs = bounds.second});
1606 void ComputeArcRelation(
int arc_index,
int dimension,
1607 const NodeExpression& tail_expr,
1608 const NodeExpression& head_expr, sat::Relation r,
1609 const IntegerTrail& integer_trail) {
1611 (r.expr.vars[0] == tail_expr.var && r.expr.vars[1] == head_expr.var) ||
1612 (r.expr.vars[0] == head_expr.var && r.expr.vars[1] == tail_expr.var));
1613 if (r.expr.vars[0] != tail_expr.var) {
1614 std::swap(r.expr.vars[0], r.expr.vars[1]);
1615 std::swap(r.expr.coeffs[0], r.expr.coeffs[1]);
1617 if (r.expr.coeffs[0] == 0 || tail_expr.coeff == 0) {
1618 LocalRelation result = ComputeArcUnaryRelation(
1619 head_expr, tail_expr, r.expr.coeffs[1], r.lhs, r.rhs);
1620 std::swap(result.tail_coeff, result.head_coeff);
1621 ProcessNewArcRelation(arc_index, dimension, result);
1624 if (r.expr.coeffs[1] == 0 || head_expr.coeff == 0) {
1625 ProcessNewArcRelation(
1626 arc_index, dimension,
1627 ComputeArcUnaryRelation(tail_expr, head_expr, r.expr.coeffs[0], r.lhs,
1631 const auto [lhs, rhs] =
1633 {integer_trail.LowerBound(tail_expr.var),
1634 integer_trail.UpperBound(tail_expr.var)},
1635 {integer_trail.LowerBound(head_expr.var),
1636 integer_trail.UpperBound(head_expr.var)});
1637 ProcessNewArcRelation(
1638 arc_index, dimension,
1639 {.tail_coeff = -1, .head_coeff = 1, .lhs = lhs, .rhs = rhs});
1645 LocalRelation ComputeArcUnaryRelation(
const NodeExpression& tail_expr,
1646 const NodeExpression& head_expr,
1647 IntegerValue coeff, IntegerValue lhs,
1649 DCHECK_NE(coeff, 0);
1655 if (tail_expr.coeff == 0)
return {};
1656 const int64_t k = std::abs(tail_expr.coeff.value()) /
1657 std::gcd(tail_expr.coeff.value(), coeff.value());
1660 const IntegerValue tail_coeff = (k * coeff) / tail_expr.coeff;
1661 const IntegerValue head_coeff = head_expr.coeff != 0 ? 0 : -tail_coeff;
1662 const IntegerValue domain_offset =
1664 CapProdI(head_coeff, head_expr.offset));
1668 const IntegerValue lhs_offset =
CapAddI(
CapProdI(k, lhs), domain_offset);
1669 const IntegerValue rhs_offset =
CapAddI(
CapProdI(k, rhs), domain_offset);
1674 .tail_coeff = tail_coeff,
1675 .head_coeff = head_coeff,
1681 const int num_nodes_;
1682 const int num_arcs_;
1683 absl::Span<const int> tails_;
1684 absl::Span<const int> heads_;
1685 absl::Span<const Literal> literals_;
1686 const ConditionalLinear2Bounds& binary_relation_repository_;
1688 int num_dimensions_;
1689 absl::flat_hash_map<PositiveOnlyIndex, int> dimension_by_var_;
1690 absl::flat_hash_map<Literal, int> num_arcs_per_literal_;
1694 std::vector<std::vector<std::vector<int>>> adjacent_relation_indices_;
1698 std::vector<AffineExpression> flat_node_dim_expressions_;
1699 std::vector<HeadMinusTailBounds> flat_arc_dim_relations_;
1700 std::vector<IntegerValue> flat_shortest_path_lbs_;
1706 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
1707 absl::Span<const Literal> literals,
1710 RouteRelationsBuilder builder(num_nodes, tails, heads, literals, {},
1711 binary_relation_repository);
1712 if (!builder.BuildNodeExpressions())
return result;
1715 std::move(builder.flat_node_dim_expressions());
1721IntegerValue GetDifferenceLowerBound(
1722 const NodeExpression& x_expr,
const NodeExpression& y_expr,
1724 const std::pair<IntegerValue, IntegerValue>& x_var_bounds,
1725 const std::pair<IntegerValue, IntegerValue>& y_var_bounds) {
1748 auto lower_bound = [&](IntegerValue k) {
1749 const IntegerValue y_coeff = y_expr.coeff - k * r.
expr.
coeffs[1];
1750 const IntegerValue x_coeff = k * (-r.
expr.
coeffs[0]) - x_expr.coeff;
1751 return y_coeff * (y_coeff >= 0 ? y_var_bounds.first : y_var_bounds.second) +
1752 x_coeff * (x_coeff >= 0 ? x_var_bounds.first : x_var_bounds.second) +
1753 k * (k >= 0 ? r.
lhs : r.
rhs);
1755 const IntegerValue k_x =
1757 const IntegerValue k_y =
1759 IntegerValue result = lower_bound(0);
1760 result = std::max(result, lower_bound(k_x));
1761 result = std::max(result, lower_bound(k_x + 1));
1762 result = std::max(result, lower_bound(k_y));
1763 result = std::max(result, lower_bound(k_y + 1));
1764 return result + y_expr.offset - x_expr.offset;
1771 const std::pair<IntegerValue, IntegerValue>& x_var_bounds,
1772 const std::pair<IntegerValue, IntegerValue>& y_var_bounds) {
1777 DCHECK_NE(x_expr.
coeff, 0);
1778 DCHECK_NE(y_expr.
coeff, 0);
1781 const IntegerValue lb =
1782 GetDifferenceLowerBound(x_expr, y_expr, r, x_var_bounds, y_var_bounds);
1783 const IntegerValue ub = -GetDifferenceLowerBound(
1784 x_expr.
Negated(), y_expr.
Negated(), r, x_var_bounds, y_var_bounds);
1789 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
1790 absl::Span<const Literal> literals,
1791 absl::Span<const AffineExpression> flat_node_dim_expressions,
1793 CHECK(model !=
nullptr);
1794 if (flat_node_dim_expressions.empty())
return nullptr;
1795 RouteRelationsBuilder builder(
num_nodes, tails, heads, literals,
1796 flat_node_dim_expressions,
1797 binary_relation_repository);
1798 builder.BuildArcRelations(model);
1799 builder.BuildShortestPaths(model);
1801 builder.num_dimensions(), std::move(builder.flat_node_dim_expressions()),
1802 std::move(builder.flat_arc_dim_relations()),
1803 std::move(builder.flat_shortest_path_lbs())));
1804 if (VLOG_IS_ON(2)) helper->LogStats();
1809 int num_dimensions, std::vector<AffineExpression> flat_node_dim_expressions,
1810 std::vector<HeadMinusTailBounds> flat_arc_dim_relations,
1811 std::vector<IntegerValue> flat_shortest_path_lbs)
1812 : num_nodes_(flat_node_dim_expressions.size() / num_dimensions),
1813 num_dimensions_(num_dimensions),
1814 flat_node_dim_expressions_(
std::move(flat_node_dim_expressions)),
1815 flat_arc_dim_relations_(
std::move(flat_arc_dim_relations)),
1816 flat_shortest_path_lbs_(
std::move(flat_shortest_path_lbs)) {
1817 DCHECK_GE(num_dimensions_, 1);
1818 DCHECK_EQ(flat_node_dim_expressions_.size(), num_nodes_ * num_dimensions_);
1822 int arc,
int dimension,
bool negate_expressions)
const {
1826 return negate_expressions ? -r.rhs : r.lhs;
1830 absl::Span<const int> sorted_arc_indices) {
1834 if (!sorted_arc_indices.empty() && sorted_arc_indices.front() ==
i) {
1835 sorted_arc_indices.remove_prefix(1);
1838 for (
int d = 0; d < num_dimensions_; ++d) {
1839 flat_arc_dim_relations_[new_size++] =
1840 flat_arc_dim_relations_[
i * num_dimensions_ + d];
1843 flat_arc_dim_relations_.resize(new_size);
1846void RouteRelationsHelper::LogStats()
const {
1851 for (
int d = 0; d < num_dimensions_; ++d) {
1853 int num_relations = 0;
1863 LOG(INFO) <<
"dimension " << d <<
": " << num_vars <<
" vars and "
1864 << num_relations <<
" relations";
1875IntegerVariable ToPositiveIntegerVariable(
int i) {
1880int ToNodeVariableIndex(IntegerVariable var) {
1882 return var.value() >> 1;
1895 const absl::Span<const int> vars = ct.linear().vars();
1896 if (ct.enforcement_literal().size() != 1 || vars.size() != 2)
continue;
1897 if (vars[0] == vars[1])
continue;
1898 repository.AddPartialRelation(ToLiteral(ct.enforcement_literal(0)),
1899 ToPositiveIntegerVariable(vars[0]),
1900 ToPositiveIntegerVariable(vars[1]));
1907int MaybeFillRoutesConstraintNodeExpressions(
1910 for (
const int node : routes.tails()) {
1911 max_node = std::max(max_node, node);
1913 for (
const int node : routes.heads()) {
1914 max_node = std::max(max_node, node);
1916 const int num_nodes = max_node + 1;
1917 std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1918 std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1919 std::vector<Literal> literals;
1920 literals.reserve(routes.literals_size());
1921 for (
int lit : routes.literals()) {
1922 literals.push_back(ToLiteral(lit));
1926 num_nodes, tails, heads, literals, repository);
1927 if (cumuls.num_dimensions == 0)
return 0;
1929 for (
int d = 0; d < cumuls.num_dimensions; ++d) {
1931 *routes.add_dimensions();
1932 for (
int n = 0; n < num_nodes; ++n) {
1938 ? expr.coeff.value()
1939 : -expr.coeff.value());
1944 return cumuls.num_dimensions;
1951 std::vector<RoutesConstraintProto*> routes_to_fill;
1954 if (!ct.routes().dimensions().empty())
continue;
1955 routes_to_fill.push_back(ct.mutable_routes());
1957 if (routes_to_fill.empty())
return {0, 0};
1959 int total_num_dimensions = 0;
1961 ComputePartialConditionalLinear2Bounds(input_model);
1963 total_num_dimensions +=
1964 MaybeFillRoutesConstraintNodeExpressions(*routes, partial_repository);
1966 return {
static_cast<int>(routes_to_fill.size()), total_num_dimensions};
1971class RoutingCutHelper {
1973 RoutingCutHelper(
int num_nodes,
bool is_route_constraint,
1974 absl::Span<const AffineExpression> flat_node_dim_expressions,
1975 absl::Span<const int> tails, absl::Span<const int> heads,
1976 absl::Span<const Literal> literals,
Model* model)
1977 : num_nodes_(num_nodes),
1978 is_route_constraint_(is_route_constraint),
1979 tails_(tails.
begin(), tails.
end()),
1980 heads_(heads.
begin(), heads.
end()),
1981 literals_(literals.
begin(), literals.
end()),
1982 params_(*model->GetOrCreate<SatParameters>()),
1983 trail_(*model->GetOrCreate<
Trail>()),
1984 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
1985 binary_relation_repository_(
1986 *model->GetOrCreate<ConditionalLinear2Bounds>()),
1987 implied_bounds_(*model->GetOrCreate<ImpliedBounds>()),
1988 random_(model->GetOrCreate<ModelRandomGenerator>()),
1989 encoder_(model->GetOrCreate<IntegerEncoder>()),
1990 root_level_bounds_(*model->GetOrCreate<RootLevelLinear2Bounds>()),
1991 in_subset_(num_nodes, false),
1992 self_arc_literal_(num_nodes_),
1993 self_arc_lp_value_(num_nodes_),
1994 nodes_incoming_weight_(num_nodes_),
1995 nodes_outgoing_weight_(num_nodes_),
1996 min_outgoing_flow_helper_(num_nodes, tails_, heads_, literals_, model),
1997 route_relations_helper_(RouteRelationsHelper::Create(
1998 num_nodes, tails_, heads_, literals_, flat_node_dim_expressions,
1999 *model->GetOrCreate<ConditionalLinear2Bounds>(), model)) {}
2001 int num_nodes()
const {
return num_nodes_; }
2003 bool is_route_constraint()
const {
return is_route_constraint_; }
2006 absl::Span<const ArcWithLpValue> relevant_arcs()
const {
2007 return relevant_arcs_;
2015 absl::Span<const std::pair<int, int>> ordered_arcs()
const {
2016 return ordered_arcs_;
2021 void InitializeForNewLpSolution(LinearConstraintManager* manager);
2025 absl::Span<const ArcWithLpValue> SymmetrizedRelevantArcs() {
2026 symmetrized_relevant_arcs_ = relevant_arcs_;
2030 if (is_route_constraint_) {
2032 for (
const ArcWithLpValue& arc : symmetrized_relevant_arcs_) {
2033 if (arc.tail == 0 || arc.head == 0)
continue;
2034 symmetrized_relevant_arcs_[new_size++] = arc;
2036 symmetrized_relevant_arcs_.resize(new_size);
2039 return symmetrized_relevant_arcs_;
2043 bool TrySubsetCut(
int known_bound, std::string name,
2044 absl::Span<const int> subset,
2045 LinearConstraintManager* manager);
2050 int ShortestPathBound(
int bound, absl::Span<const int> subset);
2063 bool TryBlossomSubsetCut(std::string name,
2064 absl::Span<const ArcWithLpValue> symmetrized_edges,
2065 absl::Span<const int> subset,
2066 LinearConstraintManager* manager);
2070 void TryInfeasiblePathCuts(LinearConstraintManager* manager);
2079 BestChoice PickBestNode(absl::Span<const int> cannot_be_first,
2080 absl::Span<const int> cannot_be_last);
2084 void FilterFalseArcsAtLevelZero();
2092 bool AddOutgoingCut(LinearConstraintManager* manager, std::string name,
2093 int subset_size,
const std::vector<bool>& in_subset,
2094 int64_t rhs_lower_bound);
2096 bool MaybeAddStrongerFlowCut(LinearConstraintManager* manager,
2097 std::string name,
int k,
2098 absl::Span<const int> cannot_be_first,
2099 absl::Span<const int> cannot_be_last,
2100 const std::vector<bool>& in_subset);
2102 void GenerateCutsForInfeasiblePaths(
2103 int start_node,
int max_path_length,
2104 const CompactVectorVector<
int, std::pair<int, double>>&
2105 outgoing_arcs_with_lp_values,
2106 LinearConstraintManager* manager, TopNCuts& top_n_cuts);
2108 const int num_nodes_;
2109 const bool is_route_constraint_;
2110 std::vector<int> tails_;
2111 std::vector<int> heads_;
2112 std::vector<Literal> literals_;
2113 std::vector<ArcWithLpValue> relevant_arcs_;
2114 std::vector<std::pair<int, double>> relevant_arc_indices_;
2115 std::vector<ArcWithLpValue> symmetrized_relevant_arcs_;
2116 std::vector<std::pair<int, int>> ordered_arcs_;
2118 const SatParameters& params_;
2119 const Trail& trail_;
2120 const IntegerTrail& integer_trail_;
2121 const ConditionalLinear2Bounds& binary_relation_repository_;
2122 const ImpliedBounds& implied_bounds_;
2123 ModelRandomGenerator* random_;
2124 IntegerEncoder* encoder_;
2125 const RootLevelLinear2Bounds& root_level_bounds_;
2127 std::vector<bool> in_subset_;
2130 std::vector<int> nodes_with_self_arc_;
2131 std::vector<Literal> self_arc_literal_;
2132 std::vector<double> self_arc_lp_value_;
2135 std::vector<double> nodes_incoming_weight_;
2136 std::vector<double> nodes_outgoing_weight_;
2140 MaxBoundedSubsetSum max_bounded_subset_sum_;
2141 MaxBoundedSubsetSumExact max_bounded_subset_sum_exact_;
2142 MinOutgoingFlowHelper min_outgoing_flow_helper_;
2143 std::unique_ptr<RouteRelationsHelper> route_relations_helper_;
2146void RoutingCutHelper::FilterFalseArcsAtLevelZero() {
2150 const int size =
static_cast<int>(tails_.size());
2151 const VariablesAssignment& assignment = trail_.
Assignment();
2152 std::vector<int> removed_arcs;
2153 for (
int i = 0;
i < size; ++
i) {
2154 if (assignment.LiteralIsFalse(literals_[i])) {
2155 removed_arcs.push_back(i);
2158 tails_[new_size] = tails_[
i];
2159 heads_[new_size] = heads_[
i];
2160 literals_[new_size] = literals_[
i];
2163 if (new_size < size) {
2164 tails_.resize(new_size);
2165 heads_.resize(new_size);
2166 literals_.resize(new_size);
2167 if (route_relations_helper_ !=
nullptr) {
2168 route_relations_helper_->RemoveArcs(removed_arcs);
2173void RoutingCutHelper::InitializeForNewLpSolution(
2174 LinearConstraintManager* manager) {
2175 FilterFalseArcsAtLevelZero();
2179 relevant_arcs_.clear();
2180 relevant_arc_indices_.clear();
2181 nodes_with_self_arc_.clear();
2184 const auto& lp_values = manager->LpValues();
2185 std::vector<std::pair<double, int>> relevant_arc_by_decreasing_lp_values;
2186 for (
int i = 0;
i < literals_.size(); ++
i) {
2187 const IntegerVariable direct_view = encoder_->
GetLiteralView(literals_[i]);
2188 const double lp_value =
2190 ? lp_values[direct_view]
2191 : 1.0 - lp_values[encoder_->
GetLiteralView(literals_[i].Negated())];
2198 if (tails_[i] == heads_[i]) {
2199 const int node = tails_[
i];
2200 nodes_with_self_arc_.push_back(node);
2201 self_arc_lp_value_[node] = lp_value;
2202 self_arc_literal_[node] = literals_[
i];
2206 if (lp_value < 1e-6)
continue;
2207 relevant_arcs_.push_back({tails_[
i], heads_[
i], lp_value});
2208 relevant_arc_indices_.push_back({
i, lp_value});
2209 relevant_arc_by_decreasing_lp_values.push_back({lp_value,
i});
2211 std::sort(relevant_arc_by_decreasing_lp_values.begin(),
2212 relevant_arc_by_decreasing_lp_values.end(),
2213 std::greater<std::pair<double, int>>());
2217 ordered_arcs_.clear();
2218 for (
const auto& [score, arc] : relevant_arc_by_decreasing_lp_values) {
2219 if (is_route_constraint_) {
2220 if (tails_[arc] == 0 || heads_[arc] == 0)
continue;
2222 ordered_arcs_.push_back({tails_[arc], heads_[arc]});
2225 if (absl::GetFlag(FLAGS_cp_model_dump_routes_support_graphs) &&
2227 static std::atomic<int> counter = 0;
2228 const std::string name =
2229 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2230 "support_graph_", counter++,
".pb.txt");
2231 LOG(INFO) <<
"Dumping routes support graph to '" << name <<
"'.";
2232 RoutesSupportGraphProto support_graph_proto;
2233 for (
auto& [tail, head, lp_value] : relevant_arcs_) {
2234 auto* arc = support_graph_proto.add_arc_lp_values();
2235 arc->set_tail(tail);
2236 arc->set_head(head);
2237 arc->set_lp_value(lp_value);
2258std::pair<double, double> GetIncomingAndOutgoingLpFlow(
2259 absl::Span<const ArcWithLpValue> relevant_arcs,
2260 const std::vector<bool>& in_subset,
int outside_node_to_ignore = -1) {
2261 double outgoing_flow = 0.0;
2262 double incoming_flow = 0.0;
2263 for (
const auto arc : relevant_arcs) {
2264 const bool tail_in = in_subset[arc.tail];
2265 const bool head_in = in_subset[arc.head];
2266 if (tail_in && !head_in) {
2267 if (arc.head == outside_node_to_ignore)
continue;
2268 outgoing_flow += arc.lp_value;
2269 }
else if (!tail_in && head_in) {
2270 if (arc.tail == outside_node_to_ignore)
continue;
2271 incoming_flow += arc.lp_value;
2274 return {incoming_flow, outgoing_flow};
2279RoutingCutHelper::BestChoice RoutingCutHelper::PickBestNode(
2280 absl::Span<const int> cannot_be_first,
2281 absl::Span<const int> cannot_be_last) {
2283 std::vector<BestChoice> bests;
2284 double best_weight = 0.0;
2285 for (
const int n : cannot_be_first) {
2286 const double weight = nodes_incoming_weight_[n];
2287 if (bests.empty() || weight > best_weight) {
2289 bests.push_back({n,
true, weight});
2290 best_weight = weight;
2291 }
else if (weight == best_weight) {
2292 bests.push_back({n,
true, weight});
2295 for (
const int n : cannot_be_last) {
2296 const double weight = nodes_outgoing_weight_[n];
2297 if (bests.empty() || weight > best_weight) {
2299 bests.push_back({n,
false, weight});
2300 best_weight = weight;
2301 }
else if (weight == best_weight) {
2302 bests.push_back({n,
false, weight});
2305 if (bests.empty())
return {-1,
true};
2306 return bests.size() == 1
2308 : bests[absl::Uniform<int>(*random_, 0, bests.size())];
2311bool RoutingCutHelper::MaybeAddStrongerFlowCut(
2312 LinearConstraintManager* manager, std::string name,
int k,
2313 absl::Span<const int> cannot_be_first, absl::Span<const int> cannot_be_last,
2314 const std::vector<bool>& in_subset) {
2315 if (cannot_be_first.empty() && cannot_be_last.empty())
return false;
2319 const BestChoice best_choice = PickBestNode(cannot_be_first, cannot_be_last);
2320 const int best_node = best_choice.node;
2321 const bool incoming = best_choice.incoming;
2322 CHECK_GE(best_node, 0);
2324 const auto consider_arc = [incoming, &in_subset](
int t,
int h) {
2325 return incoming ? !in_subset[t] && in_subset[h]
2326 : in_subset[t] && !in_subset[h];
2334 for (
const auto arc : relevant_arcs_) {
2335 if (!consider_arc(arc.tail, arc.head))
continue;
2336 if (arc.tail != best_node && arc.head != best_node) {
2337 flow += arc.lp_value;
2343 if (flow <
static_cast<double>(k) - kTolerance) {
2344 LinearConstraintBuilder cut_builder(encoder_, IntegerValue(k),
2346 for (
int i = 0;
i < tails_.size(); ++
i) {
2347 if (!consider_arc(tails_[i], heads_[i]))
continue;
2348 if (tails_[i] != best_node && heads_[i] != best_node) {
2349 CHECK(cut_builder.AddLiteralTerm(literals_[i], IntegerValue(1)));
2352 return manager->AddCut(cut_builder.Build(), name);
2357bool RoutingCutHelper::AddOutgoingCut(LinearConstraintManager* manager,
2358 std::string name,
int subset_size,
2359 const std::vector<bool>& in_subset,
2360 int64_t rhs_lower_bound) {
2362 const auto [in_flow, out_flow] =
2363 GetIncomingAndOutgoingLpFlow(relevant_arcs_, in_subset);
2364 const double out_violation =
static_cast<double>(rhs_lower_bound) - out_flow;
2365 const double in_violation =
static_cast<double>(rhs_lower_bound) - in_flow;
2366 if (out_violation <= 1e-3 && in_violation <= 1e-3)
return false;
2370 LinearConstraintBuilder outgoing(encoder_, IntegerValue(rhs_lower_bound),
2372 LinearConstraintBuilder incoming(encoder_, IntegerValue(rhs_lower_bound),
2377 for (
int i = 0;
i < tails_.size(); ++
i) {
2378 const bool tail_in = in_subset[tails_[
i]];
2379 const bool head_in = in_subset[heads_[
i]];
2380 if (tail_in && !head_in) {
2381 CHECK(outgoing.AddLiteralTerm(literals_[i], IntegerValue(1)));
2382 }
else if (!tail_in && head_in) {
2383 CHECK(incoming.AddLiteralTerm(literals_[i], IntegerValue(1)));
2391 const double out_efficacy = out_violation / std::sqrt(outgoing.NumTerms());
2392 const double in_efficacy = in_violation / std::sqrt(incoming.NumTerms());
2395 LinearConstraintBuilder& cut_builder =
2396 (out_efficacy >= in_efficacy) ? outgoing : incoming;
2404 int num_optional_nodes_in = 0;
2405 int num_optional_nodes_out = 0;
2406 int optional_loop_in = -1;
2407 int optional_loop_out = -1;
2408 for (
const int n : nodes_with_self_arc_) {
2410 num_optional_nodes_in++;
2411 if (optional_loop_in == -1 ||
2412 self_arc_lp_value_[n] < self_arc_lp_value_[optional_loop_in]) {
2413 optional_loop_in = n;
2416 num_optional_nodes_out++;
2417 if (optional_loop_out == -1 ||
2418 self_arc_lp_value_[n] < self_arc_lp_value_[optional_loop_out]) {
2419 optional_loop_out = n;
2426 CHECK(rhs_lower_bound == 1 || num_optional_nodes_in == 0);
2429 if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2431 if (num_optional_nodes_in == subset_size &&
2432 (optional_loop_in == -1 ||
2433 self_arc_lp_value_[optional_loop_in] > 1.0 - 1e-6)) {
2436 if (num_optional_nodes_out == num_nodes_ - subset_size &&
2437 (optional_loop_out == -1 ||
2438 self_arc_lp_value_[optional_loop_out] > 1.0 - 1e-6)) {
2443 if (num_optional_nodes_in == subset_size) {
2444 CHECK_EQ(rhs_lower_bound, 1);
2445 CHECK(cut_builder.AddLiteralTerm(self_arc_literal_[optional_loop_in],
2450 if (num_optional_nodes_out == num_nodes_ - subset_size) {
2451 CHECK_EQ(rhs_lower_bound, 1);
2452 CHECK(cut_builder.AddLiteralTerm(self_arc_literal_[optional_loop_out],
2457 return manager->AddCut(cut_builder.Build(), name);
2460int RoutingCutHelper::ShortestPathBound(
int bound,
2461 absl::Span<const int> subset) {
2462 if (!is_route_constraint_)
return bound;
2463 if (!nodes_with_self_arc_.empty())
return bound;
2464 if (route_relations_helper_ ==
nullptr)
return bound;
2465 if (!route_relations_helper_->HasShortestPathsInformation())
return bound;
2471 while (bound < subset.size()) {
2473 bound, subset, route_relations_helper_.get())) {
2482bool RoutingCutHelper::TrySubsetCut(
int known_bound, std::string name,
2483 absl::Span<const int> subset,
2484 LinearConstraintManager* manager) {
2485 DCHECK_GE(subset.size(), 1);
2486 DCHECK_LT(subset.size(), num_nodes_);
2489 in_subset_.assign(num_nodes_,
false);
2490 for (
const int n : subset) {
2491 in_subset_[n] =
true;
2496 if (is_route_constraint_) CHECK_NE(n, 0);
2501 bool all_subset_nodes_are_mandatory =
true;
2502 if (is_route_constraint_) {
2503 for (
const int n : nodes_with_self_arc_) {
2504 if (in_subset_[n]) {
2505 all_subset_nodes_are_mandatory =
false;
2518 if (!is_route_constraint_ || !all_subset_nodes_are_mandatory) {
2519 return AddOutgoingCut(manager, name, subset.size(), in_subset_,
2526 double total_outgoing_weight = 0.0;
2527 double total_incoming_weight = 0.0;
2528 nodes_incoming_weight_.assign(num_nodes_, 0);
2529 nodes_outgoing_weight_.assign(num_nodes_, 0);
2530 for (
const auto arc : relevant_arcs_) {
2531 if (in_subset_[arc.tail] != in_subset_[arc.head]) {
2532 if (in_subset_[arc.tail]) {
2533 total_outgoing_weight += arc.lp_value;
2535 total_incoming_weight += arc.lp_value;
2537 nodes_outgoing_weight_[arc.tail] += arc.lp_value;
2538 nodes_incoming_weight_[arc.head] += arc.lp_value;
2542 BestBoundHelper best_bound(name);
2543 best_bound.Update(1,
"");
2544 best_bound.Update(known_bound,
"Hierarchical");
2552 if (route_relations_helper_ !=
nullptr) {
2554 subset, *route_relations_helper_, &best_bound);
2560 best_bound.Update(bound,
"AutomaticTight");
2561 }
else if (subset.size() <
2564 best_bound.Update(bound,
"Automatic");
2567 if (subset.size() <=
2577 const double threshold =
static_cast<double>(best_bound.bound()) - 1e-4;
2578 for (
const int n : subset) {
2579 if (nodes_incoming_weight_[n] > 0.1 &&
2580 total_incoming_weight - nodes_incoming_weight_[n] < threshold &&
2582 best_bound.bound(), subset,
nullptr, manager, n,
2584 best_bound.Update(best_bound.bound(),
"", {n}, {});
2586 if (nodes_outgoing_weight_[n] > 0.1 &&
2587 total_outgoing_weight - nodes_outgoing_weight_[n] < threshold &&
2589 best_bound.bound(), subset,
nullptr, manager, n,
2591 best_bound.Update(best_bound.bound(),
"", {}, {n});
2596 if (MaybeAddStrongerFlowCut(manager,
2597 absl::StrCat(best_bound.name(),
"Lifted"),
2598 best_bound.bound(), best_bound.CannotBeFirst(),
2599 best_bound.CannotBeLast(), in_subset_)) {
2603 return AddOutgoingCut(manager, best_bound.name(), subset.size(), in_subset_,
2604 best_bound.bound());
2607bool RoutingCutHelper::TryBlossomSubsetCut(
2608 std::string name, absl::Span<const ArcWithLpValue> symmetrized_edges,
2609 absl::Span<const int> subset, LinearConstraintManager* manager) {
2610 DCHECK_GE(subset.size(), 1);
2611 DCHECK_LT(subset.size(), num_nodes_);
2614 for (
const int n : subset) in_subset_[n] =
true;
2615 auto cleanup = ::absl::MakeCleanup([subset,
this]() {
2616 for (
const int n : subset) in_subset_[n] =
false;
2621 absl::flat_hash_set<std::pair<int, int>> special_edges;
2622 int num_inverted = 0;
2623 double sum_inverted = 0.0;
2625 double best_change = 1.0;
2626 ArcWithLpValue
const* best_swap =
nullptr;
2627 for (
const ArcWithLpValue& arc : symmetrized_edges) {
2628 if (in_subset_[arc.tail] == in_subset_[arc.head])
continue;
2630 if (arc.lp_value > 0.5) {
2632 special_edges.insert({arc.tail, arc.head});
2633 sum_inverted += 1.0 - arc.lp_value;
2635 sum += arc.lp_value;
2638 const double change = std::abs(2 * arc.lp_value - 1.0);
2639 if (change < best_change) {
2640 best_change = change;
2647 if (num_inverted % 2 == 0) {
2648 if (best_swap ==
nullptr)
return false;
2649 if (special_edges.contains({best_swap->tail, best_swap->head})) {
2651 special_edges.erase({best_swap->tail, best_swap->head});
2652 sum_inverted -= (1.0 - best_swap->lp_value);
2653 sum += best_swap->lp_value;
2656 special_edges.insert({best_swap->tail, best_swap->head});
2657 sum_inverted += (1.0 - best_swap->lp_value);
2658 sum -= best_swap->lp_value;
2661 if (sum + sum_inverted > 0.99)
return false;
2665 if (!is_route_constraint_) {
2666 for (
const auto [tail, head] : special_edges) {
2667 if (tail == 0)
return false;
2674 int best_optional_index = -1;
2675 if (special_edges.size() == 1) {
2676 int num_other_optional = 0;
2677 const auto [special_tail, special_head] = *special_edges.begin();
2678 for (
const int n : nodes_with_self_arc_) {
2679 if (n != special_head && n != special_tail) {
2680 ++num_other_optional;
2681 if (best_optional_index == -1 ||
2682 self_arc_lp_value_[n] < self_arc_lp_value_[best_optional_index]) {
2683 best_optional_index = n;
2687 if (num_other_optional + 2 < num_nodes_) best_optional_index = -1;
2694 int num_actual_inverted = 0;
2695 absl::flat_hash_set<std::pair<int, int>> processed_arcs;
2696 LinearConstraintBuilder builder(encoder_, IntegerValue(1), kMaxIntegerValue);
2700 if (best_optional_index != -1) {
2701 absl::StrAppend(&name,
"_opt");
2706 CHECK(builder.AddLiteralTerm(self_arc_literal_[best_optional_index],
2710 for (
int i = 0;
i < tails_.size(); ++
i) {
2711 if (tails_[i] == heads_[i])
continue;
2712 if (in_subset_[tails_[i]] == in_subset_[heads_[i]])
continue;
2714 const std::pair<int, int> key = {tails_[
i], heads_[
i]};
2715 const std::pair<int, int> r_key = {heads_[
i], tails_[
i]};
2716 const std::pair<int, int> s_key = std::min(key, r_key);
2717 if (special_edges.contains(s_key) && !processed_arcs.contains(key)) {
2718 processed_arcs.insert(key);
2719 CHECK(builder.AddLiteralTerm(literals_[i], IntegerValue(-1)));
2720 if (!processed_arcs.contains(r_key)) {
2721 ++num_actual_inverted;
2727 CHECK(builder.AddLiteralTerm(literals_[i], IntegerValue(1)));
2729 builder.AddConstant(IntegerValue(num_actual_inverted));
2730 if (num_actual_inverted % 2 == 0)
return false;
2732 return manager->AddCut(builder.Build(), name);
2735void RoutingCutHelper::TryInfeasiblePathCuts(LinearConstraintManager* manager) {
2737 if (max_path_length <= 0)
return;
2741 std::vector<int> keys;
2742 std::vector<std::pair<int, double>> values;
2743 for (
const auto [arc_index, lp_value] : relevant_arc_indices_) {
2744 const int tail = tails_[arc_index];
2745 const int head = heads_[arc_index];
2748 DCHECK_NE(tail, head);
2749 if (tail == 0 || head == 0)
continue;
2750 keys.push_back(tail);
2751 values.push_back({arc_index, lp_value});
2753 CompactVectorVector<int, std::pair<int, double>> outgoing_arcs_with_lp_values;
2754 outgoing_arcs_with_lp_values.ResetFromFlatMapping(keys, values, num_nodes_);
2756 TopNCuts top_n_cuts(5);
2757 for (
int i = 1;
i < num_nodes_; ++
i) {
2758 GenerateCutsForInfeasiblePaths(
2759 i, max_path_length, outgoing_arcs_with_lp_values, manager, top_n_cuts);
2761 top_n_cuts.TransferToManager(manager);
2766void RoutingCutHelper::GenerateCutsForInfeasiblePaths(
2767 int start_node,
int max_path_length,
2768 const CompactVectorVector<
int, std::pair<int, double>>&
2769 outgoing_arcs_with_lp_values,
2770 LinearConstraintManager* manager, TopNCuts& top_n_cuts) {
2780 double sum_of_lp_values = 0.0;
2784 absl::flat_hash_map<IntegerVariable, IntegerValue> bounds;
2790 std::stack<State> states;
2793 std::vector<bool> path_nodes(num_nodes_,
false);
2795 std::vector<Literal> path_literals;
2797 path_nodes[start_node] =
true;
2798 states.push({start_node});
2799 while (!states.empty()) {
2800 State& state = states.top();
2801 DCHECK_EQ(states.size(), path_literals.size() + 1);
2803 const int path_length =
static_cast<int>(path_literals.size());
2804 const auto& outgoing_arcs = outgoing_arcs_with_lp_values[state.last_node];
2806 bool new_state_pushed =
false;
2807 while (state.iteration < outgoing_arcs.size()) {
2808 const auto [arc_index, lp_value] = outgoing_arcs[state.iteration++];
2810 next_state.last_node = heads_[arc_index];
2811 next_state.sum_of_lp_values = state.sum_of_lp_values + lp_value;
2812 next_state.iteration = 0;
2814 if (path_nodes[next_state.last_node])
continue;
2823 if (next_state.sum_of_lp_values <=
2824 static_cast<double>(path_length) + 1e-4) {
2828 const Literal next_literal = literals_[arc_index];
2829 next_state.bounds = state.bounds;
2831 binary_relation_repository_, implied_bounds_,
2832 next_literal, state.bounds,
2833 &next_state.bounds)) {
2835 if (path_length < max_path_length) {
2836 path_nodes[next_state.last_node] =
true;
2837 path_literals.push_back(literals_[arc_index]);
2838 states.push(std::move(next_state));
2839 new_state_pushed =
true;
2842 }
else if (path_length > 0) {
2843 LinearConstraintBuilder cut_builder(encoder_, 0, path_length);
2844 for (
const Literal literal : path_literals) {
2845 CHECK(cut_builder.AddLiteralTerm(literal, IntegerValue(1)));
2847 CHECK(cut_builder.AddLiteralTerm(next_literal, IntegerValue(1)));
2848 top_n_cuts.AddCut(cut_builder.Build(),
"CircuitInfeasiblePath",
2849 manager->LpValues());
2852 if (!new_state_pushed) {
2853 if (!path_literals.empty()) {
2854 path_literals.pop_back();
2856 path_nodes[state.last_node] =
false;
2865 absl::Span<
const std::pair<int, int>> arcs,
2866 int stop_at_num_components,
2867 std::vector<int>* subset_data,
2868 std::vector<absl::Span<const int>>* subsets) {
2877 int num_components = num_nodes;
2878 std::vector<int> parent(num_nodes);
2879 std::vector<int> root(num_nodes);
2880 for (
int i = 0;
i < num_nodes; ++
i) {
2884 auto get_root_and_compress_path = [&root](
int node) {
2886 while (root[r] != r) r = root[r];
2887 while (root[node] != r) {
2888 const int next = root[node];
2894 for (
const auto& [initial_tail, initial_head] : arcs) {
2895 if (num_components <= stop_at_num_components)
break;
2896 const int tail = get_root_and_compress_path(initial_tail);
2897 const int head = get_root_and_compress_path(initial_head);
2901 const int new_node = parent.size();
2902 parent.push_back(new_node);
2903 parent[head] = new_node;
2904 parent[tail] = new_node;
2908 root.push_back(new_node);
2909 root[head] = new_node;
2910 root[tail] = new_node;
2925 std::vector<int>* subset_data,
2926 std::vector<absl::Span<const int>>* subsets,
2931 const int num_nodes = parent.size();
2932 subset_data->resize(std::min(num_nodes, node_limit));
2937 for (
int i = 0;
i < num_nodes; ++
i) {
2938 if (parent[
i] !=
i) {
2946 std::vector<int> subtree_starts(num_nodes, -1);
2947 std::vector<int> stack;
2948 stack.reserve(num_nodes);
2949 for (
int i = 0;
i < parent.size(); ++
i) {
2950 if (parent[
i] !=
i)
continue;
2953 while (!stack.empty()) {
2954 const int node = stack.back();
2957 if (subtree_starts[node] >= 0) {
2959 if (node < node_limit) {
2960 (*subset_data)[out_index++] = node;
2962 const int start = subtree_starts[node];
2963 const int size = out_index - start;
2964 subsets->push_back(absl::MakeSpan(&(*subset_data)[start], size));
2969 subtree_starts[node] = out_index;
2970 for (
const int child : graph[node]) {
2971 stack.push_back(child);
2978 int num_nodes, absl::Span<const ArcWithLpValue> relevant_arcs) {
2982 for (
const auto& [tail, head, lp_value] : relevant_arcs) {
2989 std::vector<int> min_cut_subset;
2990 std::vector<int> parent(num_nodes, 0);
2991 for (
int s = 1; s < num_nodes; ++s) {
2992 const int t = parent[s];
2995 bool parent_of_t_in_subset =
false;
2996 for (
const int i : min_cut_subset) {
2997 if (
i == parent[t]) parent_of_t_in_subset =
true;
2998 if (
i != s && parent[
i] == t) parent[
i] = s;
3000 if (parent_of_t_in_subset) {
3001 parent[s] = parent[t];
3011 if (arc.tail <= arc.head)
continue;
3012 std::swap(arc.tail, arc.head);
3014 std::sort(arcs->begin(), arcs->end(),
3016 return std::tie(a.tail, a.head) < std::tie(b.tail, b.head);
3023 if (arc.tail == last_tail && arc.head == last_head) {
3024 (*arcs)[new_size - 1].lp_value += arc.lp_value;
3027 (*arcs)[new_size++] = arc;
3028 last_tail = arc.tail;
3029 last_head = arc.head;
3031 arcs->resize(new_size);
3037 std::vector<absl::Span<const int>> subsets,
3039 const int num_nodes = subset_data.size();
3047 std::vector<bool> cut_was_added(num_nodes,
false);
3048 std::vector<int> shortest_path_lb(num_nodes, 0);
3051 for (
const absl::Span<const int> subset : subsets) {
3052 if (subset.size() <= 1)
continue;
3053 if (subset.size() == num_nodes)
continue;
3062 int lb_for_that_subset = 1;
3063 bool included_cut_was_added =
false;
3064 const int start =
static_cast<int>(subset.data() - subset_data.data());
3065 for (
int i = start;
i < subset.size(); ++
i) {
3066 lb_for_that_subset = std::max(lb_for_that_subset, shortest_path_lb[
i]);
3067 if (cut_was_added[
i]) {
3068 included_cut_was_added =
true;
3076 lb_for_that_subset = helper.ShortestPathBound(lb_for_that_subset, subset);
3077 shortest_path_lb[start] = lb_for_that_subset;
3081 if (included_cut_was_added && subset.size() + 1 < num_nodes)
continue;
3082 if (helper.TrySubsetCut(lb_for_that_subset, cut_name, subset, manager)) {
3084 cut_was_added[start] =
true;
3099 const int num_nodes = helper.num_nodes();
3100 if (num_nodes <= 2)
return;
3102 std::vector<int> subset_data;
3103 std::vector<absl::Span<const int>> subsets;
3112 if (helper.is_route_constraint()) {
3113 subsets.push_back(absl::MakeSpan(&subset_data[1], num_nodes - 1));
3117 TryAllSubsets(
"Circuit", subset_data, subsets, helper, manager);
3133 if (num_added != 0)
return;
3134 absl::Span<const ArcWithLpValue> symmetrized_relevant_arcs =
3135 helper.SymmetrizedRelevantArcs();
3136 std::vector<int> parent =
3142 TryAllSubsets(
"CircuitExact", subset_data, subsets, helper, manager);
3147 if (num_added != 0)
return;
3148 if (num_nodes <= 2)
return;
3149 std::vector<ArcWithLpValue> for_blossom;
3150 for_blossom.reserve(symmetrized_relevant_arcs.size());
3152 if (arc.lp_value > 0.5) arc.lp_value = 1.0 - arc.lp_value;
3153 if (arc.lp_value < 1e-6)
continue;
3154 for_blossom.push_back(arc);
3158 int last_added_start = -1;
3159 for (
const absl::Span<const int> subset : subsets) {
3160 if (subset.size() <= 1)
continue;
3161 if (subset.size() == num_nodes)
continue;
3162 const int start =
static_cast<int>(subset.data() - subset_data.data());
3163 if (start <= last_added_start)
continue;
3164 if (helper.TryBlossomSubsetCut(
"CircuitBlossom", symmetrized_relevant_arcs,
3167 last_added_start = start;
3175std::vector<IntegerVariable> GetAssociatedVariables(
3176 absl::Span<const Literal> literals,
Model* model) {
3177 auto* encoder = model->GetOrCreate<IntegerEncoder>();
3178 std::vector<IntegerVariable> result;
3179 for (
const Literal l : literals) {
3180 const IntegerVariable direct_view = encoder->GetLiteralView(l);
3182 result.push_back(direct_view);
3184 result.push_back(encoder->GetLiteralView(l.Negated()));
3197 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
3198 absl::Span<const Literal> literals,
Model* model) {
3199 auto helper = std::make_unique<RoutingCutHelper>(
3202 absl::Span<const AffineExpression>{}, tails, heads, literals, model);
3204 result.
vars = GetAssociatedVariables(literals, model);
3205 result.generate_cuts =
3207 helper->InitializeForNewLpSolution(manager);
3215 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
3216 absl::Span<const Literal> literals,
3217 absl::Span<const AffineExpression> flat_node_dim_expressions,
3219 auto helper = std::make_unique<RoutingCutHelper>(
3220 num_nodes,
true, flat_node_dim_expressions, tails,
3221 heads, literals, model);
3223 result.
vars = GetAssociatedVariables(literals, model);
3226 manager->CacheReducedCostsInfo();
3227 helper->InitializeForNewLpSolution(manager);
3229 helper->TryInfeasiblePathCuts(manager);
3238 int num_nodes, absl::Span<const int> tails, absl::Span<const int> heads,
3239 absl::Span<const AffineExpression> arc_capacities,
3240 std::function<
void(
const std::vector<bool>& in_subset,
3241 IntegerValue* min_incoming_flow,
3242 IntegerValue* min_outgoing_flow)>
3252 IntegerValue offset;
3254 std::vector<Arc> relevant_arcs;
3261 std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
3262 for (
int i = 0;
i < arc_capacities.size(); ++
i) {
3263 const double lp_value = arc_capacities[
i].LpValue(lp_values);
3264 if (!arc_capacities[
i].IsConstant()) {
3265 gcd = std::gcd(gcd, std::abs(arc_capacities[
i].coeff.value()));
3267 if (lp_value < 1e-6 && arc_capacities[
i].constant == 0)
continue;
3268 relevant_arcs.push_back(
3269 {tails[
i], heads[
i], lp_value, arc_capacities[
i].constant});
3270 arc_by_decreasing_lp_values.push_back({lp_value,
i});
3272 if (gcd == 0)
return;
3273 std::sort(arc_by_decreasing_lp_values.begin(),
3274 arc_by_decreasing_lp_values.end(),
3275 std::greater<std::pair<double, int>>());
3277 std::vector<std::pair<int, int>> ordered_arcs;
3278 for (
const auto& [score, arc] : arc_by_decreasing_lp_values) {
3279 if (tails[arc] == -1)
continue;
3280 if (heads[arc] == -1)
continue;
3281 ordered_arcs.push_back({tails[arc], heads[arc]});
3283 std::vector<int> subset_data;
3284 std::vector<absl::Span<const int>> subsets;
3290 std::vector<bool> in_subset(num_nodes,
false);
3291 for (
const absl::Span<const int> subset : subsets) {
3292 DCHECK(!subset.empty());
3293 DCHECK_LE(subset.size(), num_nodes);
3296 for (
const int n : subset) in_subset[n] =
true;
3298 IntegerValue min_incoming_flow;
3299 IntegerValue min_outgoing_flow;
3300 get_flows(in_subset, &min_incoming_flow, &min_outgoing_flow);
3304 IntegerValue incoming_offset(0);
3305 IntegerValue outgoing_offset(0);
3312 double lp_outgoing_flow = 0.0;
3313 double lp_incoming_flow = 0.0;
3314 for (
const auto arc : relevant_arcs) {
3315 if (arc.tail != -1 && in_subset[arc.tail]) {
3316 if (arc.head == -1 || !in_subset[arc.head]) {
3317 incoming_offset += arc.offset;
3318 lp_outgoing_flow += arc.lp_value;
3321 if (arc.head != -1 && in_subset[arc.head]) {
3322 outgoing_offset += arc.offset;
3323 lp_incoming_flow += arc.lp_value;
3334 const IntegerValue test_incoming = min_incoming_flow - incoming_offset;
3335 const IntegerValue new_incoming =
3336 CeilRatio(test_incoming, IntegerValue(gcd)) * IntegerValue(gcd);
3337 const IntegerValue incoming_delta = new_incoming - test_incoming;
3338 if (incoming_delta > 0) min_incoming_flow += incoming_delta;
3341 const IntegerValue test_outgoing = min_outgoing_flow - outgoing_offset;
3342 const IntegerValue new_outgoing =
3343 CeilRatio(test_outgoing, IntegerValue(gcd)) * IntegerValue(gcd);
3344 const IntegerValue outgoing_delta = new_outgoing - test_outgoing;
3345 if (outgoing_delta > 0) min_outgoing_flow += outgoing_delta;
3348 if (lp_incoming_flow <
ToDouble(min_incoming_flow) - 1e-6) {
3349 VLOG(2) <<
"INCOMING CUT " << lp_incoming_flow
3350 <<
" >= " << min_incoming_flow <<
" size " << subset.size()
3351 <<
" offset " << incoming_offset <<
" gcd " << gcd;
3353 for (
int i = 0;
i < tails.size(); ++
i) {
3354 if ((tails[
i] == -1 || !in_subset[tails[
i]]) &&
3355 (heads[
i] != -1 && in_subset[heads[
i]])) {
3356 cut.
AddTerm(arc_capacities[
i], 1.0);
3362 if (lp_outgoing_flow <
ToDouble(min_outgoing_flow) - 1e-6) {
3363 VLOG(2) <<
"OUGOING CUT " << lp_outgoing_flow
3364 <<
" >= " << min_outgoing_flow <<
" size " << subset.size()
3365 <<
" offset " << outgoing_offset <<
" gcd " << gcd;
3367 for (
int i = 0;
i < tails.size(); ++
i) {
3368 if ((tails[
i] != -1 && in_subset[tails[
i]]) &&
3369 (heads[
i] == -1 || !in_subset[heads[
i]])) {
3370 cut.
AddTerm(arc_capacities[
i], 1.0);
3377 for (
const int n : subset) in_subset[n] =
false;
3382 int num_nodes,
const std::vector<int>& tails,
const std::vector<int>& heads,
3383 const std::vector<AffineExpression>& arc_capacities,
3384 std::function<
void(
const std::vector<bool>& in_subset,
3385 IntegerValue* min_incoming_flow,
3386 IntegerValue* min_outgoing_flow)>
3391 if (!expr.IsConstant()) result.
vars.push_back(expr.var);
3395 manager->LpValues(), manager, model);
std::vector< std::vector< T > > FindConnectedComponents()
bool AddEdge(T node1, T node2)
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
ArcIndex AddArcWithCapacity(NodeIndex tail, NodeIndex head, FlowQuantity capacity)
Status Solve(NodeIndex source, NodeIndex sink)
void GetSourceSideMinCut(std::vector< NodeIndex > *result)
void Update(int new_bound, std::string name, absl::Span< const int > cannot_be_first={}, absl::Span< const int > cannot_be_last={})
void AppendToLastVector(const V &value)
int Add(absl::Span< const V > values)
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL mutable_constraints(int index)
IntegerVariable GetLiteralView(Literal lit) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void AddTerm(IntegerVariable var, IntegerValue coeff)
absl::int128 ReducedCostsGap() const
absl::int128 GetLiteralReducedCost(Literal l) const
bool AddCut(LinearConstraint ct, std::string type_name, std::string extra_info="")
void set_offset(::int64_t value)
int ComputeTightMinOutgoingFlow(absl::Span< const int > subset)
int ComputeMinOutgoingFlow(absl::Span< const int > subset)
bool SubsetMightBeServedWithKRoutes(int k, absl::Span< const int > subset, RouteRelationsHelper *helper=nullptr, LinearConstraintManager *manager=nullptr, int special_node=-1, bool use_forward_direction=true)
int ComputeDimensionBasedMinOutgoingFlow(absl::Span< const int > subset, const RouteRelationsHelper &helper, BestBoundHelper *best_bound)
MinOutgoingFlowHelper(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
bool PathExists(int tail, int head) const
const HeadMinusTailBounds & GetArcRelation(int arc, int dimension) const
IntegerValue GetArcOffsetLowerBound(int arc, int dimension, bool negate_expressions) const
const AffineExpression & GetNodeExpression(int node, int dimension) const
static std::unique_ptr< RouteRelationsHelper > Create(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, absl::Span< const AffineExpression > flat_node_dim_expressions, const ConditionalLinear2Bounds &binary_relation_repository, Model *model)
bool PropagateLocalBoundsUsingShortestPaths(const IntegerTrail &integer_trail, int tail, int head, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output) const
int num_dimensions() const
void RemoveArcs(absl::Span< const int > sorted_arc_indices)
RouteRelationsHelper()=default
RoutesConstraintProto_NodeExpressions NodeExpressions
::int32_t routing_cut_subset_size_for_shortest_paths_bound() const
::int32_t routing_cut_subset_size_for_exact_binary_relation_bound() const
::int32_t routing_cut_max_infeasible_path_length() const
::int32_t routing_cut_subset_size_for_binary_relation_bound() const
::int32_t routing_cut_subset_size_for_tight_binary_relation_bound() const
int ComputeMinNumberOfBins(absl::Span< ItemOrBin > objects, std::vector< int > &objects_that_cannot_be_bin_and_reach_minimum, std::string &info)
bool UseDpToTightenCapacities(absl::Span< ItemOrBin > objects)
bool GreedyPackingWorks(int num_bins, absl::Span< const ItemOrBin > objects)
int CurrentDecisionLevel() const
const VariablesAssignment & Assignment() const
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
void Build(std::vector< ArcIndexType > *permutation) final
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
constexpr double kTolerance
H AbslHashValue(H h, std::shared_ptr< Variable > i)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
RoutingCumulExpressions DetectDimensionsAndCumulExpressions(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, const ConditionalLinear2Bounds &binary_relation_repository)
bool RefIsPositive(int ref)
bool PropagateLocalBounds(const IntegerTrail &integer_trail, const RootLevelLinear2Bounds &root_level_bounds, const ConditionalLinear2Bounds &repository, const ImpliedBounds &implied_bounds, Literal lit, const absl::flat_hash_map< IntegerVariable, IntegerValue > &input, absl::flat_hash_map< IntegerVariable, IntegerValue > *output)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, Model *model)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::vector< int > ComputeGomoryHuTree(int num_nodes, absl::Span< const ArcWithLpValue > relevant_arcs)
const LiteralIndex kNoLiteralIndex(-1)
std::pair< int, int > MaybeFillMissingRoutesConstraintNodeExpressions(const CpModelProto &input_model, CpModelProto &output_model)
int TryAllSubsets(std::string cut_name, absl::Span< const int > subset_data, std::vector< absl::Span< const int > > subsets, RoutingCutHelper &helper, LinearConstraintManager *manager)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
std::pair< IntegerValue, IntegerValue > GetDifferenceBounds(const NodeExpression &x_expr, const NodeExpression &y_expr, const sat::Relation &r, const std::pair< IntegerValue, IntegerValue > &x_var_bounds, const std::pair< IntegerValue, IntegerValue > &y_var_bounds)
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
CutGenerator CreateFlowCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< AffineExpression > &arc_capacities, std::function< void(const std::vector< bool > &in_subset, IntegerValue *min_incoming_flow, IntegerValue *min_outgoing_flow)> get_flows, Model *model)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
void SeparateSubtourInequalities(RoutingCutHelper &helper, LinearConstraintManager *manager)
CutGenerator CreateCVRPCutGenerator(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const Literal > literals, absl::Span< const AffineExpression > flat_node_dim_expressions, Model *model)
void SeparateFlowInequalities(int num_nodes, absl::Span< const int > tails, absl::Span< const int > heads, absl::Span< const AffineExpression > arc_capacities, std::function< void(const std::vector< bool > &in_subset, IntegerValue *min_incoming_flow, IntegerValue *min_outgoing_flow)> get_flows, const util_intops::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager, Model *model)
void SymmetrizeArcs(std::vector< ArcWithLpValue > *arcs)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
void ExtractAllSubsetsFromForest(absl::Span< const int > parent, std::vector< int > *subset_data, std::vector< absl::Span< const int > > *subsets, int node_limit)
bool VariableIsPositive(IntegerVariable i)
bool AtMinOrMaxInt64I(IntegerValue t)
void GenerateInterestingSubsets(int num_nodes, absl::Span< const std::pair< int, int > > arcs, int stop_at_num_components, std::vector< int > *subset_data, std::vector< absl::Span< const int > > *subsets)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
double ToDouble(IntegerValue value)
LinearRange operator==(const LinearExpr &lhs, const LinearExpr &rhs)
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
ABSL_FLAG(bool, cp_model_dump_routes_support_graphs, false, "DEBUG ONLY. When set to true, SolveCpModel() dumps the arcs with " "non-zero LP values of the routes constraints, at decision level 0, " "which are used to subsequently generate cuts. The values are " "written as a SupportGraphProto in text format to " "'FLAGS_cp_model_dump_prefix'support_graph_{counter}.pb.txt.")
AffineExpression Negated() const
absl::AnyInvocable< bool(LinearConstraintManager *manager)> generate_cuts
std::vector< IntegerVariable > vars
NodeExpression Negated() const
std::vector< AffineExpression > flat_node_dim_expressions