24#include "absl/log/check.h"
25#include "absl/numeric/int128.h"
26#include "absl/types/span.h"
42template <
bool use_
int128>
44 absl::Span<const Literal> enforcement_literals,
45 absl::Span<const IntegerVariable> vars,
46 absl::Span<const IntegerValue> coeffs, IntegerValue upper,
Model* model)
47 : upper_bound_(upper),
51 vars_(new IntegerVariable[size_]),
52 coeffs_(new IntegerValue[size_]),
53 max_variations_(new IntegerValue[size_]) {
58 memcpy(vars_.get(), vars.data(), size_ *
sizeof(IntegerVariable));
59 memcpy(coeffs_.get(), coeffs.data(), size_ *
sizeof(IntegerValue));
62 for (
int i = 0;
i < size_; ++
i) {
65 coeffs_[
i] = -coeffs_[
i];
72 literal_reason_.reserve(enforcement_literals.size());
73 for (
const Literal literal : enforcement_literals) {
74 literal_reason_.push_back(literal.Negated());
78 rev_num_fixed_vars_ = 0;
79 rev_lb_fixed_vars_ = IntegerValue(0);
83template <
bool use_
int128>
86 : upper_bound_(ct.ub),
90 vars_(
std::move(ct.vars)),
91 coeffs_(
std::move(ct.coeffs)),
92 max_variations_(new IntegerValue[size_]) {
97 for (
int i = 0;
i < size_; ++
i) {
100 coeffs_[
i] = -coeffs_[
i];
105 rev_num_fixed_vars_ = 0;
106 rev_lb_fixed_vars_ = IntegerValue(0);
109template <
bool use_
int128>
110void LinearConstraintPropagator<use_int128>::FillIntegerReason() {
111 shared_->integer_reason.clear();
112 shared_->reason_coeffs.clear();
113 for (
int i = 0;
i < size_; ++
i) {
114 const IntegerVariable var = vars_[
i];
115 if (!shared_->integer_trail->VariableLowerBoundIsFromLevelZero(var)) {
116 shared_->integer_reason.push_back(
117 shared_->integer_trail->LowerBoundAsLiteral(var));
118 shared_->reason_coeffs.push_back(coeffs_[
i]);
124IntegerValue CappedCast(absl::int128
input, IntegerValue cap) {
125 if (
input >= absl::int128(cap.value())) {
128 return IntegerValue(
static_cast<int64_t
>(
input));
134template <
bool use_
int128>
135std::pair<IntegerValue, IntegerValue>
137 IntegerLiteral integer_literal, IntegerVariable target_var)
const {
141 if (integer_literal.
var == target_var) {
149 bool literal_var_present =
false;
150 bool literal_var_present_positively =
false;
151 IntegerValue var_coeff;
153 bool target_var_present_negatively =
false;
154 absl::int128 target_coeff;
159 absl::int128 lb_128 = 0;
160 for (
int i = 0;
i < size_; ++
i) {
161 const IntegerVariable var = vars_[
i];
162 const IntegerValue coeff = coeffs_[
i];
164 target_coeff = absl::int128(coeff.value());
165 target_var_present_negatively =
true;
168 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
169 lb_128 += absl::int128(coeff.value()) * absl::int128(lb.value());
172 literal_var_present =
true;
173 literal_var_present_positively = (var == integer_literal.
var);
177 if (!literal_var_present || !target_var_present_negatively) {
183 const absl::int128 slack128 = absl::int128(upper_bound_.value()) - lb_128;
184 const IntegerValue target_lb = shared_->integer_trail->LowerBound(target_var);
185 const IntegerValue target_ub = shared_->integer_trail->UpperBound(target_var);
189 return {target_ub, target_ub};
192 const IntegerValue target_diff = target_ub - target_lb;
193 const IntegerValue delta = CappedCast(slack128 / target_coeff, target_diff);
196 if (literal_var_present_positively) {
200 const IntegerValue diff =
201 std::max(IntegerValue(0),
202 integer_literal.
bound -
203 shared_->integer_trail->LowerBound(integer_literal.
var));
204 const absl::int128 tighter_slack =
205 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
206 absl::int128(diff.value()));
207 const IntegerValue tighter_delta =
208 CappedCast(tighter_slack / target_coeff, target_diff);
209 return {target_ub - delta, target_ub - tighter_delta};
214 const IntegerValue diff =
215 std::max(IntegerValue(0),
216 shared_->integer_trail->UpperBound(integer_literal.
var) -
217 integer_literal.
bound + 1);
218 const absl::int128 tighter_slack =
219 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
220 absl::int128(diff.value()));
221 const IntegerValue tighter_delta =
222 CappedCast(tighter_slack / target_coeff, target_diff);
223 return {target_ub - tighter_delta, target_ub - delta};
227template <
bool use_
int128>
231 reason->
vars = absl::MakeSpan(vars_.get(), size_);
232 reason->
coeffs = absl::MakeSpan(coeffs_.get(), size_);
235template <
bool use_
int128>
239 int num_unassigned_enforcement_literal = 0;
241 for (
const Literal negated_enforcement : literal_reason_) {
243 if (shared_->assignment.LiteralIsFalse(literal))
return true;
244 if (!shared_->assignment.LiteralIsTrue(literal)) {
245 ++num_unassigned_enforcement_literal;
246 unique_unnasigned_literal = literal.
Index();
252 if (num_unassigned_enforcement_literal > 1)
return true;
254 int num_fixed_vars = rev_num_fixed_vars_;
255 IntegerValue lb_fixed_vars = rev_lb_fixed_vars_;
258 absl::int128 lb_128 = 0;
259 IntegerValue lb_unfixed_vars = IntegerValue(0);
260 for (
int i = num_fixed_vars;
i < size_; ++
i) {
261 const IntegerVariable var = vars_[
i];
262 const IntegerValue coeff = coeffs_[
i];
263 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
264 const IntegerValue ub = shared_->integer_trail->UpperBound(var);
266 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
271 max_variations_[
i] = (ub - lb) * coeff;
272 lb_unfixed_vars += lb * coeff;
275 std::swap(vars_[
i], vars_[num_fixed_vars]);
276 std::swap(coeffs_[
i], coeffs_[num_fixed_vars]);
277 std::swap(max_variations_[
i], max_variations_[num_fixed_vars]);
279 lb_fixed_vars += lb * coeff;
282 shared_->time_limit->AdvanceDeterministicTime(
283 static_cast<double>(size_ - num_fixed_vars) * 5e-9);
286 if (is_registered_ && num_fixed_vars != rev_num_fixed_vars_) {
288 shared_->rev_integer_value_repository->SaveState(&rev_lb_fixed_vars_);
289 shared_->rev_int_repository->SaveState(&rev_num_fixed_vars_);
290 rev_num_fixed_vars_ = num_fixed_vars;
291 rev_lb_fixed_vars_ = lb_fixed_vars;
297 const absl::int128 max_slack = std::numeric_limits<int64_t>::max() - 1;
301 absl::int128 slack128;
303 slack128 = absl::int128(upper_bound_.value()) - lb_128;
307 slack =
static_cast<int64_t
>(std::max(-max_slack, slack128));
310 slack = upper_bound_ - (lb_fixed_vars + lb_unfixed_vars);
314 shared_->integer_trail->RelaxLinearReason(
315 -slack - 1, shared_->reason_coeffs, &shared_->integer_reason);
317 if (num_unassigned_enforcement_literal == 1) {
320 std::vector<Literal> tmp = literal_reason_;
321 tmp.erase(std::find(tmp.begin(), tmp.end(), to_propagate));
322 return shared_->integer_trail->EnqueueLiteral(to_propagate, tmp,
323 shared_->integer_reason);
325 return shared_->integer_trail->ReportConflict(literal_reason_,
326 shared_->integer_reason);
330 if (num_unassigned_enforcement_literal > 0)
return true;
334 for (
int i = num_fixed_vars;
i < size_; ++
i) {
335 if (!use_int128 && max_variations_[
i] <= slack)
continue;
339 const IntegerVariable var = vars_[
i];
340 const IntegerValue coeff = coeffs_[
i];
341 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
344 IntegerValue propagation_slack;
346 const absl::int128 coeff128 = absl::int128(coeff.value());
347 const absl::int128 div128 = slack128 / coeff128;
348 const IntegerValue ub = shared_->integer_trail->UpperBound(var);
349 if (absl::int128(lb.value()) + div128 >= absl::int128(ub.value())) {
352 new_ub = lb + IntegerValue(
static_cast<int64_t
>(div128));
353 propagation_slack =
static_cast<int64_t
>(
354 std::min(max_slack, (div128 + 1) * coeff128 - slack128 - 1));
356 const IntegerValue div = slack / coeff;
358 propagation_slack = (div + 1) * coeff - slack - 1;
360 if (!shared_->integer_trail->EnqueueWithLazyReason(
373template <
bool use_
int128>
377 if (!literal_reason_.empty())
return true;
380 absl::int128 lb_128 = 0;
381 IntegerValue min_activity = IntegerValue(0);
382 for (
int i = 0;
i < size_; ++
i) {
383 const IntegerVariable var = vars_[
i];
384 const IntegerValue coeff = coeffs_[
i];
385 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(var);
387 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
389 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(var);
390 max_variations_[
i] = (ub - lb) * coeff;
391 min_activity += lb * coeff;
394 shared_->time_limit->AdvanceDeterministicTime(
395 static_cast<double>(size_ * 1e-9));
399 absl::int128 slack128;
401 slack128 = absl::int128(upper_bound_.value()) - lb_128;
403 return shared_->integer_trail->ReportConflict({}, {});
406 slack = upper_bound_ - min_activity;
408 return shared_->integer_trail->ReportConflict({}, {});
414 for (
int i = 0;
i < size_; ++
i) {
415 if (!use_int128 && max_variations_[
i] <= slack)
continue;
417 const IntegerVariable var = vars_[
i];
418 const IntegerValue coeff = coeffs_[
i];
419 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(var);
423 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(var);
424 if (absl::int128((ub - lb).value()) * absl::int128(coeff.value()) <=
428 const absl::int128 div128 = slack128 / absl::int128(coeff.value());
429 new_ub = lb + IntegerValue(
static_cast<int64_t
>(div128));
431 const IntegerValue div = slack / coeff;
434 if (!shared_->integer_trail->Enqueue(
443template <
bool use_
int128>
446 is_registered_ =
true;
447 const int id = watcher->
Register(
this);
448 for (
int i = 0;
i < size_; ++
i) {
451 for (
const Literal negated_enforcement : literal_reason_) {
456 watcher->
WatchLiteral(negated_enforcement.Negated(),
id);
465 const std::vector<IntegerVariable>& vars,
466 const std::vector<IntegerValue>& coeffs,
471 trail_(model->GetOrCreate<
Trail>()),
474 const int id = watcher->
Register(
this);
475 watcher->SetPropagatorPriority(
id, 2);
476 watcher->WatchIntegerVariable(target,
id);
477 for (
const IntegerVariable& var : vars_) {
478 watcher->WatchIntegerVariable(var,
id);
492 if (trail_->CurrentDecisionLevel() != 0)
return true;
496 for (
int i = 0;
i < vars_.size(); ++
i) {
497 if (integer_trail_->IsFixed(vars_[
i])) {
498 sum += coeffs_[
i] * integer_trail_->LowerBound(vars_[
i]);
504 if (gcd == 0)
return true;
507 VLOG(1) <<
"Objective gcd: " << gcd;
510 gcd_ = IntegerValue(gcd);
512 const IntegerValue lb = integer_trail_->LowerBound(target_);
514 if (lb_remainder != 0) {
515 if (!integer_trail_->Enqueue(
521 const IntegerValue ub = integer_trail_->UpperBound(target_);
522 const IntegerValue ub_remainder =
524 if (ub_remainder != 0) {
525 if (!integer_trail_->Enqueue(
536 : vars_(
std::move(vars)),
538 integer_trail_(integer_trail) {}
541 if (vars_.empty())
return true;
546 integer_trail_->UpperBoundAsLiteral(min_var_);
547 const IntegerValue current_min_ub = integer_trail_->UpperBound(min_var_);
548 int num_intervals_that_can_be_min = 0;
549 int last_possible_min_interval = 0;
552 for (
int i = 0;
i < vars_.size(); ++
i) {
553 const IntegerValue lb = integer_trail_->LowerBound(vars_[
i]);
554 min = std::min(min, lb);
555 if (lb <= current_min_ub) {
556 ++num_intervals_that_can_be_min;
557 last_possible_min_interval =
i;
562 if (min > integer_trail_->LowerBound(min_var_)) {
563 integer_reason_.clear();
565 integer_reason_.push_back(var.GreaterOrEqual(min));
567 if (!integer_trail_->SafeEnqueue(min_var_.GreaterOrEqual(min),
574 if (num_intervals_that_can_be_min == 1) {
575 const IntegerValue ub_of_only_candidate =
576 integer_trail_->UpperBound(vars_[last_possible_min_interval]);
577 if (current_min_ub < ub_of_only_candidate) {
578 integer_reason_.clear();
582 integer_reason_.push_back(min_ub_literal);
584 if (var == vars_[last_possible_min_interval])
continue;
585 integer_reason_.push_back(var.GreaterOrEqual(current_min_ub + 1));
587 if (!integer_trail_->SafeEnqueue(
588 vars_[last_possible_min_interval].LowerOrEqual(current_min_ub),
601 if (num_intervals_that_can_be_min == 0) {
602 integer_reason_.clear();
605 integer_reason_.push_back(min_ub_literal);
609 integer_reason_.push_back(lit);
612 return integer_trail_->ReportConflict(integer_reason_);
619 const int id = watcher->
Register(
this);
627 absl::Span<const Literal> enforcement_literals,
628 std::vector<LinearExpression> exprs, IntegerVariable min_var,
Model* model)
629 : exprs_(
std::move(exprs)),
635 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
636 RegisterWith(watcher));
640 for (
const IntegerValue coeff : expr.coeffs) CHECK_GT(coeff, 0);
648 enforcement_helper_.GetEnforcementLiterals(enforcement_id_);
652 tmp_vars_.assign(exprs_[
id].vars.begin(), exprs_[
id].vars.end());
653 tmp_coeffs_.assign(exprs_[
id].coeffs.begin(), exprs_[
id].coeffs.end());
660 tmp_coeffs_.push_back(1);
662 reason->
vars = tmp_vars_;
663 reason->
coeffs = tmp_coeffs_;
669bool GreaterThanMinOfExprsPropagator::PropagateLinearUpperBound(
670 int id, absl::Span<const IntegerVariable> vars,
671 absl::Span<const IntegerValue> coeffs,
const IntegerValue upper_bound) {
672 IntegerValue sum_lb = IntegerValue(0);
673 const int num_vars = vars.size();
674 max_variations_.resize(num_vars);
675 for (
int i = 0;
i < num_vars; ++
i) {
676 const IntegerVariable var = vars[
i];
677 const IntegerValue coeff = coeffs[
i];
680 const IntegerValue lb = integer_trail_.
LowerBound(var);
681 const IntegerValue ub = integer_trail_.
UpperBound(var);
682 max_variations_[
i] = (ub - lb) * coeff;
683 sum_lb += lb * coeff;
687 static_cast<double>(num_vars) * 1e-9);
690 const IntegerValue slack = upper_bound - sum_lb;
693 local_reason_.clear();
694 reason_coeffs_.clear();
695 for (
int i = 0;
i < num_vars; ++
i) {
696 const IntegerVariable var = vars[
i];
699 reason_coeffs_.push_back(coeffs[
i]);
702 integer_trail_.RelaxLinearReason(-slack - 1, reason_coeffs_,
704 local_reason_.insert(local_reason_.end(),
705 integer_reason_for_unique_candidate_.begin(),
706 integer_reason_for_unique_candidate_.end());
708 return enforcement_helper_.ReportConflict(enforcement_id_, local_reason_);
710 return enforcement_helper_.PropagateWhenFalse(
711 enforcement_id_, {}, local_reason_);
718 for (
int i = 0;
i < num_vars; ++
i) {
719 if (max_variations_[
i] <= slack)
continue;
721 const IntegerVariable var = vars[
i];
722 const IntegerValue coeff = coeffs[
i];
723 const IntegerValue div = slack / coeff;
724 const IntegerValue new_ub = integer_trail_.LowerBound(var) + div;
725 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
726 if (!integer_trail_.EnqueueWithLazyReason(
737 DCHECK(!exprs_.empty());
747 const IntegerValue current_min_ub = integer_trail_.UpperBound(min_var_);
748 int num_intervals_that_can_be_min = 0;
749 int last_possible_min_interval = 0;
753 for (
int i = 0;
i < exprs_.size(); ++
i) {
754 const IntegerValue lb = exprs_[
i].Min(integer_trail_);
755 expr_lbs_.push_back(lb);
756 min_of_linear_expression_lb = std::min(min_of_linear_expression_lb, lb);
757 if (lb <= current_min_ub) {
758 ++num_intervals_that_can_be_min;
759 last_possible_min_interval =
i;
767 if (min_of_linear_expression_lb > current_min_ub) {
768 min_of_linear_expression_lb = current_min_ub + 1;
770 if (min_of_linear_expression_lb > integer_trail_.LowerBound(min_var_)) {
771 local_reason_.clear();
772 for (
int i = 0;
i < exprs_.size(); ++
i) {
773 const IntegerValue slack = expr_lbs_[
i] - min_of_linear_expression_lb;
774 integer_trail_.AppendRelaxedLinearReason(slack, exprs_[
i].coeffs,
775 exprs_[
i].vars, &local_reason_);
778 if (!enforcement_helper_.SafeEnqueue(
781 min_of_linear_expression_lb),
785 }
else if (min_of_linear_expression_lb > current_min_ub) {
788 local_reason_.push_back(
790 return enforcement_helper_.PropagateWhenFalse(
791 enforcement_id_, {}, local_reason_);
799 if (num_intervals_that_can_be_min == 1) {
800 const IntegerValue ub_of_only_candidate =
801 exprs_[last_possible_min_interval].Max(integer_trail_);
802 if (current_min_ub < ub_of_only_candidate) {
805 if (rev_unique_candidate_ == 0) {
806 integer_reason_for_unique_candidate_.clear();
810 integer_reason_for_unique_candidate_.push_back(
811 integer_trail_.UpperBoundAsLiteral(min_var_));
812 for (
int i = 0;
i < exprs_.size(); ++
i) {
813 if (
i == last_possible_min_interval)
continue;
814 const IntegerValue slack = expr_lbs_[
i] - (current_min_ub + 1);
815 integer_trail_.AppendRelaxedLinearReason(
816 slack, exprs_[
i].coeffs, exprs_[
i].vars,
817 &integer_reason_for_unique_candidate_);
819 rev_unique_candidate_ = 1;
822 return PropagateLinearUpperBound(
823 last_possible_min_interval, exprs_[last_possible_min_interval].vars,
824 exprs_[last_possible_min_interval].coeffs,
825 current_min_ub - exprs_[last_possible_min_interval].offset);
832int GreaterThanMinOfExprsPropagator::RegisterWith(
834 const int id = watcher->
Register(
this);
835 bool has_var_also_in_exprs =
false;
837 for (
int i = 0;
i < expr.vars.size(); ++
i) {
838 const IntegerVariable& var = expr.vars[
i];
839 const IntegerValue coeff = expr.coeffs[
i];
845 has_var_also_in_exprs |= (var == min_var_);
850 if (has_var_also_in_exprs) {
865 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
866 RegisterWith(watcher));
870bool ProductPropagator::CanonicalizeCases() {
885 {a_.GreaterOrEqual(0), b_.GreaterOrEqual(0)});
889 if (integer_trail_.UpperBound(p_) <= 0) {
890 if (integer_trail_.LowerBound(a_) < 0) {
891 DCHECK_GT(integer_trail_.UpperBound(a_), 0);
895 DCHECK_LT(integer_trail_.LowerBound(b_), 0);
896 DCHECK_GT(integer_trail_.UpperBound(b_), 0);
911bool ProductPropagator::PropagateWhenAllNonNegative() {
913 const IntegerValue max_a = integer_trail_.UpperBound(a_);
914 const IntegerValue max_b = integer_trail_.UpperBound(b_);
915 const IntegerValue new_max =
CapProdI(max_a, max_b);
916 if (new_max < integer_trail_.UpperBound(p_)) {
917 if (!enforcement_helper_.SafeEnqueue(
918 enforcement_id_, p_.LowerOrEqual(new_max),
919 {integer_trail_.UpperBoundAsLiteral(a_),
920 integer_trail_.UpperBoundAsLiteral(b_), a_.GreaterOrEqual(0),
921 b_.GreaterOrEqual(0)})) {
928 const IntegerValue min_a = integer_trail_.LowerBound(a_);
929 const IntegerValue min_b = integer_trail_.LowerBound(b_);
930 const IntegerValue new_min =
CapProdI(min_a, min_b);
934 if (new_min > integer_trail_.UpperBound(p_)) {
935 return enforcement_helper_.ReportConflict(
936 enforcement_id_, {integer_trail_.UpperBoundAsLiteral(p_),
937 integer_trail_.LowerBoundAsLiteral(a_),
938 integer_trail_.LowerBoundAsLiteral(b_)});
940 if (new_min > integer_trail_.LowerBound(p_)) {
941 if (!enforcement_helper_.SafeEnqueue(
942 enforcement_id_, p_.GreaterOrEqual(new_min),
943 {integer_trail_.LowerBoundAsLiteral(a_),
944 integer_trail_.LowerBoundAsLiteral(b_)})) {
950 for (
int i = 0;
i < 2; ++
i) {
951 const AffineExpression a =
i == 0 ? a_ : b_;
952 const AffineExpression
b =
i == 0 ? b_ : a_;
953 const IntegerValue max_a = integer_trail_.UpperBound(a);
954 const IntegerValue min_b = integer_trail_.LowerBound(
b);
955 const IntegerValue min_p = integer_trail_.LowerBound(p_);
956 const IntegerValue max_p = integer_trail_.UpperBound(p_);
957 const IntegerValue prod =
CapProdI(max_a, min_b);
959 if (!enforcement_helper_.SafeEnqueue(
960 enforcement_id_, a.LowerOrEqual(
FloorRatio(max_p, min_b)),
961 {integer_trail_.LowerBoundAsLiteral(
b),
962 integer_trail_.UpperBoundAsLiteral(p_), p_.GreaterOrEqual(0)})) {
965 }
else if (prod < min_p && max_a != 0) {
966 if (!enforcement_helper_.SafeEnqueue(
967 enforcement_id_,
b.GreaterOrEqual(
CeilRatio(min_p, max_a)),
968 {integer_trail_.UpperBoundAsLiteral(a),
969 integer_trail_.LowerBoundAsLiteral(p_), a.GreaterOrEqual(0)})) {
985 IntegerValue max_p) {
986 const IntegerValue max_a = integer_trail_.UpperBound(a);
987 if (max_a <= 0)
return true;
990 if (max_a >= min_p) {
992 if (!enforcement_helper_.SafeEnqueue(
993 enforcement_id_, a.LowerOrEqual(max_p),
994 {p_.LowerOrEqual(max_p), p_.GreaterOrEqual(1)})) {
1001 const IntegerValue min_pos_b =
CeilRatio(min_p, max_a);
1002 if (min_pos_b > integer_trail_.UpperBound(
b)) {
1003 if (!enforcement_helper_.SafeEnqueue(
1004 enforcement_id_,
b.LowerOrEqual(0),
1005 {integer_trail_.LowerBoundAsLiteral(p_),
1006 integer_trail_.UpperBoundAsLiteral(a),
1007 integer_trail_.UpperBoundAsLiteral(b)})) {
1013 const IntegerValue new_max_a =
FloorRatio(max_p, min_pos_b);
1014 if (new_max_a < integer_trail_.UpperBound(a)) {
1015 if (!enforcement_helper_.SafeEnqueue(
1016 enforcement_id_, a.LowerOrEqual(new_max_a),
1017 {integer_trail_.LowerBoundAsLiteral(p_),
1018 integer_trail_.UpperBoundAsLiteral(a),
1019 integer_trail_.UpperBoundAsLiteral(p_)})) {
1029 const int64_t min_a = integer_trail_.LowerBound(a_).value();
1030 const int64_t max_a = integer_trail_.UpperBound(a_).value();
1031 const int64_t min_b = integer_trail_.LowerBound(b_).value();
1032 const int64_t max_b = integer_trail_.UpperBound(b_).value();
1033 const int64_t min_p = integer_trail_.LowerBound(p_).value();
1034 const int64_t max_p = integer_trail_.UpperBound(p_).value();
1035 const int64_t p1 =
CapProdI(max_a, max_b).value();
1036 const int64_t p2 =
CapProdI(max_a, min_b).value();
1037 const int64_t p3 =
CapProdI(min_a, max_b).value();
1038 const int64_t p4 =
CapProdI(min_a, min_b).value();
1039 const int64_t min_ab = std::min({p1, p2, p3, p4});
1040 const int64_t max_ab = std::max({p1, p2, p3, p4});
1043 if (min_ab > max_p) {
1044 return enforcement_helper_.PropagateWhenFalse(
1047 {a_.GreaterOrEqual(min_a), a_.LowerOrEqual(max_a),
1048 b_.GreaterOrEqual(min_b), b_.LowerOrEqual(max_b),
1049 p_.LowerOrEqual(max_p)});
1051 if (min_p > max_ab) {
1052 return enforcement_helper_.PropagateWhenFalse(
1055 {a_.GreaterOrEqual(min_a), a_.LowerOrEqual(max_a),
1056 b_.GreaterOrEqual(min_b), b_.LowerOrEqual(max_b),
1057 p_.GreaterOrEqual(min_p)});
1064 if (!CanonicalizeCases())
return false;
1068 const int64_t min_a = integer_trail_.LowerBound(a_).value();
1069 const int64_t min_b = integer_trail_.LowerBound(b_).value();
1070 if (min_a >= 0 && min_b >= 0) {
1072 DCHECK_GE(integer_trail_.LowerBound(p_), 0);
1073 return PropagateWhenAllNonNegative();
1082 const IntegerValue max_a = integer_trail_.UpperBound(a_);
1083 const IntegerValue max_b = integer_trail_.UpperBound(b_);
1084 const IntegerValue p1 =
CapProdI(max_a, max_b);
1085 const IntegerValue p2 =
CapProdI(max_a, min_b);
1086 const IntegerValue p3 =
CapProdI(min_a, max_b);
1087 const IntegerValue p4 =
CapProdI(min_a, min_b);
1088 const IntegerValue new_max_p = std::max({p1, p2, p3, p4});
1089 if (new_max_p < integer_trail_.UpperBound(p_)) {
1090 if (!enforcement_helper_.SafeEnqueue(
1091 enforcement_id_, p_.LowerOrEqual(new_max_p),
1092 {integer_trail_.LowerBoundAsLiteral(a_),
1093 integer_trail_.LowerBoundAsLiteral(b_),
1094 integer_trail_.UpperBoundAsLiteral(a_),
1095 integer_trail_.UpperBoundAsLiteral(b_)})) {
1099 const IntegerValue new_min_p = std::min({p1, p2, p3, p4});
1100 if (new_min_p > integer_trail_.LowerBound(p_)) {
1101 if (!enforcement_helper_.SafeEnqueue(
1102 enforcement_id_, p_.GreaterOrEqual(new_min_p),
1103 {integer_trail_.LowerBoundAsLiteral(a_),
1104 integer_trail_.LowerBoundAsLiteral(b_),
1105 integer_trail_.UpperBoundAsLiteral(a_),
1106 integer_trail_.UpperBoundAsLiteral(b_)})) {
1112 const IntegerValue min_p = integer_trail_.LowerBound(p_);
1113 const IntegerValue max_p = integer_trail_.UpperBound(p_);
1116 const bool zero_is_possible = min_p <= 0;
1117 if (!zero_is_possible) {
1118 if (integer_trail_.LowerBound(a_) == 0) {
1119 if (!enforcement_helper_.SafeEnqueue(
1120 enforcement_id_, a_.GreaterOrEqual(1),
1121 {p_.GreaterOrEqual(1), a_.GreaterOrEqual(0)})) {
1125 if (integer_trail_.LowerBound(b_) == 0) {
1126 if (!enforcement_helper_.SafeEnqueue(
1127 enforcement_id_, b_.GreaterOrEqual(1),
1128 {p_.GreaterOrEqual(1), b_.GreaterOrEqual(0)})) {
1132 if (integer_trail_.LowerBound(a_) >= 0 &&
1133 integer_trail_.LowerBound(b_) <= 0) {
1134 return enforcement_helper_.SafeEnqueue(
1135 enforcement_id_, b_.GreaterOrEqual(1),
1136 {a_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1138 if (integer_trail_.LowerBound(b_) >= 0 &&
1139 integer_trail_.LowerBound(a_) <= 0) {
1140 return enforcement_helper_.SafeEnqueue(
1141 enforcement_id_, a_.GreaterOrEqual(1),
1142 {b_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1146 for (
int i = 0;
i < 2; ++
i) {
1150 const IntegerValue max_b = integer_trail_.UpperBound(
b);
1151 const IntegerValue min_b = integer_trail_.LowerBound(
b);
1155 if (zero_is_possible && min_b <= 0)
continue;
1158 if (min_b < 0 && max_b > 0) {
1163 if (!PropagateMaxOnPositiveProduct(a,
b, min_p, max_p)) {
1166 if (!PropagateMaxOnPositiveProduct(a.
Negated(),
b.Negated(), min_p,
1175 if (min_b <= 0)
continue;
1177 return enforcement_helper_.SafeEnqueue(
1179 {p_.GreaterOrEqual(0), b.GreaterOrEqual(1)});
1182 return enforcement_helper_.SafeEnqueue(
1184 {p_.LowerOrEqual(0), b.GreaterOrEqual(1)});
1188 const IntegerValue new_max_a =
FloorRatio(max_p, min_b);
1189 if (new_max_a < integer_trail_.UpperBound(a)) {
1190 if (!enforcement_helper_.SafeEnqueue(
1192 {integer_trail_.UpperBoundAsLiteral(p_),
1193 integer_trail_.LowerBoundAsLiteral(b)})) {
1197 const IntegerValue new_min_a =
CeilRatio(min_p, min_b);
1198 if (new_min_a > integer_trail_.LowerBound(a)) {
1199 if (!enforcement_helper_.SafeEnqueue(
1201 {integer_trail_.LowerBoundAsLiteral(p_),
1202 integer_trail_.LowerBoundAsLiteral(b)})) {
1212 const int id = watcher->
Register(
this);
1228 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
1229 RegisterWith(watcher));
1230 CHECK_GE(integer_trail_.LevelZeroLowerBound(x), 0);
1236 const IntegerValue min_x = integer_trail_.LowerBound(x_);
1237 const IntegerValue min_s = integer_trail_.LowerBound(s_);
1238 const IntegerValue min_x_square =
CapProdI(min_x, min_x);
1239 const IntegerValue max_x = integer_trail_.UpperBound(x_);
1240 const IntegerValue max_s = integer_trail_.UpperBound(s_);
1241 const IntegerValue max_x_square =
CapProdI(max_x, max_x);
1247 if (min_x_square > max_s) {
1248 return enforcement_helper_.PropagateWhenFalse(
1251 {x_.GreaterOrEqual(min_x), s_.LowerOrEqual(min_x_square - 1)});
1253 if (min_s > max_x_square) {
1254 return enforcement_helper_.PropagateWhenFalse(
1257 {x_.LowerOrEqual(max_x), s_.GreaterOrEqual(max_x_square + 1)});
1264 if (min_x_square > min_s) {
1265 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1266 s_.GreaterOrEqual(min_x_square),
1267 {x_.GreaterOrEqual(min_x)})) {
1270 }
else if (min_x_square < min_s) {
1272 if (!enforcement_helper_.SafeEnqueue(
1273 enforcement_id_, x_.GreaterOrEqual(new_min),
1274 {s_.GreaterOrEqual((new_min - 1) * (new_min - 1) + 1)})) {
1278 if (max_x_square < max_s) {
1279 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1280 s_.LowerOrEqual(max_x_square),
1281 {x_.LowerOrEqual(max_x)})) {
1284 }
else if (max_x_square > max_s) {
1286 if (!enforcement_helper_.SafeEnqueue(
1287 enforcement_id_, x_.LowerOrEqual(new_max),
1288 {s_.LowerOrEqual(CapProdI(new_max + 1, new_max + 1) - 1)})) {
1297 const int id = watcher->
Register(
this);
1310 negated_div_(div.Negated()),
1314 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
1315 RegisterWith(watcher));
1323 if (integer_trail_.LowerBound(denom_) < 0 &&
1324 integer_trail_.UpperBound(denom_) > 0) {
1328 if (integer_trail_.UpperBound(denom_) < 0) {
1329 num_ = num_.Negated();
1330 denom_ = denom_.Negated();
1335 const IntegerValue min_num = integer_trail_.LowerBound(num_);
1336 const IntegerValue max_num = integer_trail_.UpperBound(num_);
1337 const IntegerValue min_denom = integer_trail_.LowerBound(denom_);
1338 const IntegerValue max_denom = integer_trail_.UpperBound(denom_);
1339 const IntegerValue min_div = integer_trail_.LowerBound(div_);
1340 const IntegerValue max_div = integer_trail_.UpperBound(div_);
1343 if (min_num / max_denom > max_div) {
1344 return enforcement_helper_.PropagateWhenFalse(
1347 {num_.GreaterOrEqual(min_num), denom_.LowerOrEqual(max_denom),
1348 div_.LowerOrEqual(max_div)});
1350 if (max_num / min_denom < min_div) {
1351 return enforcement_helper_.PropagateWhenFalse(
1354 {num_.LowerOrEqual(max_num), denom_.GreaterOrEqual(min_denom),
1355 div_.GreaterOrEqual(min_div)});
1362 if (!PropagateSigns(num_, denom_, div_))
return false;
1364 if (integer_trail_.UpperBound(num_) >= 0 &&
1365 integer_trail_.UpperBound(div_) >= 0 &&
1366 !PropagateUpperBounds(num_, denom_, div_)) {
1370 if (integer_trail_.LowerBound(num_) <= 0 &&
1371 integer_trail_.UpperBound(negated_div_) >= 0 &&
1372 !PropagateUpperBounds(num_.Negated(), denom_, negated_div_)) {
1376 if (integer_trail_.LowerBound(num_) >= 0 &&
1377 integer_trail_.LowerBound(div_) >= 0) {
1378 return PropagatePositiveDomains(num_, denom_, div_);
1381 if (integer_trail_.UpperBound(num_) <= 0 &&
1382 integer_trail_.LowerBound(negated_div_) >= 0) {
1383 return PropagatePositiveDomains(num_.Negated(), denom_, negated_div_);
1392 const IntegerValue min_num = integer_trail_.
LowerBound(num);
1393 const IntegerValue max_num = integer_trail_.
UpperBound(num);
1394 const IntegerValue min_div = integer_trail_.
LowerBound(div);
1395 const IntegerValue max_div = integer_trail_.
UpperBound(div);
1398 if (min_num >= 0 && min_div < 0) {
1401 {num.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1407 if (min_num <= 0 && min_div > 0) {
1410 {div.GreaterOrEqual(1), denom.GreaterOrEqual(1)})) {
1416 if (max_num <= 0 && max_div > 0) {
1417 if (!enforcement_helper_.SafeEnqueue(
1419 {num.LowerOrEqual(0), denom.GreaterOrEqual(1)})) {
1425 if (max_num >= 0 && max_div < 0) {
1426 if (!enforcement_helper_.SafeEnqueue(
1428 {div.LowerOrEqual(-1), denom.GreaterOrEqual(1)})) {
1439 const IntegerValue max_num = integer_trail_.UpperBound(num);
1440 const IntegerValue min_denom = integer_trail_.LowerBound(denom);
1441 const IntegerValue max_denom = integer_trail_.UpperBound(denom);
1442 const IntegerValue max_div = integer_trail_.UpperBound(div);
1444 const IntegerValue new_max_div = max_num / min_denom;
1445 if (max_div > new_max_div) {
1446 if (!enforcement_helper_.SafeEnqueue(
1447 enforcement_id_, div.LowerOrEqual(new_max_div),
1448 {num.LowerOrEqual(max_num), denom.GreaterOrEqual(min_denom)})) {
1456 const IntegerValue new_max_num =
1458 if (max_num > new_max_num) {
1459 if (!enforcement_helper_.SafeEnqueue(
1460 enforcement_id_, num.LowerOrEqual(new_max_num),
1461 {denom.LowerOrEqual(max_denom), denom.GreaterOrEqual(1),
1462 div.LowerOrEqual(max_div)})) {
1473 const IntegerValue min_num = integer_trail_.LowerBound(num);
1474 const IntegerValue max_num = integer_trail_.UpperBound(num);
1475 const IntegerValue min_denom = integer_trail_.LowerBound(denom);
1476 const IntegerValue max_denom = integer_trail_.UpperBound(denom);
1477 const IntegerValue min_div = integer_trail_.LowerBound(div);
1478 const IntegerValue max_div = integer_trail_.UpperBound(div);
1480 const IntegerValue new_min_div = min_num / max_denom;
1481 if (min_div < new_min_div) {
1482 if (!enforcement_helper_.SafeEnqueue(
1483 enforcement_id_, div.GreaterOrEqual(new_min_div),
1484 {num.GreaterOrEqual(min_num), denom.LowerOrEqual(max_denom),
1485 denom.GreaterOrEqual(1)})) {
1493 const IntegerValue new_min_num =
CapProdI(min_denom, min_div);
1494 if (min_num < new_min_num) {
1495 if (!enforcement_helper_.SafeEnqueue(
1496 enforcement_id_, num.GreaterOrEqual(new_min_num),
1497 {denom.GreaterOrEqual(min_denom), div.GreaterOrEqual(min_div)})) {
1507 const IntegerValue new_max_denom = max_num / min_div;
1508 if (max_denom > new_max_denom) {
1509 if (!enforcement_helper_.SafeEnqueue(
1510 enforcement_id_, denom.LowerOrEqual(new_max_denom),
1511 {num.LowerOrEqual(max_num), num.GreaterOrEqual(0),
1512 div.GreaterOrEqual(min_div), denom.GreaterOrEqual(1)})) {
1520 const IntegerValue new_min_denom =
CeilRatio(min_num + 1, max_div + 1);
1521 if (min_denom < new_min_denom) {
1522 if (!enforcement_helper_.SafeEnqueue(
1523 enforcement_id_, denom.GreaterOrEqual(new_min_denom),
1524 {num.GreaterOrEqual(min_num), div.LowerOrEqual(max_div),
1525 div.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1534 const int id = watcher->Register(
this);
1535 watcher->WatchAffineExpression(num_,
id);
1536 watcher->WatchAffineExpression(denom_,
id);
1537 watcher->WatchAffineExpression(div_,
id);
1538 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
1551 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
1552 RegisterWith(watcher));
1557 const IntegerValue min_a = integer_trail_.LowerBound(a_);
1558 const IntegerValue max_a = integer_trail_.UpperBound(a_);
1559 IntegerValue min_c = integer_trail_.LowerBound(c_);
1560 IntegerValue max_c = integer_trail_.UpperBound(c_);
1566 if (min_a / b_ > max_c) {
1567 return enforcement_helper_.PropagateWhenFalse(
1570 {a_.GreaterOrEqual(max_c * b_ + 1), c_.LowerOrEqual(max_c)});
1572 if (max_a / b_ < min_c) {
1573 return enforcement_helper_.PropagateWhenFalse(
1576 {a_.LowerOrEqual(min_c * b_ - 1), c_.GreaterOrEqual(min_c)});
1583 if (max_a / b_ < max_c) {
1585 if (!enforcement_helper_.SafeEnqueue(
1586 enforcement_id_, c_.LowerOrEqual(max_c),
1587 {integer_trail_.UpperBoundAsLiteral(a_)})) {
1590 }
else if (max_a / b_ > max_c) {
1591 const IntegerValue new_max_a =
1592 max_c >= 0 ? max_c * b_ + b_ - 1 :
CapProdI(max_c, b_);
1593 CHECK_LT(new_max_a, max_a);
1594 if (!enforcement_helper_.SafeEnqueue(
1595 enforcement_id_, a_.LowerOrEqual(new_max_a),
1596 {integer_trail_.UpperBoundAsLiteral(c_)})) {
1601 if (min_a / b_ > min_c) {
1603 if (!enforcement_helper_.SafeEnqueue(
1604 enforcement_id_, c_.GreaterOrEqual(min_c),
1605 {integer_trail_.LowerBoundAsLiteral(a_)})) {
1608 }
else if (min_a / b_ < min_c) {
1609 const IntegerValue new_min_a =
1610 min_c > 0 ?
CapProdI(min_c, b_) : min_c * b_ - b_ + 1;
1611 CHECK_GT(new_min_a, min_a);
1612 if (!enforcement_helper_.SafeEnqueue(
1613 enforcement_id_, a_.GreaterOrEqual(new_min_a),
1614 {integer_trail_.LowerBoundAsLiteral(c_)})) {
1623 const int id = watcher->
Register(
this);
1635 negated_expr_(expr.Negated()),
1636 negated_target_(target.Negated()),
1641 enforcement_id_ = enforcement_helper_.
Register(enforcement_literals, watcher,
1642 RegisterWith(watcher));
1648 const IntegerValue min_target = integer_trail_.LowerBound(target_);
1649 const IntegerValue max_target = integer_trail_.UpperBound(target_);
1650 if (min_target >= mod_) {
1651 return enforcement_helper_.PropagateWhenFalse(
1652 enforcement_id_, {},
1653 {target_.GreaterOrEqual(mod_)});
1654 }
else if (max_target <= -mod_) {
1655 return enforcement_helper_.PropagateWhenFalse(
1656 enforcement_id_, {},
1657 {target_.LowerOrEqual(-mod_)});
1659 if (min_target > 0) {
1660 if (!PropagateWhenFalseAndTargetIsPositive(expr_, target_))
return false;
1661 }
else if (max_target < 0) {
1662 if (!PropagateWhenFalseAndTargetIsPositive(negated_expr_,
1666 }
else if (!PropagateWhenFalseAndTargetDomainContainsZero()) {
1674 if (!PropagateSignsAndTargetRange())
return false;
1675 if (!PropagateOuterBounds())
return false;
1677 if (integer_trail_.LowerBound(expr_) >= 0) {
1678 if (!PropagateBoundsWhenExprIsNonNegative(expr_, target_))
return false;
1679 }
else if (integer_trail_.UpperBound(expr_) <= 0) {
1680 if (!PropagateBoundsWhenExprIsNonNegative(negated_expr_, negated_target_)) {
1688bool FixedModuloPropagator::PropagateWhenFalseAndTargetIsPositive(
1690 const IntegerValue min_expr = integer_trail_.
LowerBound(expr);
1691 const IntegerValue max_expr = integer_trail_.
UpperBound(expr);
1695 const IntegerValue min_expr_mod =
1696 std::max(IntegerValue(0), integer_trail_.
LowerBound(target));
1697 const IntegerValue max_expr_mod =
1698 std::min(mod_ - 1, integer_trail_.
UpperBound(target));
1704 if (max_expr < min_expr_mod) {
1707 enforcement_id_, {},
1713 if (min_expr > max_expr_mod + k * mod_) {
1715 return enforcement_helper_.PropagateWhenFalse(
1716 enforcement_id_, {},
1721 max_expr_mod < mod_ - 1
1723 : integer_trail_.LevelZeroUpperBound(target))});
1728bool FixedModuloPropagator::PropagateWhenFalseAndTargetDomainContainsZero() {
1729 const IntegerValue neg_max_expr_mod =
1730 std::max(-mod_ + 1, integer_trail_.LowerBound(target_));
1731 const IntegerValue pos_max_expr_mod =
1732 std::min(mod_ - 1, integer_trail_.UpperBound(target_));
1739 const IntegerValue min_expr = integer_trail_.LowerBound(expr_);
1740 const IntegerValue max_expr = integer_trail_.UpperBound(expr_);
1743 if (k >= 0 && min_expr > pos_max_expr_mod + k * mod_) {
1744 const IntegerValue min_target = integer_trail_.LowerBound(target_);
1746 return enforcement_helper_.PropagateWhenFalse(
1747 enforcement_id_, {},
1748 {expr_.GreaterOrEqual(pos_max_expr_mod + k * mod_ + 1),
1749 expr_.LowerOrEqual((k + 1) * mod_ - 1),
1750 target_.GreaterOrEqual(min_target),
1751 target_.LowerOrEqual(pos_max_expr_mod)});
1755 if (k >= 0 && max_expr < neg_max_expr_mod - k * mod_) {
1756 const IntegerValue max_target = integer_trail_.UpperBound(target_);
1758 return enforcement_helper_.PropagateWhenFalse(
1759 enforcement_id_, {},
1760 {expr_.GreaterOrEqual(-(k + 1) * mod_ + 1),
1761 expr_.LowerOrEqual(neg_max_expr_mod - k * mod_ - 1),
1762 target_.GreaterOrEqual(neg_max_expr_mod),
1763 target_.LowerOrEqual(max_target)});
1768bool FixedModuloPropagator::PropagateSignsAndTargetRange() {
1770 if (integer_trail_.UpperBound(target_) >= mod_) {
1771 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1772 target_.LowerOrEqual(mod_ - 1), {})) {
1777 if (integer_trail_.LowerBound(target_) <= -mod_) {
1778 if (!enforcement_helper_.SafeEnqueue(
1779 enforcement_id_, target_.GreaterOrEqual(1 - mod_), {})) {
1785 if (integer_trail_.LowerBound(expr_) >= 0 &&
1786 integer_trail_.LowerBound(target_) < 0) {
1788 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1789 target_.GreaterOrEqual(0),
1790 {expr_.GreaterOrEqual(0)})) {
1795 if (integer_trail_.UpperBound(expr_) <= 0 &&
1796 integer_trail_.UpperBound(target_) > 0) {
1798 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1799 target_.LowerOrEqual(0),
1800 {expr_.LowerOrEqual(0)})) {
1805 if (integer_trail_.LowerBound(target_) > 0 &&
1806 integer_trail_.LowerBound(expr_) <= 0) {
1808 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1809 expr_.GreaterOrEqual(1),
1810 {target_.GreaterOrEqual(1)})) {
1815 if (integer_trail_.UpperBound(target_) < 0 &&
1816 integer_trail_.UpperBound(expr_) >= 0) {
1818 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1819 expr_.LowerOrEqual(-1),
1820 {target_.LowerOrEqual(-1)})) {
1828bool FixedModuloPropagator::PropagateOuterBounds() {
1829 const IntegerValue min_expr = integer_trail_.LowerBound(expr_);
1830 const IntegerValue max_expr = integer_trail_.UpperBound(expr_);
1831 const IntegerValue min_target = integer_trail_.LowerBound(target_);
1832 const IntegerValue max_target = integer_trail_.UpperBound(target_);
1834 if (max_expr % mod_ > max_target) {
1835 if (!enforcement_helper_.SafeEnqueue(
1837 expr_.LowerOrEqual((max_expr / mod_) * mod_ + max_target),
1838 {integer_trail_.UpperBoundAsLiteral(target_),
1839 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1844 if (min_expr % mod_ < min_target) {
1845 if (!enforcement_helper_.SafeEnqueue(
1847 expr_.GreaterOrEqual((min_expr / mod_) * mod_ + min_target),
1848 {integer_trail_.LowerBoundAsLiteral(expr_),
1849 integer_trail_.LowerBoundAsLiteral(target_)})) {
1854 if (min_expr / mod_ == max_expr / mod_) {
1855 if (min_target < min_expr % mod_) {
1856 if (!enforcement_helper_.SafeEnqueue(
1858 target_.GreaterOrEqual(min_expr - (min_expr / mod_) * mod_),
1859 {integer_trail_.LowerBoundAsLiteral(target_),
1860 integer_trail_.UpperBoundAsLiteral(target_),
1861 integer_trail_.LowerBoundAsLiteral(expr_),
1862 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1867 if (max_target > max_expr % mod_) {
1868 if (!enforcement_helper_.SafeEnqueue(
1870 target_.LowerOrEqual(max_expr - (max_expr / mod_) * mod_),
1871 {integer_trail_.LowerBoundAsLiteral(target_),
1872 integer_trail_.UpperBoundAsLiteral(target_),
1873 integer_trail_.LowerBoundAsLiteral(expr_),
1874 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1878 }
else if (min_expr / mod_ == 0 && min_target < 0) {
1880 if (min_target < min_expr) {
1881 if (!enforcement_helper_.SafeEnqueue(
1882 enforcement_id_, target_.GreaterOrEqual(min_expr),
1883 {integer_trail_.LowerBoundAsLiteral(target_),
1884 integer_trail_.LowerBoundAsLiteral(expr_)})) {
1888 }
else if (max_expr / mod_ == 0 && max_target > 0) {
1890 if (max_target > max_expr) {
1891 if (!enforcement_helper_.SafeEnqueue(
1892 enforcement_id_, target_.LowerOrEqual(max_expr),
1893 {integer_trail_.UpperBoundAsLiteral(target_),
1894 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1903bool FixedModuloPropagator::PropagateBoundsWhenExprIsNonNegative(
1905 const IntegerValue min_target = integer_trail_.LowerBound(target);
1906 DCHECK_GE(min_target, 0);
1907 const IntegerValue max_target = integer_trail_.UpperBound(target);
1911 if (min_target == 0 && max_target == mod_ - 1)
return true;
1913 const IntegerValue min_expr = integer_trail_.LowerBound(expr);
1914 const IntegerValue max_expr = integer_trail_.UpperBound(expr);
1916 if (max_expr % mod_ < min_target) {
1917 DCHECK_GE(max_expr, 0);
1918 if (!enforcement_helper_.SafeEnqueue(
1920 expr.LowerOrEqual((max_expr / mod_ - 1) * mod_ + max_target),
1921 {integer_trail_.UpperBoundAsLiteral(expr),
1922 integer_trail_.LowerBoundAsLiteral(target),
1923 integer_trail_.UpperBoundAsLiteral(target)})) {
1928 if (min_expr % mod_ > max_target) {
1929 DCHECK_GE(min_expr, 0);
1930 if (!enforcement_helper_.SafeEnqueue(
1932 expr.GreaterOrEqual((min_expr / mod_ + 1) * mod_ + min_target),
1933 {integer_trail_.LowerBoundAsLiteral(target),
1934 integer_trail_.UpperBoundAsLiteral(target),
1935 integer_trail_.LowerBoundAsLiteral(expr)})) {
1944 const int id = watcher->Register(
this);
1945 watcher->WatchAffineExpression(expr_,
id);
1946 watcher->WatchAffineExpression(target_,
id);
1947 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(
id);
static int64_t GCD64(int64_t x, int64_t y)
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
DivisionPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression num, AffineExpression denom, AffineExpression div, Model *model)
ABSL_MUST_USE_RESULT bool PropagateWhenFalse(EnforcementId id, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
EnforcementStatus Status(EnforcementId id) const
ABSL_MUST_USE_RESULT bool SafeEnqueue(EnforcementId id, IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
FixedDivisionPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression a, IntegerValue b, AffineExpression c, Model *model)
FixedModuloPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression expr, IntegerValue mod, AffineExpression target, Model *model)
void RegisterReversibleInt(int id, int *rev)
void WatchAffineExpression(AffineExpression e, int id)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
void WatchLiteral(Literal l, int id, int watch_index=-1)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
GreaterThanMinOfExprsPropagator(absl::Span< const Literal > enforcement_literals, std::vector< LinearExpression > exprs, IntegerVariable min_var, Model *model)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
IntegerValue LowerBound(IntegerVariable i) const
bool VariableLowerBoundIsFromLevelZero(IntegerVariable var) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
LevelZeroEquality(IntegerVariable target, const std::vector< IntegerVariable > &vars, const std::vector< IntegerValue > &coeffs, Model *model)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
std::pair< IntegerValue, IntegerValue > ConditionalLb(IntegerLiteral integer_literal, IntegerVariable target_var) const
void RegisterWith(GenericLiteralWatcher *watcher)
LinearConstraintPropagator(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coeffs, IntegerValue upper_bound, Model *model)
bool PropagateAtLevelZero()
LiteralIndex Index() const
void RegisterWith(GenericLiteralWatcher *watcher)
MinPropagator(std::vector< AffineExpression > vars, AffineExpression min_var, IntegerTrail *integer_trail)
ProductPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression a, AffineExpression b, AffineExpression p, Model *model)
SquarePropagator(absl::Span< const Literal > enforcement_literals, AffineExpression x, AffineExpression s, Model *model)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
int64_t FloorSquareRoot(int64_t a)
int64_t CeilSquareRoot(int64_t a)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
IntegerVariable PositiveVariable(IntegerVariable i)
IntegerValue PositiveRemainder(IntegerValue dividend, IntegerValue positive_divisor)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
@ CAN_PROPAGATE_ENFORCEMENT
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
static int input(yyscan_t yyscanner)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
AffineExpression Negated() const
IntegerLiteral LowerOrEqual(IntegerValue bound) const
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral TrueLiteral()
IntegerLiteral Negated() const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const Literal > boolean_literals_at_false
absl::Span< const IntegerValue > coeffs
absl::Span< const Literal > boolean_literals_at_true
absl::Span< const IntegerVariable > vars
absl::Span< const IntegerLiteral > integer_literals