104 rev_num_fixed_vars_ = 0;
105 rev_lb_fixed_vars_ = IntegerValue(0);
108template <
bool use_
int128>
109void LinearConstraintPropagator<use_int128>::FillIntegerReason() {
110 shared_->integer_reason.clear();
111 shared_->reason_coeffs.clear();
112 for (
int i = 0;
i < size_; ++
i) {
113 const IntegerVariable var = vars_[
i];
114 if (!shared_->integer_trail->VariableLowerBoundIsFromLevelZero(var)) {
115 shared_->integer_reason.push_back(
116 shared_->integer_trail->LowerBoundAsLiteral(var));
117 shared_->reason_coeffs.push_back(coeffs_[
i]);
123IntegerValue CappedCast(absl::int128
input, IntegerValue cap) {
124 if (
input >= absl::int128(cap.value())) {
127 return IntegerValue(
static_cast<int64_t
>(
input));
133template <
bool use_
int128>
134std::pair<IntegerValue, IntegerValue>
136 IntegerLiteral integer_literal, IntegerVariable target_var)
const {
140 if (integer_literal.
var == target_var) {
148 bool literal_var_present =
false;
149 bool literal_var_present_positively =
false;
150 IntegerValue var_coeff;
152 bool target_var_present_negatively =
false;
153 absl::int128 target_coeff;
158 absl::int128 lb_128 = 0;
159 for (
int i = 0;
i < size_; ++
i) {
160 const IntegerVariable var = vars_[
i];
161 const IntegerValue coeff = coeffs_[
i];
163 target_coeff = absl::int128(coeff.value());
164 target_var_present_negatively =
true;
167 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
168 lb_128 += absl::int128(coeff.value()) * absl::int128(lb.value());
171 literal_var_present =
true;
172 literal_var_present_positively = (var == integer_literal.
var);
176 if (!literal_var_present || !target_var_present_negatively) {
182 const absl::int128 slack128 = absl::int128(upper_bound_.value()) - lb_128;
183 const IntegerValue target_lb = shared_->integer_trail->LowerBound(target_var);
184 const IntegerValue target_ub = shared_->integer_trail->UpperBound(target_var);
188 return {target_ub, target_ub};
191 const IntegerValue target_diff = target_ub - target_lb;
192 const IntegerValue delta = CappedCast(slack128 / target_coeff, target_diff);
195 if (literal_var_present_positively) {
199 const IntegerValue diff =
200 std::max(IntegerValue(0),
201 integer_literal.
bound -
202 shared_->integer_trail->LowerBound(integer_literal.
var));
203 const absl::int128 tighter_slack =
204 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
205 absl::int128(diff.value()));
206 const IntegerValue tighter_delta =
207 CappedCast(tighter_slack / target_coeff, target_diff);
208 return {target_ub - delta, target_ub - tighter_delta};
213 const IntegerValue diff =
214 std::max(IntegerValue(0),
215 shared_->integer_trail->UpperBound(integer_literal.
var) -
216 integer_literal.
bound + 1);
217 const absl::int128 tighter_slack =
218 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
219 absl::int128(diff.value()));
220 const IntegerValue tighter_delta =
221 CappedCast(tighter_slack / target_coeff, target_diff);
222 return {target_ub - tighter_delta, target_ub - delta};
226template <
bool use_
int128>
228 int , IntegerValue propagation_slack, IntegerVariable var_to_explain,
229 int trail_index, std::vector<Literal>* literals_reason,
230 std::vector<int>* trail_indices_reason) {
231 *literals_reason = literal_reason_;
232 trail_indices_reason->clear();
233 shared_->reason_coeffs.clear();
234 for (
int i = 0;
i < size_; ++
i) {
235 const IntegerVariable var = vars_[
i];
240 shared_->integer_trail->FindTrailIndexOfVarBefore(var, trail_index);
242 trail_indices_reason->push_back(index);
243 if (propagation_slack > 0) {
244 shared_->reason_coeffs.push_back(coeffs_[
i]);
248 if (propagation_slack > 0) {
249 shared_->integer_trail->RelaxLinearReason(
250 propagation_slack, shared_->reason_coeffs, trail_indices_reason);
254template <
bool use_
int128>
258 int num_unassigned_enforcement_literal = 0;
260 for (
const Literal negated_enforcement : literal_reason_) {
262 if (shared_->assignment.LiteralIsFalse(literal))
return true;
263 if (!shared_->assignment.LiteralIsTrue(literal)) {
264 ++num_unassigned_enforcement_literal;
265 unique_unnasigned_literal = literal.
Index();
271 if (num_unassigned_enforcement_literal > 1)
return true;
273 int num_fixed_vars = rev_num_fixed_vars_;
274 IntegerValue lb_fixed_vars = rev_lb_fixed_vars_;
277 absl::int128 lb_128 = 0;
278 IntegerValue lb_unfixed_vars = IntegerValue(0);
279 for (
int i = num_fixed_vars;
i < size_; ++
i) {
280 const IntegerVariable var = vars_[
i];
281 const IntegerValue coeff = coeffs_[
i];
282 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
283 const IntegerValue ub = shared_->integer_trail->UpperBound(var);
285 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
290 max_variations_[
i] = (ub - lb) * coeff;
291 lb_unfixed_vars += lb * coeff;
294 std::swap(vars_[
i], vars_[num_fixed_vars]);
295 std::swap(coeffs_[
i], coeffs_[num_fixed_vars]);
296 std::swap(max_variations_[
i], max_variations_[num_fixed_vars]);
298 lb_fixed_vars += lb * coeff;
301 shared_->time_limit->AdvanceDeterministicTime(
302 static_cast<double>(size_ - num_fixed_vars) * 5e-9);
305 if (is_registered_ && num_fixed_vars != rev_num_fixed_vars_) {
307 shared_->rev_integer_value_repository->SaveState(&rev_lb_fixed_vars_);
308 shared_->rev_int_repository->SaveState(&rev_num_fixed_vars_);
309 rev_num_fixed_vars_ = num_fixed_vars;
310 rev_lb_fixed_vars_ = lb_fixed_vars;
316 const absl::int128 max_slack = std::numeric_limits<int64_t>::max() - 1;
320 absl::int128 slack128;
322 slack128 = absl::int128(upper_bound_.value()) - lb_128;
326 slack =
static_cast<int64_t
>(std::max(-max_slack, slack128));
329 slack = upper_bound_ - (lb_fixed_vars + lb_unfixed_vars);
333 shared_->integer_trail->RelaxLinearReason(
334 -slack - 1, shared_->reason_coeffs, &shared_->integer_reason);
336 if (num_unassigned_enforcement_literal == 1) {
339 std::vector<Literal> tmp = literal_reason_;
340 tmp.erase(std::find(tmp.begin(), tmp.end(), to_propagate));
341 shared_->integer_trail->EnqueueLiteral(to_propagate, tmp,
342 shared_->integer_reason);
345 return shared_->integer_trail->ReportConflict(literal_reason_,
346 shared_->integer_reason);
350 if (num_unassigned_enforcement_literal > 0)
return true;
354 for (
int i = num_fixed_vars;
i < size_; ++
i) {
355 if (!use_int128 && max_variations_[
i] <= slack)
continue;
359 const IntegerVariable var = vars_[
i];
360 const IntegerValue coeff = coeffs_[
i];
361 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
364 IntegerValue propagation_slack;
366 const absl::int128 coeff128 = absl::int128(coeff.value());
367 const absl::int128 div128 = slack128 / coeff128;
368 const IntegerValue ub = shared_->integer_trail->UpperBound(var);
369 if (absl::int128(lb.value()) + div128 >= absl::int128(ub.value())) {
372 new_ub = lb + IntegerValue(
static_cast<int64_t
>(div128));
373 propagation_slack =
static_cast<int64_t
>(
374 std::min(max_slack, (div128 + 1) * coeff128 - slack128 - 1));
376 const IntegerValue div = slack / coeff;
378 propagation_slack = (div + 1) * coeff - slack - 1;
380 if (!shared_->integer_trail->EnqueueWithLazyReason(
393template <
bool use_
int128>
397 if (!literal_reason_.empty())
return true;
400 absl::int128 lb_128 = 0;
401 IntegerValue min_activity = IntegerValue(0);
402 for (
int i = 0;
i < size_; ++
i) {
403 const IntegerVariable var = vars_[
i];
404 const IntegerValue coeff = coeffs_[
i];
405 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(var);
407 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
409 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(var);
410 max_variations_[
i] = (ub - lb) * coeff;
411 min_activity += lb * coeff;
414 shared_->time_limit->AdvanceDeterministicTime(
415 static_cast<double>(size_ * 1e-9));
419 absl::int128 slack128;
421 slack128 = absl::int128(upper_bound_.value()) - lb_128;
423 return shared_->integer_trail->ReportConflict({}, {});
426 slack = upper_bound_ - min_activity;
428 return shared_->integer_trail->ReportConflict({}, {});
434 for (
int i = 0;
i < size_; ++
i) {
435 if (!use_int128 && max_variations_[
i] <= slack)
continue;
437 const IntegerVariable var = vars_[
i];
438 const IntegerValue coeff = coeffs_[
i];
439 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(var);
443 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(var);
444 if (absl::int128((ub - lb).value()) * absl::int128(coeff.value()) <=
448 const absl::int128 div128 = slack128 / absl::int128(coeff.value());
449 new_ub = lb + IntegerValue(
static_cast<int64_t
>(div128));
451 const IntegerValue div = slack / coeff;
454 if (!shared_->integer_trail->Enqueue(
463template <
bool use_
int128>
466 is_registered_ =
true;
467 const int id = watcher->
Register(
this);
468 for (
int i = 0;
i < size_; ++
i) {
471 for (
const Literal negated_enforcement : literal_reason_) {
476 watcher->
WatchLiteral(negated_enforcement.Negated(),
id);
485 const std::vector<IntegerVariable>& vars,
486 const std::vector<IntegerValue>& coeffs,
491 trail_(model->GetOrCreate<
Trail>()),
494 const int id = watcher->
Register(
this);
495 watcher->SetPropagatorPriority(
id, 2);
496 watcher->WatchIntegerVariable(target,
id);
497 for (
const IntegerVariable& var : vars_) {
498 watcher->WatchIntegerVariable(var,
id);
512 if (trail_->CurrentDecisionLevel() != 0)
return true;
516 for (
int i = 0;
i < vars_.size(); ++
i) {
517 if (integer_trail_->IsFixed(vars_[
i])) {
518 sum += coeffs_[
i] * integer_trail_->LowerBound(vars_[
i]);
524 if (gcd == 0)
return true;
527 VLOG(1) <<
"Objective gcd: " << gcd;
530 gcd_ = IntegerValue(gcd);
532 const IntegerValue lb = integer_trail_->LowerBound(target_);
534 if (lb_remainder != 0) {
535 if (!integer_trail_->Enqueue(
541 const IntegerValue ub = integer_trail_->UpperBound(target_);
542 const IntegerValue ub_remainder =
544 if (ub_remainder != 0) {
545 if (!integer_trail_->Enqueue(
556 : vars_(
std::move(vars)),
558 integer_trail_(integer_trail) {}
561 if (vars_.empty())
return true;
566 integer_trail_->UpperBoundAsLiteral(min_var_);
567 const IntegerValue current_min_ub = integer_trail_->UpperBound(min_var_);
568 int num_intervals_that_can_be_min = 0;
569 int last_possible_min_interval = 0;
572 for (
int i = 0;
i < vars_.size(); ++
i) {
573 const IntegerValue lb = integer_trail_->LowerBound(vars_[
i]);
574 min = std::min(min, lb);
575 if (lb <= current_min_ub) {
576 ++num_intervals_that_can_be_min;
577 last_possible_min_interval =
i;
582 if (min > integer_trail_->LowerBound(min_var_)) {
583 integer_reason_.clear();
585 integer_reason_.push_back(var.GreaterOrEqual(min));
587 if (!integer_trail_->SafeEnqueue(min_var_.GreaterOrEqual(min),
594 if (num_intervals_that_can_be_min == 1) {
595 const IntegerValue ub_of_only_candidate =
596 integer_trail_->UpperBound(vars_[last_possible_min_interval]);
597 if (current_min_ub < ub_of_only_candidate) {
598 integer_reason_.clear();
602 integer_reason_.push_back(min_ub_literal);
604 if (var == vars_[last_possible_min_interval])
continue;
605 integer_reason_.push_back(var.GreaterOrEqual(current_min_ub + 1));
607 if (!integer_trail_->SafeEnqueue(
608 vars_[last_possible_min_interval].LowerOrEqual(current_min_ub),
621 if (num_intervals_that_can_be_min == 0) {
622 integer_reason_.clear();
625 integer_reason_.push_back(min_ub_literal);
629 integer_reason_.push_back(lit);
632 return integer_trail_->ReportConflict(integer_reason_);
639 const int id = watcher->
Register(
this);
647 IntegerVariable min_var,
Model* model)
648 : exprs_(
std::move(exprs)),
654 IntegerVariable var_to_explain,
int trail_index,
655 std::vector<Literal>* literals_reason,
656 std::vector<int>* trail_indices_reason) {
657 const auto& vars = exprs_[id].vars;
658 const auto& coeffs = exprs_[id].coeffs;
659 literals_reason->clear();
660 trail_indices_reason->clear();
661 std::vector<IntegerValue> reason_coeffs;
662 const int size = vars.size();
663 for (
int i = 0;
i < size; ++
i) {
664 const IntegerVariable var = vars[
i];
669 integer_trail_->FindTrailIndexOfVarBefore(var, trail_index);
671 trail_indices_reason->push_back(index);
672 if (propagation_slack > 0) {
673 reason_coeffs.push_back(coeffs[
i]);
677 if (propagation_slack > 0) {
678 integer_trail_->RelaxLinearReason(propagation_slack, reason_coeffs,
679 trail_indices_reason);
682 for (
IntegerLiteral reason_lit : integer_reason_for_unique_candidate_) {
684 integer_trail_->FindTrailIndexOfVarBefore(reason_lit.var, trail_index);
686 trail_indices_reason->push_back(index);
691bool LinMinPropagator::PropagateLinearUpperBound(
692 int id, absl::Span<const IntegerVariable> vars,
693 absl::Span<const IntegerValue> coeffs,
const IntegerValue upper_bound) {
694 IntegerValue sum_lb = IntegerValue(0);
695 const int num_vars = vars.size();
696 max_variations_.resize(num_vars);
697 for (
int i = 0;
i < num_vars; ++
i) {
698 const IntegerVariable var = vars[
i];
699 const IntegerValue coeff = coeffs[
i];
702 const IntegerValue lb = integer_trail_->
LowerBound(var);
703 const IntegerValue ub = integer_trail_->
UpperBound(var);
704 max_variations_[
i] = (ub - lb) * coeff;
705 sum_lb += lb * coeff;
709 static_cast<double>(num_vars) * 1e-9);
711 const IntegerValue slack = upper_bound - sum_lb;
714 local_reason_.clear();
715 reason_coeffs_.clear();
716 for (
int i = 0;
i < num_vars; ++
i) {
717 const IntegerVariable var = vars[
i];
720 reason_coeffs_.push_back(coeffs[
i]);
723 integer_trail_->RelaxLinearReason(-slack - 1, reason_coeffs_,
725 local_reason_.insert(local_reason_.end(),
726 integer_reason_for_unique_candidate_.begin(),
727 integer_reason_for_unique_candidate_.end());
728 return integer_trail_->ReportConflict({}, local_reason_);
733 for (
int i = 0;
i < num_vars; ++
i) {
734 if (max_variations_[
i] <= slack)
continue;
736 const IntegerVariable var = vars[
i];
737 const IntegerValue coeff = coeffs[
i];
738 const IntegerValue div = slack / coeff;
739 const IntegerValue new_ub = integer_trail_->LowerBound(var) + div;
740 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
741 if (!integer_trail_->EnqueueWithLazyReason(
751 if (exprs_.empty())
return true;
755 const IntegerValue current_min_ub = integer_trail_->UpperBound(min_var_);
756 int num_intervals_that_can_be_min = 0;
757 int last_possible_min_interval = 0;
761 for (
int i = 0;
i < exprs_.size(); ++
i) {
762 const IntegerValue lb = exprs_[
i].Min(*integer_trail_);
763 expr_lbs_.push_back(lb);
764 min_of_linear_expression_lb = std::min(min_of_linear_expression_lb, lb);
765 if (lb <= current_min_ub) {
766 ++num_intervals_that_can_be_min;
767 last_possible_min_interval =
i;
775 if (min_of_linear_expression_lb > current_min_ub) {
776 min_of_linear_expression_lb = current_min_ub + 1;
778 if (min_of_linear_expression_lb > integer_trail_->LowerBound(min_var_)) {
779 local_reason_.clear();
780 for (
int i = 0;
i < exprs_.size(); ++
i) {
781 const IntegerValue slack = expr_lbs_[
i] - min_of_linear_expression_lb;
782 integer_trail_->AppendRelaxedLinearReason(slack, exprs_[
i].coeffs,
783 exprs_[
i].vars, &local_reason_);
786 min_var_, min_of_linear_expression_lb),
787 {}, local_reason_)) {
796 if (num_intervals_that_can_be_min == 1) {
797 const IntegerValue ub_of_only_candidate =
798 exprs_[last_possible_min_interval].Max(*integer_trail_);
799 if (current_min_ub < ub_of_only_candidate) {
802 if (rev_unique_candidate_ == 0) {
803 integer_reason_for_unique_candidate_.clear();
807 integer_reason_for_unique_candidate_.push_back(
808 integer_trail_->UpperBoundAsLiteral(min_var_));
809 for (
int i = 0;
i < exprs_.size(); ++
i) {
810 if (
i == last_possible_min_interval)
continue;
811 const IntegerValue slack = expr_lbs_[
i] - (current_min_ub + 1);
812 integer_trail_->AppendRelaxedLinearReason(
813 slack, exprs_[
i].coeffs, exprs_[
i].vars,
814 &integer_reason_for_unique_candidate_);
816 rev_unique_candidate_ = 1;
819 return PropagateLinearUpperBound(
820 last_possible_min_interval, exprs_[last_possible_min_interval].vars,
821 exprs_[last_possible_min_interval].coeffs,
822 current_min_ub - exprs_[last_possible_min_interval].offset);
830 const int id = watcher->
Register(
this);
831 bool has_var_also_in_exprs =
false;
833 for (
int i = 0;
i < expr.vars.size(); ++
i) {
834 const IntegerVariable& var = expr.vars[
i];
835 const IntegerValue coeff = expr.coeffs[
i];
841 has_var_also_in_exprs |= (var == min_var_);
846 if (has_var_also_in_exprs) {
854 : a_(a), b_(
b), p_(p), integer_trail_(integer_trail) {}
857bool ProductPropagator::CanonicalizeCases() {
871 p_.
GreaterOrEqual(0), {a_.GreaterOrEqual(0), b_.GreaterOrEqual(0)});
875 if (integer_trail_->UpperBound(p_) <= 0) {
876 if (integer_trail_->LowerBound(a_) < 0) {
877 DCHECK_GT(integer_trail_->UpperBound(a_), 0);
881 DCHECK_LT(integer_trail_->LowerBound(b_), 0);
882 DCHECK_GT(integer_trail_->UpperBound(b_), 0);
897bool ProductPropagator::PropagateWhenAllNonNegative() {
899 const IntegerValue max_a = integer_trail_->UpperBound(a_);
900 const IntegerValue max_b = integer_trail_->UpperBound(b_);
901 const IntegerValue new_max =
CapProdI(max_a, max_b);
902 if (new_max < integer_trail_->
UpperBound(p_)) {
903 if (!integer_trail_->SafeEnqueue(
904 p_.LowerOrEqual(new_max),
905 {integer_trail_->UpperBoundAsLiteral(a_),
906 integer_trail_->UpperBoundAsLiteral(b_), a_.GreaterOrEqual(0),
907 b_.GreaterOrEqual(0)})) {
914 const IntegerValue min_a = integer_trail_->LowerBound(a_);
915 const IntegerValue min_b = integer_trail_->LowerBound(b_);
916 const IntegerValue new_min =
CapProdI(min_a, min_b);
920 if (new_min > integer_trail_->UpperBound(p_)) {
921 return integer_trail_->ReportConflict(
922 {integer_trail_->UpperBoundAsLiteral(p_),
923 integer_trail_->LowerBoundAsLiteral(a_),
924 integer_trail_->LowerBoundAsLiteral(b_)});
926 if (new_min > integer_trail_->LowerBound(p_)) {
927 if (!integer_trail_->SafeEnqueue(
928 p_.GreaterOrEqual(new_min),
929 {integer_trail_->LowerBoundAsLiteral(a_),
930 integer_trail_->LowerBoundAsLiteral(b_)})) {
936 for (
int i = 0;
i < 2; ++
i) {
937 const AffineExpression a =
i == 0 ? a_ : b_;
938 const AffineExpression
b =
i == 0 ? b_ : a_;
939 const IntegerValue max_a = integer_trail_->UpperBound(a);
940 const IntegerValue min_b = integer_trail_->LowerBound(
b);
941 const IntegerValue min_p = integer_trail_->LowerBound(p_);
942 const IntegerValue max_p = integer_trail_->UpperBound(p_);
943 const IntegerValue prod =
CapProdI(max_a, min_b);
945 if (!integer_trail_->SafeEnqueue(a.LowerOrEqual(
FloorRatio(max_p, min_b)),
946 {integer_trail_->LowerBoundAsLiteral(
b),
947 integer_trail_->UpperBoundAsLiteral(p_),
948 p_.GreaterOrEqual(0)})) {
951 }
else if (prod < min_p && max_a != 0) {
952 if (!integer_trail_->SafeEnqueue(
954 {integer_trail_->UpperBoundAsLiteral(a),
955 integer_trail_->LowerBoundAsLiteral(p_), a.GreaterOrEqual(0)})) {
971 IntegerValue max_p) {
972 const IntegerValue max_a = integer_trail_->UpperBound(a);
973 if (max_a <= 0)
return true;
976 if (max_a >= min_p) {
978 if (!integer_trail_->SafeEnqueue(
979 a.LowerOrEqual(max_p),
980 {p_.LowerOrEqual(max_p), p_.GreaterOrEqual(1)})) {
987 const IntegerValue min_pos_b =
CeilRatio(min_p, max_a);
988 if (min_pos_b > integer_trail_->UpperBound(
b)) {
989 if (!integer_trail_->SafeEnqueue(
990 b.LowerOrEqual(0), {integer_trail_->LowerBoundAsLiteral(p_),
991 integer_trail_->UpperBoundAsLiteral(a),
992 integer_trail_->UpperBoundAsLiteral(b)})) {
998 const IntegerValue new_max_a =
FloorRatio(max_p, min_pos_b);
999 if (new_max_a < integer_trail_->
UpperBound(a)) {
1000 if (!integer_trail_->SafeEnqueue(
1001 a.LowerOrEqual(new_max_a),
1002 {integer_trail_->LowerBoundAsLiteral(p_),
1003 integer_trail_->UpperBoundAsLiteral(a),
1004 integer_trail_->UpperBoundAsLiteral(p_)})) {
1012 if (!CanonicalizeCases())
return false;
1016 const int64_t min_a = integer_trail_->LowerBound(a_).value();
1017 const int64_t min_b = integer_trail_->LowerBound(b_).value();
1018 if (min_a >= 0 && min_b >= 0) {
1020 DCHECK_GE(integer_trail_->LowerBound(p_), 0);
1021 return PropagateWhenAllNonNegative();
1030 const IntegerValue max_a = integer_trail_->UpperBound(a_);
1031 const IntegerValue max_b = integer_trail_->UpperBound(b_);
1032 const IntegerValue p1 =
CapProdI(max_a, max_b);
1033 const IntegerValue p2 =
CapProdI(max_a, min_b);
1034 const IntegerValue p3 =
CapProdI(min_a, max_b);
1035 const IntegerValue p4 =
CapProdI(min_a, min_b);
1036 const IntegerValue new_max_p = std::max({p1, p2, p3, p4});
1037 if (new_max_p < integer_trail_->
UpperBound(p_)) {
1038 if (!integer_trail_->SafeEnqueue(
1039 p_.LowerOrEqual(new_max_p),
1040 {integer_trail_->LowerBoundAsLiteral(a_),
1041 integer_trail_->LowerBoundAsLiteral(b_),
1042 integer_trail_->UpperBoundAsLiteral(a_),
1043 integer_trail_->UpperBoundAsLiteral(b_)})) {
1047 const IntegerValue new_min_p = std::min({p1, p2, p3, p4});
1048 if (new_min_p > integer_trail_->LowerBound(p_)) {
1049 if (!integer_trail_->SafeEnqueue(
1050 p_.GreaterOrEqual(new_min_p),
1051 {integer_trail_->LowerBoundAsLiteral(a_),
1052 integer_trail_->LowerBoundAsLiteral(b_),
1053 integer_trail_->UpperBoundAsLiteral(a_),
1054 integer_trail_->UpperBoundAsLiteral(b_)})) {
1060 const IntegerValue min_p = integer_trail_->LowerBound(p_);
1061 const IntegerValue max_p = integer_trail_->UpperBound(p_);
1064 const bool zero_is_possible = min_p <= 0;
1065 if (!zero_is_possible) {
1066 if (integer_trail_->LowerBound(a_) == 0) {
1067 if (!integer_trail_->SafeEnqueue(
1068 a_.GreaterOrEqual(1),
1069 {p_.GreaterOrEqual(1), a_.GreaterOrEqual(0)})) {
1073 if (integer_trail_->LowerBound(b_) == 0) {
1074 if (!integer_trail_->SafeEnqueue(
1075 b_.GreaterOrEqual(1),
1076 {p_.GreaterOrEqual(1), b_.GreaterOrEqual(0)})) {
1080 if (integer_trail_->LowerBound(a_) >= 0 &&
1081 integer_trail_->LowerBound(b_) <= 0) {
1082 return integer_trail_->SafeEnqueue(
1083 b_.GreaterOrEqual(1), {a_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1085 if (integer_trail_->LowerBound(b_) >= 0 &&
1086 integer_trail_->LowerBound(a_) <= 0) {
1087 return integer_trail_->SafeEnqueue(
1088 a_.GreaterOrEqual(1), {b_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1092 for (
int i = 0;
i < 2; ++
i) {
1096 const IntegerValue max_b = integer_trail_->UpperBound(
b);
1097 const IntegerValue min_b = integer_trail_->LowerBound(
b);
1101 if (zero_is_possible && min_b <= 0)
continue;
1104 if (min_b < 0 && max_b > 0) {
1109 if (!PropagateMaxOnPositiveProduct(a,
b, min_p, max_p)) {
1112 if (!PropagateMaxOnPositiveProduct(a.
Negated(),
b.Negated(), min_p,
1121 if (min_b <= 0)
continue;
1123 return integer_trail_->SafeEnqueue(
1124 a.
GreaterOrEqual(0), {p_.GreaterOrEqual(0), b.GreaterOrEqual(1)});
1127 return integer_trail_->SafeEnqueue(
1128 a.
LowerOrEqual(0), {p_.LowerOrEqual(0), b.GreaterOrEqual(1)});
1132 const IntegerValue new_max_a =
FloorRatio(max_p, min_b);
1133 if (new_max_a < integer_trail_->
UpperBound(a)) {
1134 if (!integer_trail_->SafeEnqueue(
1136 {integer_trail_->UpperBoundAsLiteral(p_),
1137 integer_trail_->LowerBoundAsLiteral(b)})) {
1141 const IntegerValue new_min_a =
CeilRatio(min_p, min_b);
1142 if (new_min_a > integer_trail_->LowerBound(a)) {
1143 if (!integer_trail_->SafeEnqueue(
1145 {integer_trail_->LowerBoundAsLiteral(p_),
1146 integer_trail_->LowerBoundAsLiteral(b)})) {
1156 const int id = watcher->
Register(
this);
1165 : x_(x), s_(s), integer_trail_(integer_trail) {
1172 const IntegerValue min_x = integer_trail_->LowerBound(x_);
1173 const IntegerValue min_s = integer_trail_->LowerBound(s_);
1174 const IntegerValue min_x_square =
CapProdI(min_x, min_x);
1175 if (min_x_square > min_s) {
1176 if (!integer_trail_->SafeEnqueue(s_.GreaterOrEqual(min_x_square),
1177 {x_.GreaterOrEqual(min_x)})) {
1180 }
else if (min_x_square < min_s) {
1182 if (!integer_trail_->SafeEnqueue(
1183 x_.GreaterOrEqual(new_min),
1184 {s_.GreaterOrEqual((new_min - 1) * (new_min - 1) + 1)})) {
1189 const IntegerValue max_x = integer_trail_->UpperBound(x_);
1190 const IntegerValue max_s = integer_trail_->UpperBound(s_);
1191 const IntegerValue max_x_square =
CapProdI(max_x, max_x);
1192 if (max_x_square < max_s) {
1193 if (!integer_trail_->SafeEnqueue(s_.LowerOrEqual(max_x_square),
1194 {x_.LowerOrEqual(max_x)})) {
1197 }
else if (max_x_square > max_s) {
1199 if (!integer_trail_->SafeEnqueue(
1200 x_.LowerOrEqual(new_max),
1201 {s_.LowerOrEqual(CapProdI(new_max + 1, new_max + 1) - 1)})) {
1210 const int id = watcher->
Register(
this);
1223 negated_denom_(denom.Negated()),
1224 negated_num_(num.Negated()),
1225 negated_div_(div.Negated()),
1226 integer_trail_(integer_trail) {}
1233 if (integer_trail_->LowerBound(denom_) < 0 &&
1234 integer_trail_->UpperBound(denom_) > 0) {
1243 if (integer_trail_->UpperBound(denom) < 0) {
1244 std::swap(num, negated_num);
1245 std::swap(denom, negated_denom);
1248 if (!PropagateSigns(num, denom, div_))
return false;
1250 if (integer_trail_->UpperBound(num) >= 0 &&
1251 integer_trail_->UpperBound(div_) >= 0 &&
1252 !PropagateUpperBounds(num, denom, div_)) {
1256 if (integer_trail_->UpperBound(negated_num) >= 0 &&
1257 integer_trail_->UpperBound(negated_div_) >= 0 &&
1258 !PropagateUpperBounds(negated_num, denom, negated_div_)) {
1262 if (integer_trail_->LowerBound(num) >= 0 &&
1263 integer_trail_->LowerBound(div_) >= 0) {
1264 return PropagatePositiveDomains(num, denom, div_);
1267 if (integer_trail_->LowerBound(negated_num) >= 0 &&
1268 integer_trail_->LowerBound(negated_div_) >= 0) {
1269 return PropagatePositiveDomains(negated_num, denom, negated_div_);
1278 const IntegerValue min_num = integer_trail_->
LowerBound(num);
1279 const IntegerValue max_num = integer_trail_->
UpperBound(num);
1280 const IntegerValue min_div = integer_trail_->
LowerBound(div);
1281 const IntegerValue max_div = integer_trail_->
UpperBound(div);
1284 if (min_num >= 0 && min_div < 0) {
1287 {num.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1293 if (min_num <= 0 && min_div > 0) {
1296 {div.GreaterOrEqual(1), denom.GreaterOrEqual(1)})) {
1302 if (max_num <= 0 && max_div > 0) {
1303 if (!integer_trail_->SafeEnqueue(
1305 {num.LowerOrEqual(0), denom.GreaterOrEqual(1)})) {
1311 if (max_num >= 0 && max_div < 0) {
1312 if (!integer_trail_->SafeEnqueue(
1314 {div.LowerOrEqual(-1), denom.GreaterOrEqual(1)})) {
1325 const IntegerValue max_num = integer_trail_->UpperBound(num);
1326 const IntegerValue min_denom = integer_trail_->LowerBound(denom);
1327 const IntegerValue max_denom = integer_trail_->UpperBound(denom);
1328 const IntegerValue max_div = integer_trail_->UpperBound(div);
1330 const IntegerValue new_max_div = max_num / min_denom;
1331 if (max_div > new_max_div) {
1332 if (!integer_trail_->SafeEnqueue(
1333 div.LowerOrEqual(new_max_div),
1334 {num.LowerOrEqual(max_num), denom.GreaterOrEqual(min_denom)})) {
1342 const IntegerValue new_max_num =
1344 if (max_num > new_max_num) {
1345 if (!integer_trail_->SafeEnqueue(
1346 num.LowerOrEqual(new_max_num),
1347 {denom.LowerOrEqual(max_denom), denom.GreaterOrEqual(1),
1348 div.LowerOrEqual(max_div)})) {
1359 const IntegerValue min_num = integer_trail_->LowerBound(num);
1360 const IntegerValue max_num = integer_trail_->UpperBound(num);
1361 const IntegerValue min_denom = integer_trail_->LowerBound(denom);
1362 const IntegerValue max_denom = integer_trail_->UpperBound(denom);
1363 const IntegerValue min_div = integer_trail_->LowerBound(div);
1364 const IntegerValue max_div = integer_trail_->UpperBound(div);
1366 const IntegerValue new_min_div = min_num / max_denom;
1367 if (min_div < new_min_div) {
1368 if (!integer_trail_->SafeEnqueue(
1369 div.GreaterOrEqual(new_min_div),
1370 {num.GreaterOrEqual(min_num), denom.LowerOrEqual(max_denom),
1371 denom.GreaterOrEqual(1)})) {
1379 const IntegerValue new_min_num =
CapProdI(min_denom, min_div);
1380 if (min_num < new_min_num) {
1381 if (!integer_trail_->SafeEnqueue(
1382 num.GreaterOrEqual(new_min_num),
1383 {denom.GreaterOrEqual(min_denom), div.GreaterOrEqual(min_div)})) {
1393 const IntegerValue new_max_denom = max_num / min_div;
1394 if (max_denom > new_max_denom) {
1395 if (!integer_trail_->SafeEnqueue(
1396 denom.LowerOrEqual(new_max_denom),
1397 {num.LowerOrEqual(max_num), num.GreaterOrEqual(0),
1398 div.GreaterOrEqual(min_div), denom.GreaterOrEqual(1)})) {
1406 const IntegerValue new_min_denom =
CeilRatio(min_num + 1, max_div + 1);
1407 if (min_denom < new_min_denom) {
1408 if (!integer_trail_->SafeEnqueue(
1409 denom.GreaterOrEqual(new_min_denom),
1410 {num.GreaterOrEqual(min_num), div.LowerOrEqual(max_div),
1411 div.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1420 const int id = watcher->
Register(
this);
1431 : a_(a), b_(
b), c_(c), integer_trail_(integer_trail) {
1436 const IntegerValue min_a = integer_trail_->LowerBound(a_);
1437 const IntegerValue max_a = integer_trail_->UpperBound(a_);
1438 IntegerValue min_c = integer_trail_->LowerBound(c_);
1439 IntegerValue max_c = integer_trail_->UpperBound(c_);
1441 if (max_a / b_ < max_c) {
1443 if (!integer_trail_->SafeEnqueue(
1444 c_.LowerOrEqual(max_c),
1445 {integer_trail_->UpperBoundAsLiteral(a_)})) {
1448 }
else if (max_a / b_ > max_c) {
1449 const IntegerValue new_max_a =
1450 max_c >= 0 ? max_c * b_ + b_ - 1 :
CapProdI(max_c, b_);
1451 CHECK_LT(new_max_a, max_a);
1452 if (!integer_trail_->SafeEnqueue(
1453 a_.LowerOrEqual(new_max_a),
1454 {integer_trail_->UpperBoundAsLiteral(c_)})) {
1459 if (min_a / b_ > min_c) {
1461 if (!integer_trail_->SafeEnqueue(
1462 c_.GreaterOrEqual(min_c),
1463 {integer_trail_->LowerBoundAsLiteral(a_)})) {
1466 }
else if (min_a / b_ < min_c) {
1467 const IntegerValue new_min_a =
1468 min_c > 0 ?
CapProdI(min_c, b_) : min_c * b_ - b_ + 1;
1469 CHECK_GT(new_min_a, min_a);
1470 if (!integer_trail_->SafeEnqueue(
1471 a_.GreaterOrEqual(new_min_a),
1472 {integer_trail_->LowerBoundAsLiteral(c_)})) {
1481 const int id = watcher->
Register(
this);
1490 : expr_(expr), mod_(mod), target_(target), integer_trail_(integer_trail) {
1495 if (!PropagateSignsAndTargetRange())
return false;
1496 if (!PropagateOuterBounds())
return false;
1498 if (integer_trail_->LowerBound(expr_) >= 0) {
1499 if (!PropagateBoundsWhenExprIsPositive(expr_, target_))
return false;
1500 }
else if (integer_trail_->UpperBound(expr_) <= 0) {
1501 if (!PropagateBoundsWhenExprIsPositive(expr_.Negated(),
1502 target_.Negated())) {
1510bool FixedModuloPropagator::PropagateSignsAndTargetRange() {
1512 if (integer_trail_->
UpperBound(target_) >= mod_) {
1518 if (integer_trail_->
LowerBound(target_) <= -mod_) {
1525 if (integer_trail_->LowerBound(expr_) >= 0 &&
1526 integer_trail_->LowerBound(target_) < 0) {
1527 if (!integer_trail_->SafeEnqueue(target_.GreaterOrEqual(0),
1528 {expr_.GreaterOrEqual(0)})) {
1533 if (integer_trail_->UpperBound(expr_) <= 0 &&
1534 integer_trail_->UpperBound(target_) > 0) {
1535 if (!integer_trail_->SafeEnqueue(target_.LowerOrEqual(0),
1536 {expr_.LowerOrEqual(0)})) {
1544bool FixedModuloPropagator::PropagateOuterBounds() {
1545 const IntegerValue min_expr = integer_trail_->LowerBound(expr_);
1546 const IntegerValue max_expr = integer_trail_->UpperBound(expr_);
1547 const IntegerValue min_target = integer_trail_->LowerBound(target_);
1548 const IntegerValue max_target = integer_trail_->UpperBound(target_);
1550 if (max_expr % mod_ > max_target) {
1551 if (!integer_trail_->SafeEnqueue(
1552 expr_.LowerOrEqual((max_expr / mod_) * mod_ + max_target),
1553 {integer_trail_->UpperBoundAsLiteral(target_),
1554 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1559 if (min_expr % mod_ < min_target) {
1560 if (!integer_trail_->SafeEnqueue(
1561 expr_.GreaterOrEqual((min_expr / mod_) * mod_ + min_target),
1562 {integer_trail_->LowerBoundAsLiteral(expr_),
1563 integer_trail_->LowerBoundAsLiteral(target_)})) {
1568 if (min_expr / mod_ == max_expr / mod_) {
1569 if (min_target < min_expr % mod_) {
1570 if (!integer_trail_->SafeEnqueue(
1571 target_.GreaterOrEqual(min_expr - (min_expr / mod_) * mod_),
1572 {integer_trail_->LowerBoundAsLiteral(target_),
1573 integer_trail_->UpperBoundAsLiteral(target_),
1574 integer_trail_->LowerBoundAsLiteral(expr_),
1575 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1580 if (max_target > max_expr % mod_) {
1581 if (!integer_trail_->SafeEnqueue(
1582 target_.LowerOrEqual(max_expr - (max_expr / mod_) * mod_),
1583 {integer_trail_->LowerBoundAsLiteral(target_),
1584 integer_trail_->UpperBoundAsLiteral(target_),
1585 integer_trail_->LowerBoundAsLiteral(expr_),
1586 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1590 }
else if (min_expr / mod_ == 0 && min_target < 0) {
1592 if (min_target < min_expr) {
1593 if (!integer_trail_->SafeEnqueue(
1594 target_.GreaterOrEqual(min_expr),
1595 {integer_trail_->LowerBoundAsLiteral(target_),
1596 integer_trail_->LowerBoundAsLiteral(expr_)})) {
1600 }
else if (max_expr / mod_ == 0 && max_target > 0) {
1602 if (max_target > max_expr) {
1603 if (!integer_trail_->SafeEnqueue(
1604 target_.LowerOrEqual(max_expr),
1605 {integer_trail_->UpperBoundAsLiteral(target_),
1606 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1615bool FixedModuloPropagator::PropagateBoundsWhenExprIsPositive(
1617 const IntegerValue min_target = integer_trail_->LowerBound(target);
1618 DCHECK_GE(min_target, 0);
1619 const IntegerValue max_target = integer_trail_->UpperBound(target);
1623 if (min_target == 0 && max_target == mod_ - 1)
return true;
1625 const IntegerValue min_expr = integer_trail_->LowerBound(expr);
1626 const IntegerValue max_expr = integer_trail_->UpperBound(expr);
1628 if (max_expr % mod_ < min_target) {
1629 DCHECK_GE(max_expr, 0);
1630 if (!integer_trail_->SafeEnqueue(
1631 expr.LowerOrEqual((max_expr / mod_ - 1) * mod_ + max_target),
1632 {integer_trail_->UpperBoundAsLiteral(expr),
1633 integer_trail_->LowerBoundAsLiteral(target),
1634 integer_trail_->UpperBoundAsLiteral(target)})) {
1639 if (min_expr % mod_ > max_target) {
1640 DCHECK_GE(min_expr, 0);
1641 if (!integer_trail_->SafeEnqueue(
1642 expr.GreaterOrEqual((min_expr / mod_ + 1) * mod_ + min_target),
1643 {integer_trail_->LowerBoundAsLiteral(target),
1644 integer_trail_->UpperBoundAsLiteral(target),
1645 integer_trail_->LowerBoundAsLiteral(expr)})) {
1654 const int id = watcher->
Register(
this);