26#include "absl/algorithm/container.h"
27#include "absl/container/btree_set.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/container/inlined_vector.h"
31#include "absl/log/check.h"
32#include "absl/log/log.h"
33#include "absl/log/vlog_is_on.h"
34#include "absl/types/span.h"
61 DCHECK_NE(expr.
coeffs[0], 0);
62 DCHECK_NE(expr.
coeffs[1], 0);
64 auto [it, inserted] = expr_to_index_.insert({expr, exprs_.size()});
66 CHECK_LT(2 * exprs_.size() + 1,
67 std::numeric_limits<LinearExpression2Index>::max());
68 exprs_.push_back(expr);
70 const LinearExpression2Index result =
71 negated ?
NegationOf(LinearExpression2Index(2 * it->second))
72 : LinearExpression2Index(2 * it->second);
74 if (!inserted)
return result;
79 if (var1 > var2) std::swap(var1, var2);
80 var_pair_to_bounds_[{var1, var2}].push_back(result);
81 var_to_bounds_[var1].push_back(result);
82 var_to_bounds_[var2].push_back(result);
91 for (
const int id : propagator_ids_) {
92 watcher_->CallOnNextPropagate(
id);
96 if (var >= var_timestamp_.size()) {
97 var_timestamp_.resize(var + 1, 0);
99 var_timestamp_[var]++;
105 return var < var_timestamp_.size() ? var_timestamp_[var] : 0;
111 const IntegerValue zero_level_ub = integer_trail_->LevelZeroUpperBound(expr);
112 if (ub >= zero_level_ub) {
115 if (best_upper_bounds_.size() <= index) {
118 if (ub >= best_upper_bounds_[index]) {
121 best_upper_bounds_[index] = ub;
124 linear2_watcher_->NotifyBoundChanged(expr);
131 if (index >= in_coeff_one_lookup_.size()) {
132 in_coeff_one_lookup_.resize(index + 1,
false);
134 if (!in_coeff_one_lookup_[index]) {
135 const IntegerVariable a =
137 const IntegerVariable
b =
140 coeff_one_var_lookup_.resize(integer_trail_->NumIntegerVariables());
141 in_coeff_one_lookup_[index] =
true;
142 coeff_one_var_lookup_[a].push_back({
b, index});
143 coeff_one_var_lookup_[
b].push_back({a, index});
152 if (shared_linear2_bounds_ !=
nullptr) {
154 const int proto_var0 =
155 cp_model_mapping_->GetProtoVariableFromIntegerVariable(
157 const int proto_var1 =
158 cp_model_mapping_->GetProtoVariableFromIntegerVariable(
160 if (proto_var0 >= 0 && proto_var1 >= 0) {
164 key.
vars[0] = proto_var0;
167 key.
vars[1] = proto_var1;
170 shared_linear2_bounds_->Add(shared_linear2_bounds_id_, key, lb, ub);
181 if (var >= coeff_one_var_lookup_.size())
return 0;
182 if (
NegationOf(var) >= coeff_one_var_lookup_.size())
return 0;
187 for (
const auto [a, a_index] : coeff_one_var_lookup_[var]) {
189 const IntegerValue a_ub = best_upper_bounds_[a_index];
190 for (
const auto [
b, b_index] : coeff_one_var_lookup_[
NegationOf(var)]) {
193 if (++work_done > work_limit)
return work_done;
196 AddUpperBound(candidate, a_ub + best_upper_bounds_[b_index]);
203 if (!VLOG_IS_ON(1))
return;
204 std::vector<std::pair<std::string, int64_t>> stats;
205 stats.push_back({
"RootLevelLinear2Bounds/num_updates", num_updates_});
206 shared_stats_->AddStats(stats);
211 IntegerValue known_ub = integer_trail_->LevelZeroUpperBound(expr);
212 IntegerValue known_lb = integer_trail_->LevelZeroLowerBound(expr);
218 if (expr.
coeffs[0] == 0) {
221 DCHECK_NE(expr.
coeffs[1], 0);
224 const LinearExpression2Index index = lin2_indices_->GetIndex(expr);
240 LinearExpression2Index index)
const {
241 if (best_upper_bounds_.size() <= index) {
244 return best_upper_bounds_[index];
247std::vector<std::pair<LinearExpression2, IntegerValue>>
249 std::vector<std::pair<LinearExpression2, IntegerValue>> result;
250 for (LinearExpression2Index index = LinearExpression2Index{0};
251 index < best_upper_bounds_.size(); ++index) {
252 const IntegerValue ub = best_upper_bounds_[index];
256 result.push_back({expr, ub});
259 std::sort(result.begin(), result.end());
263std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>>
265 IntegerVariable var)
const {
266 std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>> result;
267 for (
const LinearExpression2Index index :
268 lin2_indices_->GetAllLinear2ContainingVariable(var)) {
272 const IntegerValue trail_lb = integer_trail_->LevelZeroLowerBound(expr);
273 const IntegerValue trail_ub = integer_trail_->LevelZeroUpperBound(expr);
274 if (lb <= trail_lb && ub >= trail_ub)
continue;
278 explicit_vars_expr.
coeffs[0] = -explicit_vars_expr.
coeffs[0];
282 explicit_vars_expr.
coeffs[1] = -explicit_vars_expr.
coeffs[1];
284 if (explicit_vars_expr.
vars[1] == var) {
285 std::swap(explicit_vars_expr.
vars[0], explicit_vars_expr.
vars[1]);
286 std::swap(explicit_vars_expr.
coeffs[0], explicit_vars_expr.
coeffs[1]);
288 DCHECK(explicit_vars_expr.
vars[0] == var);
290 {explicit_vars_expr, std::max(lb, trail_lb), std::min(ub, trail_ub)});
298std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>>
300 IntegerVariable var1, IntegerVariable var2)
const {
301 std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>> result;
302 for (
const LinearExpression2Index index :
303 lin2_indices_->GetAllLinear2ContainingVariables(var1, var2)) {
307 const IntegerValue trail_lb = integer_trail_->LevelZeroLowerBound(expr);
308 const IntegerValue trail_ub = integer_trail_->LevelZeroUpperBound(expr);
309 if (lb <= trail_lb && ub >= trail_ub)
continue;
315 explicit_vars_expr.
coeffs[0] = -explicit_vars_expr.
coeffs[0];
320 explicit_vars_expr.
coeffs[1] = -explicit_vars_expr.
coeffs[1];
322 if (explicit_vars_expr.
vars[1] == var1) {
323 std::swap(explicit_vars_expr.
vars[0], explicit_vars_expr.
vars[1]);
324 std::swap(explicit_vars_expr.
coeffs[0], explicit_vars_expr.
coeffs[1]);
326 DCHECK(explicit_vars_expr.
vars[0] == var1 &&
327 explicit_vars_expr.
vars[1] == var2);
329 {explicit_vars_expr, std::max(lb, trail_lb), std::min(ub, trail_ub)});
334absl::Span<const std::pair<IntegerVariable, LinearExpression2Index>>
336 IntegerVariable var)
const {
337 if (var >= coeff_one_var_lookup_.size())
return {};
338 return coeff_one_var_lookup_[var];
342 if (!VLOG_IS_ON(1))
return;
343 std::vector<std::pair<std::string, int64_t>> stats;
344 stats.push_back({
"EnforcedLinear2Bounds/num_conditional_relation_updates",
345 num_conditional_relation_updates_});
346 shared_stats_->AddStats(stats);
350 absl::Span<const Literal> enforcements, LinearExpression2Index lin2_index,
352 DCHECK_EQ(trail_->CurrentDecisionLevel(), stored_level_);
355 for (
const Literal l : enforcements) {
356 CHECK(trail_->Assignment().LiteralIsTrue(l));
360 if (enforcements.empty() || trail_->CurrentDecisionLevel() == 0) {
361 root_level_bounds_->AddUpperBound(lin2_index, rhs);
365 if (rhs >= root_level_bounds_->LevelZeroUpperBound(lin2_index))
return;
368 linear2_watcher_->NotifyBoundChanged(expr);
369 ++num_conditional_relation_updates_;
371 const int new_index = conditional_stack_.size();
372 if (conditional_relations_.size() <= lin2_index) {
373 conditional_relations_.resize(lin2_index.value() + 1, -1);
375 if (conditional_relations_[lin2_index] == -1) {
376 conditional_relations_[lin2_index] = new_index;
377 CreateLevelEntryIfNeeded();
378 conditional_stack_.emplace_back(-1, rhs, lin2_index,
383 std::max(expr.
vars[0].value(), expr.
vars[1].value()) + 1;
384 if (new_size > conditional_var_lookup_.size()) {
385 conditional_var_lookup_.resize(new_size);
387 conditional_var_lookup_[expr.
vars[0]].push_back(
388 {expr.
vars[1], lin2_index});
389 conditional_var_lookup_[expr.
vars[1]].push_back(
390 {expr.
vars[0], lin2_index});
393 const int prev_entry = conditional_relations_[lin2_index];
394 if (rhs >= conditional_stack_[prev_entry].rhs)
return;
397 conditional_relations_[lin2_index] = new_index;
398 CreateLevelEntryIfNeeded();
399 conditional_stack_.emplace_back(prev_entry, rhs, lin2_index, enforcements);
403void EnforcedLinear2Bounds::CreateLevelEntryIfNeeded() {
405 if (!level_to_stack_size_.empty() &&
406 level_to_stack_size_.back().first == current)
408 level_to_stack_size_.push_back({current, conditional_stack_.size()});
413 stored_level_ = level;
414 while (!level_to_stack_size_.empty() &&
415 level_to_stack_size_.back().first > level) {
416 const int target = level_to_stack_size_.back().second;
417 DCHECK_GE(conditional_stack_.size(), target);
418 while (conditional_stack_.size() > target) {
419 const ConditionalEntry& back = conditional_stack_.back();
420 if (back.prev_entry != -1) {
421 conditional_relations_[back.key] = back.prev_entry;
423 conditional_relations_[back.key] = -1;
427 DCHECK_EQ(conditional_var_lookup_[expr.
vars[0]].back().first,
429 DCHECK_EQ(conditional_var_lookup_[expr.
vars[1]].back().first,
431 conditional_var_lookup_[expr.
vars[0]].pop_back();
432 conditional_var_lookup_[expr.
vars[1]].pop_back();
435 conditional_stack_.pop_back();
437 level_to_stack_size_.pop_back();
442 LinearExpression2Index index, IntegerValue ub,
443 std::vector<Literal>* literal_reason,
444 std::vector<IntegerLiteral>* )
const {
445 DCHECK_EQ(trail_->CurrentDecisionLevel(), stored_level_);
446 if (ub >= root_level_bounds_->LevelZeroUpperBound(index))
return;
447 DCHECK_LT(index, conditional_relations_.size());
448 const int entry_index = conditional_relations_[index];
449 DCHECK_NE(entry_index, -1);
451 const ConditionalEntry& entry = conditional_stack_[entry_index];
453 for (
const Literal l : entry.enforcements) {
454 CHECK(trail_->Assignment().LiteralIsTrue(l));
457 DCHECK_LE(entry.rhs, ub);
458 for (
const Literal l : entry.enforcements) {
459 literal_reason->push_back(l.
Negated());
464 LinearExpression2Index index)
const {
465 DCHECK_EQ(trail_->CurrentDecisionLevel(), stored_level_);
466 if (index >= conditional_relations_.size()) {
469 const int entry_index = conditional_relations_[index];
470 if (entry_index == -1) {
473 const ConditionalEntry& entry = conditional_stack_[entry_index];
475 for (
const Literal l : entry.enforcements) {
476 CHECK(trail_->Assignment().LiteralIsTrue(l));
492 const int64_t in_timestamp = root_level_bounds_->num_updates();
493 if (in_timestamp <= build_timestamp_)
return true;
494 build_timestamp_ = in_timestamp;
496 const std::vector<std::pair<LinearExpression2, IntegerValue>>
497 root_relations_sorted =
498 root_level_bounds_->GetSortedNonTrivialUpperBounds();
500 for (
const auto [expr, _] : root_relations_sorted) {
518 for (
const auto [expr, negated_offset] : root_relations_sorted) {
520 DCHECK_GT(expr.coeffs[0], 0);
521 DCHECK_GT(expr.coeffs[1], 0);
528 const IntegerValue offset = -negated_offset;
529 if (offset < 0)
continue;
531 if (expr.coeffs[0] != 1 || expr.coeffs[1] != 1) {
541 bool graph_has_cycle =
false;
542 topological_order_.clear();
543 while (sorter.
GetNext(&next, &graph_has_cycle,
nullptr)) {
544 topological_order_.push_back(IntegerVariable(next));
545 if (graph_has_cycle) {
550 is_dag_ = !graph_has_cycle;
555 const int kWorkLimit = params_->transitive_precedences_work_limit();
556 if (kWorkLimit > 0) {
557 for (
const IntegerVariable var : topological_order_) {
558 const int work = root_level_bounds_->AugmentSimpleRelations(
559 var, kWorkLimit - total_work);
561 if (total_work >= kWorkLimit)
break;
565 build_timestamp_ = root_level_bounds_->num_updates();
566 VLOG(2) <<
"Full precedences. Work=" << total_work
567 <<
" Relations=" << root_relations_sorted.size()
568 <<
" num_added=" << build_timestamp_ - in_timestamp;
578 absl::Span<const IntegerVariable> vars,
579 std::vector<FullIntegerPrecedence>* output) {
582 if (!is_dag_)
return;
589 absl::flat_hash_set<IntegerVariable> is_interesting;
590 absl::flat_hash_set<IntegerVariable> to_consider(vars.begin(), vars.end());
591 absl::flat_hash_map<IntegerVariable,
592 absl::flat_hash_map<IntegerVariable, IntegerValue>>
593 vars_before_with_offset;
594 absl::flat_hash_map<IntegerVariable, IntegerValue> tail_map;
595 for (
const IntegerVariable tail_var : topological_order_) {
596 if (!to_consider.contains(tail_var) &&
597 !vars_before_with_offset.contains(tail_var)) {
605 const auto it = vars_before_with_offset.find(tail_var);
606 if (it != vars_before_with_offset.end()) {
607 tail_map = it->second;
612 for (
const auto [neg_head_var, index] :
613 root_level_bounds_->GetVariablesInSimpleRelation(tail_var)) {
614 const IntegerVariable head_var =
NegationOf(neg_head_var);
615 const IntegerValue arc_offset =
616 -root_level_bounds_->GetUpperBoundNoTrail(index);
619 if (tail_map.empty() && !to_consider.contains(tail_var))
continue;
621 auto& to_update = vars_before_with_offset[head_var];
622 for (
const auto& [var_before, offset] : tail_map) {
623 if (!to_update.contains(var_before)) {
624 to_update[var_before] = arc_offset + offset;
626 to_update[var_before] =
627 std::max(arc_offset + offset, to_update[var_before]);
630 if (to_consider.contains(tail_var)) {
631 if (!to_update.contains(tail_var)) {
632 to_update[tail_var] = arc_offset;
634 to_update[tail_var] = std::max(arc_offset, to_update[tail_var]);
642 if (to_update.size() > tail_map.size() + 1) {
643 is_interesting.insert(head_var);
645 is_interesting.erase(head_var);
653 if (!is_interesting.contains(tail_var))
continue;
654 if (tail_map.size() == 1)
continue;
659 for (
int i = 0;
i < vars.size(); ++
i) {
660 const auto offset_it = tail_map.find(vars[
i]);
661 if (offset_it == tail_map.end())
continue;
663 data.
offsets.push_back(offset_it->second);
664 min_offset = std::min(data.
offsets.back(), min_offset);
666 output->push_back(std::move(data));
671 absl::Span<const IntegerVariable> vars,
672 std::vector<PrecedenceData>* output) {
673 const int needed_size = integer_trail_->NumIntegerVariables().value();
674 var_to_degree_.resize(needed_size);
675 var_to_last_index_.resize(needed_size);
676 var_with_positive_degree_.resize(needed_size);
677 tmp_precedences_.clear();
681 int num_relevants = 0;
682 IntegerVariable* var_with_positive_degree = var_with_positive_degree_.data();
683 int* var_to_degree = var_to_degree_.data();
684 int* var_to_last_index = var_to_last_index_.data();
687 absl::Span<const std::pair<IntegerVariable, LinearExpression2Index>>
689 for (
const auto [other, lin2_index] : v) {
690 const IntegerVariable after =
NegationOf(other);
691 DCHECK_LT(after, needed_size);
692 if (var_to_degree[after.value()] == 0) {
693 var_with_positive_degree[num_relevants++] = after;
696 if (var_to_last_index[after.value()] == var_index)
continue;
699 tmp_precedences_.push_back({after, var_index, lin2_index});
700 var_to_degree[after.value()]++;
701 var_to_last_index[after.value()] = var_index;
705 for (
int var_index = 0; var_index < vars.size(); ++var_index) {
706 const IntegerVariable var = vars[var_index];
707 process(var_index, root_level_bounds_->GetVariablesInSimpleRelation(var));
708 if (var < conditional_var_lookup_.size()) {
709 process(var_index, conditional_var_lookup_[var]);
717 const absl::Span<const IntegerVariable> relevant_variables =
718 absl::MakeSpan(var_with_positive_degree, num_relevants);
719 for (
const IntegerVariable var : relevant_variables) {
720 const int degree = var_to_degree[var.value()];
722 var_to_degree[var.value()] = start;
726 var_to_degree[var.value()] = -1;
730 output->resize(start);
731 for (
const auto& precedence : tmp_precedences_) {
733 const int pos = var_to_degree[precedence.var.value()]++;
734 if (pos < 0)
continue;
735 (*output)[pos] = precedence;
740 for (
const IntegerVariable var : relevant_variables) {
741 var_to_degree[var.value()] = 0;
746 if (!VLOG_IS_ON(1))
return;
747 std::vector<std::pair<std::string, int64_t>> stats;
748 stats.push_back({
"ConditionalLinear2Bounds/num_enforced_relations",
749 num_enforced_relations_});
750 stats.push_back({
"ConditionalLinear2Bounds/num_encoded_equivalences",
751 num_encoded_equivalences_});
752 shared_stats_->AddStats(stats);
756 IntegerValue lhs, IntegerValue rhs) {
762 num_enforced_relations_++;
764 relations_.push_back(
765 {.enforcement = lit, .expr = expr, .lhs = lhs, .rhs = rhs});
780 std::vector<std::pair<LiteralIndex, int>> literal_key_values;
781 const int num_relations = relations_.size();
782 literal_key_values.reserve(num_enforced_relations_);
783 for (
int i = 0;
i < num_relations; ++
i) {
787 lit_to_relations_.ResetFromPairs(literal_key_values);
788 lit_to_relations_.Add({});
809 absl::flat_hash_map<LinearExpression2, IntegerValue> lin2_to_upper_bound;
810 for (LiteralIndex lit_index{0}; lit_index < lit_to_relations_.size() - 1;
813 lin2_to_upper_bound.clear();
814 const absl::Span<const int> relations = lit_to_relations_[lit_index];
815 const absl::Span<const int> relations_negation =
817 if (relations.empty() || relations_negation.empty())
continue;
818 for (
const int relation_index : relations) {
819 const Relation& r = relations_[relation_index];
823 const auto [it, inserted] = lin2_to_upper_bound.insert({expr, r.
rhs});
825 it->second = std::min(it->second, r.
rhs);
830 const auto [it, inserted] = lin2_to_upper_bound.insert({expr, -r.
lhs});
832 it->second = std::min(it->second, -r.
lhs);
836 for (
const int relation_index : relations_negation) {
837 const Relation& r = relations_[relation_index];
841 const IntegerValue lower_bounds[2] = {r.
lhs, -r.
rhs};
844 for (
int i = 0;
i < 2; ++
i) {
846 const auto it = lin2_to_upper_bound.find(exprs[
i]);
847 if (it != lin2_to_upper_bound.end()) {
848 const IntegerValue ub = it->second;
850 if (ub >= lower_bounds[
i]) {
854 num_encoded_equivalences_++;
857 reified_linear2_bounds_->AddBoundEncodingIfNonTrivial(lit, exprs[
i],
861 reified_linear2_bounds_->AddBoundEncodingIfNonTrivial(
862 lit.
Negated(), expr, -lower_bounds[
i]);
874 const absl::flat_hash_map<IntegerVariable, IntegerValue>&
input,
875 absl::flat_hash_map<IntegerVariable, IntegerValue>* output) {
878 auto get_lower_bound = [&](IntegerVariable var) {
879 const auto it =
input.find(var);
880 if (it !=
input.end())
return it->second;
883 auto get_upper_bound = [&](IntegerVariable var) {
886 auto update_lower_bound_by_var = [&](IntegerVariable var, IntegerValue lb) {
888 const auto [it, inserted] = output->insert({var, lb});
890 it->second = std::max(it->second, lb);
893 auto update_upper_bound_by_var = [&](IntegerVariable var, IntegerValue ub) {
894 update_lower_bound_by_var(
NegationOf(var), -ub);
898 if (expr.
coeffs[0] == 0)
return;
902 if (expr.
coeffs[1] != 0) {
903 lhs = lhs - expr.
coeffs[1] * get_upper_bound(expr.
vars[1]);
904 rhs = rhs - expr.
coeffs[1] * get_lower_bound(expr.
vars[1]);
906 update_lower_bound_by_var(expr.
vars[0],
908 update_upper_bound_by_var(expr.
vars[0],
911 auto update_var_bounds_from_relation = [&](
Relation r) {
912 DCHECK(r.expr.IsCanonicalized());
913 update_var_bounds(r.expr, r.lhs, r.rhs);
914 std::swap(r.expr.vars[0], r.expr.vars[1]);
915 std::swap(r.expr.coeffs[0], r.expr.coeffs[1]);
916 update_var_bounds(r.expr, r.lhs, r.rhs);
918 for (
const int relation_index :
922 update_var_bounds_from_relation(r);
924 for (
const auto& [var, unused] :
input) {
925 for (
const auto& [expr, lb, ub] :
931 update_var_bounds_from_relation(r);
934 update_var_bounds_from_relation(
Relation{
940 for (
const auto [var, lb] : *output) {
946void GreaterThanAtLeastOneOfDetector::AddRelationIfNonTrivial(
948 std::vector<VariableConditionalAffineBound>* clause_bounds)
const {
949 const Literal l = r.enforcement;
951 auto add_if_non_trivial = [&](Literal cond, IntegerVariable var,
952 AffineExpression expr) {
955 clause_bounds->push_back({cond, var, expr});
962 for (
int i = 0;
i < 2; ++
i) {
963 const AffineExpression affine_lb = r.expr.GetAffineLowerBound(
964 i, r.lhs, integer_trail_.LevelZeroLowerBound(r.expr.vars[1 -
i]));
965 add_if_non_trivial(l, r.expr.vars[
i], affine_lb);
969 LinearExpression2 negated_expr(r.expr);
970 negated_expr.Negate();
971 for (
int i = 0;
i < 2; ++
i) {
972 const AffineExpression affine_lb = negated_expr.GetAffineLowerBound(
974 integer_trail_.LevelZeroLowerBound(negated_expr.vars[1 -
i]));
975 add_if_non_trivial(l, negated_expr.vars[
i], affine_lb);
980bool GreaterThanAtLeastOneOfDetector::AddRelationFromBounds(
981 IntegerVariable var, absl::Span<const Literal> clause,
982 absl::Span<const VariableConditionalAffineBound> bounds,
Model* model) {
983 std::vector<AffineExpression> exprs;
984 std::vector<Literal> selectors;
985 absl::flat_hash_set<LiteralIndex> used;
987 for (
const VariableConditionalAffineBound& bound : bounds) {
988 DCHECK_EQ(
bound.var, var);
990 selectors.push_back(
bound.enforcement_literal);
991 used.insert(
bound.enforcement_literal);
992 exprs.push_back(
bound.bound);
997 std::vector<Literal> enforcements;
998 for (
const Literal l : clause) {
999 if (!used.contains(l.Index())) {
1000 enforcements.push_back(l.Negated());
1006 if (used.size() <= 1)
return false;
1008 total_num_expressions_ += exprs.size();
1009 ++num_detected_constraints_;
1010 maximum_num_expressions_ =
1011 std::max(maximum_num_expressions_,
static_cast<int64_t
>(exprs.size()));
1014 GreaterThanAtLeastOneOfPropagator* constraint =
1015 new GreaterThanAtLeastOneOfPropagator(var, exprs, selectors, enforcements,
1017 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
1018 model->TakeOwnership(constraint);
1022int GreaterThanAtLeastOneOfDetector::
1023 AddGreaterThanAtLeastOneOfConstraintsFromClause(
1024 const absl::Span<const Literal> clause,
Model* model,
1026 implied_bounds_by_literal) {
1027 CHECK_EQ(model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
1028 if (clause.size() < 2)
return 0;
1030 std::vector<VariableConditionalAffineBound> clause_bounds;
1033 for (
const Literal l : clause) {
1034 for (
const int index :
1035 repository_.IndicesOfRelationsEnforcedBy(l.Index())) {
1036 const Relation& r = repository_.relation(index);
1037 AddRelationIfNonTrivial(r, &clause_bounds);
1039 for (
const IntegerLiteral& i_lit : implied_bounds_by_literal[l.Index()]) {
1040 if (integer_trail_.LevelZeroLowerBound(i_lit.var) < i_lit.bound) {
1041 clause_bounds.push_back({l, i_lit.var, AffineExpression(i_lit.bound)});
1045 if (clause_bounds.size() <= 1)
return 0;
1048 absl::c_stable_sort(
1050 [](
const VariableConditionalAffineBound& a,
1051 const VariableConditionalAffineBound&
b) {
return a.var <
b.var; });
1054 int num_added_constraints = 0;
1055 for (
int i = 0;
i < clause_bounds.size();) {
1056 const int start =
i;
1057 const IntegerVariable var = clause_bounds[start].var;
1059 for (;
i < clause_bounds.size() && clause_bounds[
i].var == var; ++
i) {
1063 absl::Span<const VariableConditionalAffineBound> bounds =
1064 absl::MakeSpan(clause_bounds).subspan(start,
end - start);
1067 if (bounds.size() < 2)
continue;
1073 if (bounds.size() + 1 < clause.size())
continue;
1075 if (AddRelationFromBounds(var, clause, bounds, model)) {
1076 ++num_added_constraints;
1079 return num_added_constraints;
1082int GreaterThanAtLeastOneOfDetector::
1083 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(
Model* model) {
1084 auto* time_limit = model->GetOrCreate<TimeLimit>();
1085 auto* solver = model->GetOrCreate<SatSolver>();
1088 std::vector<VariableConditionalAffineBound> clause_bounds;
1089 for (
int index = 0; index < repository_.size(); ++index) {
1090 const Relation& r = repository_.relation(index);
1092 AddRelationIfNonTrivial(r, &clause_bounds);
1094 for (
const auto& [literal_var_pair, bound] :
1095 implied_bounds_.GetModelImpliedBounds()) {
1096 if (integer_trail_.LevelZeroLowerBound(literal_var_pair.second) < bound) {
1097 clause_bounds.push_back(VariableConditionalAffineBound{
1098 .enforcement_literal = Literal(literal_var_pair.first),
1099 .var = literal_var_pair.second,
1100 .bound = AffineExpression(bound)});
1108 absl::c_stable_sort(
1110 [](
const VariableConditionalAffineBound& a,
1111 const VariableConditionalAffineBound&
b) {
return a.var <
b.var; });
1113 int num_added_constraints = 0;
1114 for (
int i = 0;
i < clause_bounds.size();) {
1115 const int start =
i;
1116 const IntegerVariable var = clause_bounds[start].var;
1118 for (;
i < clause_bounds.size() && clause_bounds[
i].var == var; ++
i) {
1122 absl::Span<const VariableConditionalAffineBound> bounds =
1123 absl::MakeSpan(clause_bounds).subspan(start,
end - start);
1125 if (bounds.size() <= 1)
continue;
1126 if (time_limit->LimitReached())
return num_added_constraints;
1131 solver->Backtrack(0);
1132 if (solver->ModelIsUnsat())
return num_added_constraints;
1133 std::vector<Literal> clause;
1134 for (
const VariableConditionalAffineBound& bound : bounds) {
1135 const Literal literal =
bound.enforcement_literal;
1136 if (solver->Assignment().LiteralIsFalse(literal))
continue;
1138 solver->EnqueueDecisionAndBacktrackOnConflict(literal.Negated());
1142 clause = solver->GetLastIncompatibleDecisions();
1143 for (Literal& ref : clause) ref = ref.Negated();
1147 solver->Backtrack(0);
1148 if (clause.size() <= 1)
continue;
1151 const absl::btree_set<Literal> clause_set(clause.begin(), clause.end());
1153 std::vector<VariableConditionalAffineBound> for_cur_clause;
1154 for (
const VariableConditionalAffineBound& bound : bounds) {
1155 const Literal literal =
bound.enforcement_literal;
1156 if (clause_set.contains(literal)) {
1157 for_cur_clause.push_back(bound);
1161 if (AddRelationFromBounds(bounds[0].var, clause, for_cur_clause, model)) {
1162 ++num_added_constraints;
1166 solver->Backtrack(0);
1167 return num_added_constraints;
1171 Model* model,
bool auto_detect_clauses) {
1177 int num_added_constraints = 0;
1178 SOLVER_LOG(logger,
"[Precedences] num_relations=", repository_.size(),
1179 " num_clauses=", clauses->AllClausesInCreationOrder().size());
1183 const auto& all_implied_bounds = implied_bounds_.GetModelImpliedBounds();
1184 std::vector<LiteralIndex> implied_bounds_conditions;
1185 std::vector<IntegerLiteral> implied_bounds_integer_lit;
1186 implied_bounds_conditions.reserve(all_implied_bounds.size());
1187 implied_bounds_integer_lit.reserve(all_implied_bounds.size());
1188 for (
const auto& [literal_var_pair, bound] : all_implied_bounds) {
1189 implied_bounds_conditions.push_back(literal_var_pair.first);
1190 implied_bounds_integer_lit.push_back(
1194 std::move(implied_bounds_conditions),
1195 std::move(implied_bounds_integer_lit), 2 * solver->NumVariables());
1204 if (!auto_detect_clauses &&
1205 clauses->AllClausesInCreationOrder().size() < 1e6) {
1213 for (
const SatClause* clause : clauses->AllClausesInCreationOrder()) {
1214 if (time_limit->LimitReached())
return num_added_constraints;
1215 if (solver->ModelIsUnsat())
return num_added_constraints;
1216 num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
1217 clause->AsSpan(), model, implied_bounds_by_literal);
1224 const int num_booleans = solver->NumVariables();
1225 if (num_booleans < 1e6) {
1226 for (
int i = 0;
i < num_booleans; ++
i) {
1227 if (time_limit->LimitReached())
return num_added_constraints;
1228 if (solver->ModelIsUnsat())
return num_added_constraints;
1229 num_added_constraints +=
1230 AddGreaterThanAtLeastOneOfConstraintsFromClause(
1231 {
Literal(BooleanVariable(
i),
true),
1232 Literal(BooleanVariable(
i),
false)},
1233 model, implied_bounds_by_literal);
1238 num_added_constraints +=
1239 AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(model);
1242 if (num_added_constraints > 0) {
1243 SOLVER_LOG(logger,
"[Precedences] Added ", num_added_constraints,
1244 " GreaterThanAtLeastOneOf() constraints.");
1247 return num_added_constraints;
1251 if (!VLOG_IS_ON(1))
return;
1252 std::vector<std::pair<std::string, int64_t>> stats;
1253 stats.push_back({
"GreaterThanAtLeastOneOfDetector/total_num_expressions",
1254 total_num_expressions_});
1255 stats.push_back({
"GreaterThanAtLeastOneOfDetector/maximum_num_expressions",
1256 maximum_num_expressions_});
1257 stats.push_back({
"GreaterThanAtLeastOneOfDetector/num_detected_constraints",
1258 num_detected_constraints_});
1259 shared_stats_->AddStats(stats);
1269 DCHECK_EQ(trail->CurrentDecisionLevel(), 0);
1270 absl::flat_hash_set<Literal> relevant_true_literals;
1271 for (; index < trail->Index(); ++index) {
1272 const Literal l = (*trail)[index];
1273 if (variable_appearing_in_reified_relations_.contains(l.Variable())) {
1274 relevant_true_literals.insert(l);
1277 if (relevant_true_literals.empty())
return true;
1280 for (
const auto [l, expr_index, ub] : all_reified_relations_) {
1281 if (relevant_true_literals.contains(l)) {
1282 ++num_relations_fixed_at_root_level_;
1283 best_root_level_bounds_->AddUpperBound(expr_index, ub);
1284 VLOG(2) <<
"New fixed precedence: "
1285 << lin2_indices_->GetExpression(expr_index) <<
" <= " << ub
1286 <<
" (was reified by " << l <<
")";
1287 }
else if (relevant_true_literals.contains(l.Negated())) {
1288 ++num_relations_fixed_at_root_level_;
1289 best_root_level_bounds_->AddLowerBound(expr_index, ub + 1);
1290 VLOG(2) <<
"New fixed precedence: "
1291 << lin2_indices_->GetExpression(expr_index) <<
" > " << ub
1292 <<
" (was reified by not(" << l <<
"))";
1300 if (!VLOG_IS_ON(1))
return;
1301 std::vector<std::pair<std::string, int64_t>> stats;
1303 {
"ReifiedLinear2Bounds/num_linear3_relations", num_linear3_relations_});
1305 {
"ReifiedLinear2Bounds/num_literal_relations", relation_to_lit_.size()});
1306 stats.push_back({
"ReifiedLinear2Bounds/num_relations_fixed_at_root_level",
1307 num_relations_fixed_at_root_level_});
1308 shared_stats_->AddStats(stats);
1325 const LinearExpression2Index expr_index = lin2_indices_->AddOrGet(expr);
1326 relation_to_lit_.insert({{expr_index, ub}, l});
1327 variable_appearing_in_reified_relations_.insert(l.
Variable());
1328 all_reified_relations_.push_back({l, expr_index, ub});
1331std::variant<ReifiedLinear2Bounds::ReifiedBoundType, Literal, IntegerLiteral>
1345 DCHECK_EQ(expr.
coeffs[1], 1);
1349 const LinearExpression2Index expr_index = lin2_indices_->GetIndex(expr);
1353 const auto it = relation_to_lit_.find({expr_index, ub});
1354 if (it != relation_to_lit_.end())
return it->second;
1355 if (linear3_bounds_.size() <= expr_index) {
1358 const auto [affine_expr, divisor] = linear3_bounds_[expr_index];
1362 const IntegerValue affine_bound =
CapProdI(ub, divisor);
1366 return affine_expr.LowerOrEqual(affine_bound);
1370 absl::Span<const int64_t> coeffs,
1372 DCHECK_EQ(vars.size(), 3);
1373 DCHECK_EQ(coeffs.size(), 3);
1374 for (
int i = 0;
i < vars.size(); ++
i) {
1376 coeffs[(
i + 1) % 3]);
1381 const LinearExpression2Index expr_index = lin2_indices_->AddOrGet(expr);
1382 if (linear3_bounds_.size() <= expr_index) {
1385 auto& [old_affine_expr, old_divisor] = linear3_bounds_[expr_index];
1386 if (old_divisor == 0 || old_divisor > gcd) {
1387 linear3_bounds_[expr_index] = {affine_expr, gcd};
1388 if (old_divisor == 0) ++num_linear3_relations_;
1394 LinearExpression2Index lin2_index)
const {
1395 DCHECK_LT(lin2_index, linear3_bounds_.size());
1396 DCHECK_GT(linear3_bounds_[lin2_index].second, 0);
1397 return linear3_bounds_[lin2_index];
1402 trail_(model->GetOrCreate<
Trail>()),
1410 if (!VLOG_IS_ON(1))
return;
1411 std::vector<std::pair<std::string, int64_t>> stats;
1413 {
"Linear2BoundsFromLinear3/num_affine_updates", num_affine_updates_});
1414 shared_stats_->AddStats(stats);
1422 LinearExpression2Index lin2_index, IntegerValue lin_expr_gcd,
1425 if (trail_->CurrentDecisionLevel() == 0 || affine_ub.
IsConstant()) {
1426 root_level_bounds_->AddUpperBound(
1427 lin2_index,
FloorRatio(integer_trail_->LevelZeroUpperBound(affine_ub),
1434 if (lin2_index >= best_affine_ub_.size()) {
1435 best_affine_ub_.resize(lin2_index.value() + 1, {AffineExpression(), 0});
1437 auto& [old_affine_ub, old_divisor] = best_affine_ub_[lin2_index];
1438 if (old_divisor != 0) {
1444 if (old_affine_ub == affine_ub && old_divisor == lin_expr_gcd) {
1445 linear2_watcher_->NotifyBoundChanged(
1446 lin2_indices_->GetExpression(lin2_index));
1450 const IntegerValue new_ub =
1453 integer_trail_->UpperBound(old_affine_ub), old_divisor);
1454 if (old_ub <= new_ub)
return false;
1456 best_affine_ub_[lin2_index] = {affine_ub, lin_expr_gcd};
1459 best_affine_ub_[lin2_index] = {affine_ub, lin_expr_gcd};
1462 ++num_affine_updates_;
1463 linear2_watcher_->NotifyBoundChanged(
1464 lin2_indices_->GetExpression(lin2_index));
1469 LinearExpression2Index lin2_index)
const {
1471 auto [affine, divisor] = best_affine_ub_[lin2_index];
1473 return FloorRatio(integer_trail_->UpperBound(affine), divisor);
1477 LinearExpression2Index lin2_index, IntegerValue ub,
1478 std::vector<Literal>* ,
1479 std::vector<IntegerLiteral>* integer_reason)
const {
1481 DCHECK_LT(lin2_index, best_affine_ub_.size());
1485 const auto [affine, divisor] = best_affine_ub_[lin2_index];
1486 DCHECK_NE(divisor, 0);
1487 integer_reason->push_back(affine.LowerOrEqual(
CapProdI(ub + 1, divisor) - 1));
1491 if (!VLOG_IS_ON(1))
return;
1492 std::vector<std::pair<std::string, int64_t>> stats;
1493 stats.push_back({
"Linear2Bounds/enqueue_trivial", enqueue_trivial_});
1494 stats.push_back({
"Linear2Bounds/enqueue_degenerate", enqueue_degenerate_});
1495 stats.push_back({
"Linear2Bounds/enqueue_true_at_root_level",
1496 enqueue_true_at_root_level_});
1497 stats.push_back({
"Linear2Bounds/enqueue_conflict_false_at_root_level",
1498 enqueue_conflict_false_at_root_level_});
1499 stats.push_back({
"Linear2Bounds/enqueue_individual_var_bounds",
1500 enqueue_individual_var_bounds_});
1502 {
"Linear2Bounds/enqueue_literal_encoding", enqueue_literal_encoding_});
1503 stats.push_back({
"Linear2Bounds/enqueue_integer_linear3_encoding",
1504 enqueue_integer_linear3_encoding_});
1505 shared_stats_->AddStats(stats);
1509 LinearExpression2Index lin2_index)
const {
1512 integer_trail_->UpperBound(lin2_indices_->GetExpression(lin2_index)));
1517 if (expr.
coeffs[0] == 0) {
1518 return integer_trail_->UpperBound(expr);
1520 DCHECK_NE(expr.
coeffs[1], 0);
1522 IntegerValue ub = integer_trail_->UpperBound(expr);
1525 ub = std::min(ub, integer_trail_->LevelZeroUpperBound(expr));
1526 const LinearExpression2Index index = lin2_indices_->GetIndex(expr);
1528 ub = std::min(ub, root_level_bounds_->GetUpperBoundNoTrail(index));
1529 ub = std::min(ub, enforced_bounds_->GetUpperBoundFromEnforced(index));
1530 ub = std::min(ub, linear3_bounds_->GetUpperBoundFromLinear3(index));
1537 std::vector<Literal>* literal_reason,
1538 std::vector<IntegerLiteral>* integer_reason)
const {
1542 if (integer_trail_->LevelZeroUpperBound(expr) <= ub) {
1548 const LinearExpression2Index index = lin2_indices_->GetIndex(expr);
1551 if (root_level_bounds_->GetUpperBoundNoTrail(index) <= ub) {
1555 if (enforced_bounds_->GetUpperBoundFromEnforced(index) <= ub) {
1556 return enforced_bounds_->AddReasonForUpperBoundLowerThan(
1557 index, ub, literal_reason, integer_reason);
1560 if (linear3_bounds_->GetUpperBoundFromLinear3(index) <= ub) {
1561 return linear3_bounds_->AddReasonForUpperBoundLowerThan(
1562 index, ub, literal_reason, integer_reason);
1567 const IntegerValue implied_ub = integer_trail_->UpperBound(expr);
1568 const IntegerValue slack = ub - implied_ub;
1569 DCHECK_GE(slack, 0);
1571 absl::Span<const IntegerVariable> vars = expr.
non_zero_vars();
1573 integer_trail_->AppendRelaxedLinearReason(slack, coeffs, vars,
1578 IntegerValue ub)
const {
1581 const LinearExpression2Index index = lin2_indices_->GetIndex(expr);
1582 IntegerValue known_ub;
1583 IntegerValue known_lb;
1585 known_ub =
CapProdI(gcd, integer_trail_->UpperBound(expr));
1587 known_lb = -
CapProdI(gcd, integer_trail_->UpperBound(expr));
1600 absl::Span<const Literal> literal_reason,
1601 absl::Span<const IntegerLiteral> integer_reason) {
1615 return integer_trail_->ReportConflict(literal_reason, integer_reason);
1620 if (expr.
coeffs[0] == 0) {
1621 ++enqueue_degenerate_;
1622 return integer_trail_->Enqueue(
1629 const auto reified_bound = reified_lin2_bounds_->GetEncodedBound(expr, ub);
1632 if (std::holds_alternative<ReifiedBoundType>(reified_bound) &&
1633 std::get<ReifiedBoundType>(reified_bound) ==
1634 ReifiedBoundType::kAlwaysTrue) {
1635 ++enqueue_true_at_root_level_;
1640 if (std::holds_alternative<ReifiedBoundType>(reified_bound) &&
1641 std::get<ReifiedBoundType>(reified_bound) ==
1642 ReifiedBoundType::kAlwaysFalse) {
1643 ++enqueue_conflict_false_at_root_level_;
1645 return integer_trail_->ReportConflict(literal_reason, integer_reason);
1650 if (trail_->CurrentDecisionLevel() == 0) {
1651 root_level_bounds_->AddUpperBound(expr, ub);
1656 if (std::holds_alternative<ReifiedBoundType>(reified_bound) &&
1657 std::get<ReifiedBoundType>(reified_bound) ==
1658 ReifiedBoundType::kNoLiteralStored) {
1660 ++enqueue_individual_var_bounds_;
1662 const ReasonIndex reason_index =
1663 integer_trail_->AppendReasonToInternalBuffers(literal_reason,
1665 if (reason_index >= saved_reasons_.size()) {
1666 saved_reasons_.resize(reason_index + 1);
1670 CHECK_GT(expr.
coeffs[0], 0);
1671 CHECK_GT(expr.
coeffs[1], 0);
1672 saved_reasons_[reason_index] = expr;
1676 const IntegerValue min_activity =
1677 integer_trail_->LowerBound(expr.
vars[0]) * expr.
coeffs[0] +
1678 integer_trail_->LowerBound(expr.
vars[1]) * expr.
coeffs[1];
1679 const IntegerValue linear_slack = ub - min_activity;
1680 CHECK_GE(linear_slack, 0);
1681 for (
int i = 0;
i < 2; ++
i) {
1682 const IntegerValue div = linear_slack / expr.
coeffs[
i];
1683 const IntegerValue new_ub =
1684 integer_trail_->LowerBound(expr.
vars[
i]) + div;
1685 const IntegerValue propagation_slack =
1686 (div + 1) * expr.
coeffs[
i] - linear_slack - 1;
1687 if (!integer_trail_->EnqueueWithLazyReason(
1689 reason_index.value(), propagation_slack,
this)) {
1697 if (std::holds_alternative<Literal>(reified_bound)) {
1698 ++enqueue_literal_encoding_;
1699 const Literal literal = std::get<Literal>(reified_bound);
1700 if (!integer_trail_->SafeEnqueueLiteral(literal, literal_reason,
1704 enforced_bounds_->PushConditionalRelation({literal}, expr, ub);
1710 if (std::holds_alternative<IntegerLiteral>(reified_bound)) {
1711 ++enqueue_integer_linear3_encoding_;
1715 const auto [affine, divisor] =
1716 reified_lin2_bounds_->GetLinear3Bound(lin2_indices_->GetIndex(expr));
1717 linear3_bounds_->AddAffineUpperBound(lin2_indices_->GetIndex(expr), divisor,
1720 const IntegerLiteral literal = std::get<IntegerLiteral>(reified_bound);
1721 return integer_trail_->Enqueue(literal, literal_reason, integer_reason);
1723 LOG(FATAL) <<
"Unknown reified bound type";
1729 const ReasonIndex reason_index(
id);
1731 integer_trail_->LiteralReasonAsSpan(reason_index);
1732 reason->
integer_literals = integer_trail_->IntegerReasonAsSpan(reason_index);
1736 reason->
vars = saved_reasons_[reason_index].non_zero_vars();
1737 reason->
coeffs = saved_reasons_[reason_index].non_zero_coeffs();
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
void ResetFromFlatMapping(Keys keys, Values values, int minimum_num_nodes=0)
void AddPartialRelation(Literal lit, IntegerVariable a, IntegerVariable b)
absl::Span< const int > IndicesOfRelationsEnforcedBy(LiteralIndex lit) const
void Add(Literal lit, LinearExpression2 expr, IntegerValue lhs, IntegerValue rhs)
~ConditionalLinear2Bounds()
const Relation & relation(int index) const
~EnforcedLinear2Bounds() override
void SetLevel(int level) final
void PushConditionalRelation(absl::Span< const Literal > enforcements, LinearExpression2Index index, IntegerValue rhs)
IntegerValue GetUpperBoundFromEnforced(LinearExpression2Index index) const
void AddReasonForUpperBoundLowerThan(LinearExpression2Index index, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
void CollectPrecedences(absl::Span< const IntegerVariable > vars, std::vector< PrecedenceData > *output)
~GreaterThanAtLeastOneOfDetector()
int AddGreaterThanAtLeastOneOfConstraints(Model *model, bool auto_detect_clauses=false)
const std::vector< ImpliedBoundEntry > & GetImpliedBounds(IntegerVariable var)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
IntegerValue GetUpperBoundFromLinear3(LinearExpression2Index lin2_index) const
Linear2BoundsFromLinear3(Model *model)
~Linear2BoundsFromLinear3()
bool AddAffineUpperBound(LinearExpression2Index lin2_index, IntegerValue lin_expr_gcd, AffineExpression affine_ub)
void AddReasonForUpperBoundLowerThan(LinearExpression2Index lin2_index, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
RelationStatus GetStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
bool EnqueueLowerOrEqual(LinearExpression2 expr, IntegerValue ub, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void AddReasonForUpperBoundLowerThan(LinearExpression2 expr, IntegerValue ub, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
IntegerValue NonTrivialUpperBound(LinearExpression2Index lin2_index) const
IntegerValue UpperBound(LinearExpression2 expr) const
LinearExpression2Index AddOrGet(LinearExpression2 expr)
int64_t VarTimestamp(IntegerVariable var)
void NotifyBoundChanged(LinearExpression2 expr)
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
std::pair< AffineExpression, IntegerValue > GetLinear3Bound(LinearExpression2Index lin2_index) const
void AddBoundEncodingIfNonTrivial(Literal l, LinearExpression2 expr, IntegerValue ub)
std::variant< ReifiedBoundType, Literal, IntegerLiteral > GetEncodedBound(LinearExpression2 expr, IntegerValue ub)
void AddLinear3(absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coeffs, int64_t activity)
ReifiedLinear2Bounds(Model *model)
RelationStatus GetLevelZeroStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
int AugmentSimpleRelations(IntegerVariable var, int work_limit)
absl::Span< const std::pair< IntegerVariable, LinearExpression2Index > > GetVariablesInSimpleRelation(IntegerVariable var) const
std::vector< std::tuple< LinearExpression2, IntegerValue, IntegerValue > > GetAllBoundsContainingVariable(IntegerVariable var) const
~RootLevelLinear2Bounds()
bool AddUpperBound(LinearExpression2 expr, IntegerValue ub)
IntegerValue GetUpperBoundNoTrail(LinearExpression2Index index) const
std::vector< std::pair< LinearExpression2, IntegerValue > > GetSortedNonTrivialUpperBounds() const
std::vector< std::tuple< LinearExpression2, IntegerValue, IntegerValue > > GetAllBoundsContainingVariables(IntegerVariable var1, IntegerVariable var2) const
IntegerValue LevelZeroUpperBound(LinearExpression2 expr) const
int CurrentDecisionLevel() const
void ComputeFullPrecedences(absl::Span< const IntegerVariable > vars, std::vector< FullIntegerPrecedence > *output)
void AddEdge(int from, int to)
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
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)
const LiteralIndex kNoLiteralIndex(-1)
const LinearExpression2Index kNoLinearExpression2Index(-1)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
IntegerValue FloorRatioWithTest(IntegerValue dividend, IntegerValue positive_divisor)
bool VariableIsPositive(IntegerVariable i)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
ClosedInterval::Iterator end(ClosedInterval interval)
static int input(yyscan_t yyscanner)
std::vector< IntegerValue > offsets
std::vector< int > indices
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const Literal > boolean_literals_at_false
absl::Span< const IntegerValue > coeffs
absl::Span< const IntegerVariable > vars
absl::Span< const IntegerLiteral > integer_literals
std::vector< std::function< bool()> > callbacks
absl::Span< const IntegerValue > non_zero_coeffs() const
bool NegateForCanonicalization()
absl::Span< const IntegerVariable > non_zero_vars() const
IntegerValue DivideByGcd()
bool IsCanonicalized() const
bool CanonicalizeAndUpdateBounds(IntegerValue &lb, IntegerValue &ub)
void SimpleCanonicalization()
::util::DenseIntStableTopologicalSorter DenseIntStableTopologicalSorter
#define SOLVER_LOG(logger,...)