25#include "absl/container/flat_hash_map.h"
26#include "absl/log/check.h"
27#include "absl/numeric/int128.h"
28#include "absl/types/span.h"
45template <
bool use_
int128>
47 absl::Span<const Literal> enforcement_literals,
48 absl::Span<const IntegerVariable> vars,
49 absl::Span<const IntegerValue> coeffs, IntegerValue
upper,
Model*
model)
50 : upper_bound_(
upper),
54 vars_(new IntegerVariable[size_]),
55 coeffs_(new IntegerValue[size_]),
56 max_variations_(new IntegerValue[size_]) {
61 memcpy(vars_.get(), vars.data(), size_ *
sizeof(IntegerVariable));
62 memcpy(coeffs_.get(), coeffs.data(), size_ *
sizeof(IntegerValue));
65 for (
int i = 0;
i < size_; ++
i) {
68 coeffs_[
i] = -coeffs_[
i];
75 literal_reason_.reserve(enforcement_literals.size());
77 literal_reason_.push_back(
literal.Negated());
81 rev_num_fixed_vars_ = 0;
82 rev_lb_fixed_vars_ = IntegerValue(0);
86template <
bool use_
int128>
89 : upper_bound_(
ct.ub),
94 coeffs_(
std::move(
ct.coeffs)),
95 max_variations_(new IntegerValue[size_]) {
100 for (
int i = 0;
i < size_; ++
i) {
101 if (coeffs_[
i] < 0) {
103 coeffs_[
i] = -coeffs_[
i];
108 rev_num_fixed_vars_ = 0;
109 rev_lb_fixed_vars_ = IntegerValue(0);
112template <
bool use_
int128>
114 shared_->integer_reason.clear();
115 shared_->reason_coeffs.clear();
116 for (
int i = 0;
i < size_; ++
i) {
118 if (!shared_->integer_trail->VariableLowerBoundIsFromLevelZero(
var)) {
119 shared_->integer_reason.push_back(
120 shared_->integer_trail->LowerBoundAsLiteral(
var));
121 shared_->reason_coeffs.push_back(coeffs_[
i]);
127IntegerValue CappedCast(absl::int128
input, IntegerValue cap) {
128 if (
input >= absl::int128(cap.value())) {
131 return IntegerValue(
static_cast<int64_t
>(
input));
137template <
bool use_
int128>
138std::pair<IntegerValue, IntegerValue>
140 IntegerLiteral integer_literal, IntegerVariable target_var)
const {
144 if (integer_literal.
var == target_var) {
152 bool literal_var_present =
false;
153 bool literal_var_present_positively =
false;
154 IntegerValue var_coeff;
156 bool target_var_present_negatively =
false;
157 absl::int128 target_coeff;
162 absl::int128 lb_128 = 0;
163 for (
int i = 0;
i < size_; ++
i) {
165 const IntegerValue coeff = coeffs_[
i];
167 target_coeff = absl::int128(coeff.value());
168 target_var_present_negatively =
true;
171 const IntegerValue lb = shared_->integer_trail->LowerBound(
var);
172 lb_128 += absl::int128(coeff.value()) * absl::int128(lb.value());
175 literal_var_present =
true;
176 literal_var_present_positively = (
var == integer_literal.
var);
180 if (!literal_var_present || !target_var_present_negatively) {
186 const absl::int128 slack128 = absl::int128(upper_bound_.value()) - lb_128;
187 const IntegerValue target_lb = shared_->integer_trail->LowerBound(target_var);
188 const IntegerValue target_ub = shared_->integer_trail->UpperBound(target_var);
192 return {target_ub, target_ub};
195 const IntegerValue target_diff = target_ub - target_lb;
196 const IntegerValue
delta = CappedCast(slack128 / target_coeff, target_diff);
199 if (literal_var_present_positively) {
203 const IntegerValue diff =
204 std::max(IntegerValue(0),
205 integer_literal.
bound -
206 shared_->integer_trail->LowerBound(integer_literal.
var));
207 const absl::int128 tighter_slack =
208 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
209 absl::int128(diff.value()));
210 const IntegerValue tighter_delta =
211 CappedCast(tighter_slack / target_coeff, target_diff);
212 return {target_ub -
delta, target_ub - tighter_delta};
217 const IntegerValue diff =
218 std::max(IntegerValue(0),
219 shared_->integer_trail->UpperBound(integer_literal.
var) -
220 integer_literal.
bound + 1);
221 const absl::int128 tighter_slack =
222 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
223 absl::int128(diff.value()));
224 const IntegerValue tighter_delta =
225 CappedCast(tighter_slack / target_coeff, target_diff);
226 return {target_ub - tighter_delta, target_ub -
delta};
230template <
bool use_
int128>
232 int , IntegerValue propagation_slack, IntegerVariable var_to_explain,
233 int trail_index, std::vector<Literal>* literals_reason,
234 std::vector<int>* trail_indices_reason) {
235 *literals_reason = literal_reason_;
236 trail_indices_reason->clear();
237 shared_->reason_coeffs.clear();
238 for (
int i = 0;
i < size_; ++
i) {
244 shared_->integer_trail->FindTrailIndexOfVarBefore(
var, trail_index);
246 trail_indices_reason->push_back(
index);
247 if (propagation_slack > 0) {
248 shared_->reason_coeffs.push_back(coeffs_[
i]);
252 if (propagation_slack > 0) {
253 shared_->integer_trail->RelaxLinearReason(
254 propagation_slack, shared_->reason_coeffs, trail_indices_reason);
258template <
bool use_
int128>
262 int num_unassigned_enforcement_literal = 0;
264 for (
const Literal negated_enforcement : literal_reason_) {
266 if (shared_->assignment.LiteralIsFalse(
literal))
return true;
267 if (!shared_->assignment.LiteralIsTrue(
literal)) {
268 ++num_unassigned_enforcement_literal;
269 unique_unnasigned_literal =
literal.Index();
275 if (num_unassigned_enforcement_literal > 1)
return true;
277 int num_fixed_vars = rev_num_fixed_vars_;
278 IntegerValue lb_fixed_vars = rev_lb_fixed_vars_;
281 absl::int128 lb_128 = 0;
282 IntegerValue lb_unfixed_vars = IntegerValue(0);
283 for (
int i = num_fixed_vars;
i < size_; ++
i) {
285 const IntegerValue coeff = coeffs_[
i];
286 const IntegerValue lb = shared_->integer_trail->LowerBound(
var);
287 const IntegerValue ub = shared_->integer_trail->UpperBound(
var);
289 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
294 max_variations_[
i] = (ub - lb) * coeff;
295 lb_unfixed_vars += lb * coeff;
299 std::swap(coeffs_[
i], coeffs_[num_fixed_vars]);
300 std::swap(max_variations_[
i], max_variations_[num_fixed_vars]);
302 lb_fixed_vars += lb * coeff;
306 static_cast<double>(size_ - num_fixed_vars) * 5e-9);
309 if (is_registered_ && num_fixed_vars != rev_num_fixed_vars_) {
311 shared_->rev_integer_value_repository->SaveState(&rev_lb_fixed_vars_);
312 shared_->rev_int_repository->SaveState(&rev_num_fixed_vars_);
313 rev_num_fixed_vars_ = num_fixed_vars;
314 rev_lb_fixed_vars_ = lb_fixed_vars;
320 const absl::int128 max_slack = std::numeric_limits<int64_t>::max() - 1;
324 absl::int128 slack128;
326 slack128 = absl::int128(upper_bound_.value()) - lb_128;
330 slack =
static_cast<int64_t
>(std::max(-max_slack, slack128));
333 slack = upper_bound_ - (lb_fixed_vars + lb_unfixed_vars);
337 shared_->integer_trail->RelaxLinearReason(
338 -slack - 1, shared_->reason_coeffs, &shared_->integer_reason);
340 if (num_unassigned_enforcement_literal == 1) {
343 std::vector<Literal> tmp = literal_reason_;
344 tmp.erase(std::find(tmp.begin(), tmp.end(), to_propagate));
345 shared_->integer_trail->EnqueueLiteral(to_propagate, tmp,
346 shared_->integer_reason);
349 return shared_->integer_trail->ReportConflict(literal_reason_,
350 shared_->integer_reason);
354 if (num_unassigned_enforcement_literal > 0)
return true;
358 for (
int i = num_fixed_vars;
i < size_; ++
i) {
359 if (!use_int128 && max_variations_[
i] <= slack)
continue;
364 const IntegerValue coeff = coeffs_[
i];
365 const IntegerValue lb = shared_->integer_trail->LowerBound(
var);
368 IntegerValue propagation_slack;
370 const absl::int128 coeff128 = absl::int128(coeff.value());
371 const absl::int128 div128 = slack128 / coeff128;
372 const IntegerValue ub = shared_->integer_trail->UpperBound(
var);
373 if (absl::int128(lb.value()) + div128 >= absl::int128(ub.value())) {
376 new_ub = lb + IntegerValue(
static_cast<int64_t
>(div128));
377 propagation_slack =
static_cast<int64_t
>(
378 std::min(max_slack, (div128 + 1) * coeff128 - slack128 - 1));
380 const IntegerValue div = slack / coeff;
382 propagation_slack = (div + 1) * coeff - slack - 1;
384 if (!shared_->integer_trail->EnqueueWithLazyReason(
397template <
bool use_
int128>
401 if (!literal_reason_.empty())
return true;
404 absl::int128 lb_128 = 0;
405 IntegerValue min_activity = IntegerValue(0);
406 for (
int i = 0;
i < size_; ++
i) {
408 const IntegerValue coeff = coeffs_[
i];
409 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(
var);
411 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
413 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(
var);
414 max_variations_[
i] = (ub - lb) * coeff;
415 min_activity += lb * coeff;
419 static_cast<double>(size_ * 1e-9));
423 absl::int128 slack128;
425 slack128 = absl::int128(upper_bound_.value()) - lb_128;
427 return shared_->integer_trail->ReportConflict({}, {});
430 slack = upper_bound_ - min_activity;
432 return shared_->integer_trail->ReportConflict({}, {});
438 for (
int i = 0;
i < size_; ++
i) {
439 if (!use_int128 && max_variations_[
i] <= slack)
continue;
442 const IntegerValue coeff = coeffs_[
i];
443 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(
var);
447 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(
var);
448 const absl::int128 div128 = slack128 / absl::int128(coeff.value());
449 if (absl::int128(lb.value()) + div128 >= absl::int128(ub.value())) {
452 new_ub = lb + IntegerValue(
static_cast<int64_t
>(div128));
454 const IntegerValue div = slack / coeff;
457 if (!shared_->integer_trail->Enqueue(
466template <
bool use_
int128>
469 is_registered_ =
true;
470 const int id = watcher->
Register(
this);
471 for (
int i = 0;
i < size_; ++
i) {
474 for (
const Literal negated_enforcement : literal_reason_) {
479 watcher->
WatchLiteral(negated_enforcement.Negated(),
id);
488 const std::vector<IntegerVariable>& vars,
489 const std::vector<IntegerValue>& coeffs,
497 const int id = watcher->
Register(
this);
498 watcher->SetPropagatorPriority(
id, 2);
499 watcher->WatchIntegerVariable(target,
id);
500 for (
const IntegerVariable&
var : vars_) {
501 watcher->WatchIntegerVariable(
var,
id);
519 for (
int i = 0;
i < vars_.size(); ++
i) {
520 if (integer_trail_->
IsFixed(vars_[
i])) {
521 sum += coeffs_[
i] * integer_trail_->
LowerBound(vars_[
i]);
527 if (gcd == 0)
return true;
530 VLOG(1) <<
"Objective gcd: " << gcd;
533 gcd_ = IntegerValue(gcd);
535 const IntegerValue lb = integer_trail_->
LowerBound(target_);
537 if (lb_remainder != 0) {
544 const IntegerValue ub = integer_trail_->
UpperBound(target_);
545 const IntegerValue ub_remainder =
547 if (ub_remainder != 0) {
557 IntegerVariable min_var,
559 :
vars_(vars), min_var_(min_var), integer_trail_(integer_trail) {}
562 if (vars_.empty())
return true;
568 const IntegerValue current_min_ub = integer_trail_->
UpperBound(min_var_);
569 int num_intervals_that_can_be_min = 0;
570 int last_possible_min_interval = 0;
573 for (
int i = 0;
i < vars_.size(); ++
i) {
574 const IntegerValue lb = integer_trail_->
LowerBound(vars_[
i]);
576 if (lb <= current_min_ub) {
577 ++num_intervals_that_can_be_min;
578 last_possible_min_interval =
i;
584 integer_reason_.clear();
585 for (
const IntegerVariable
var : vars_) {
589 {}, integer_reason_)) {
595 if (num_intervals_that_can_be_min == 1) {
596 const IntegerValue ub_of_only_candidate =
597 integer_trail_->
UpperBound(vars_[last_possible_min_interval]);
598 if (current_min_ub < ub_of_only_candidate) {
599 integer_reason_.clear();
603 integer_reason_.push_back(min_ub_literal);
604 for (
const IntegerVariable
var : vars_) {
605 if (
var ==
vars_[last_possible_min_interval])
continue;
606 integer_reason_.push_back(
612 {}, integer_reason_)) {
624 if (num_intervals_that_can_be_min == 0) {
625 integer_reason_.clear();
628 integer_reason_.push_back(min_ub_literal);
629 for (
const IntegerVariable
var : vars_) {
630 integer_reason_.push_back(
640 const int id = watcher->
Register(
this);
641 for (
const IntegerVariable&
var : vars_) {
655 IntegerVariable var_to_explain,
int trail_index,
656 std::vector<Literal>* literals_reason,
657 std::vector<int>* trail_indices_reason) {
658 const auto& vars = exprs_[id].vars;
659 const auto& coeffs = exprs_[id].coeffs;
660 literals_reason->clear();
661 trail_indices_reason->clear();
662 std::vector<IntegerValue> reason_coeffs;
663 const int size = vars.size();
664 for (
int i = 0;
i <
size; ++
i) {
665 const IntegerVariable
var = vars[
i];
672 trail_indices_reason->push_back(
index);
673 if (propagation_slack > 0) {
674 reason_coeffs.push_back(coeffs[
i]);
678 if (propagation_slack > 0) {
680 trail_indices_reason);
683 for (
IntegerLiteral reason_lit : integer_reason_for_unique_candidate_) {
687 trail_indices_reason->push_back(
index);
692bool LinMinPropagator::PropagateLinearUpperBound(
693 int id, absl::Span<const IntegerVariable> vars,
694 absl::Span<const IntegerValue> coeffs,
const IntegerValue
upper_bound) {
695 IntegerValue sum_lb = IntegerValue(0);
696 const int num_vars = vars.size();
697 max_variations_.resize(num_vars);
698 for (
int i = 0;
i < num_vars; ++
i) {
699 const IntegerVariable
var = vars[
i];
700 const IntegerValue coeff = coeffs[
i];
705 max_variations_[
i] = (ub - lb) * coeff;
706 sum_lb += lb * coeff;
710 static_cast<double>(num_vars) * 1e-9);
715 local_reason_.clear();
716 reason_coeffs_.clear();
717 for (
int i = 0;
i < num_vars; ++
i) {
718 const IntegerVariable
var = vars[
i];
721 reason_coeffs_.push_back(coeffs[
i]);
726 local_reason_.insert(local_reason_.end(),
727 integer_reason_for_unique_candidate_.begin(),
728 integer_reason_for_unique_candidate_.end());
734 for (
int i = 0;
i < num_vars; ++
i) {
735 if (max_variations_[
i] <= slack)
continue;
737 const IntegerVariable
var = vars[
i];
738 const IntegerValue coeff = coeffs[
i];
739 const IntegerValue div = slack / coeff;
740 const IntegerValue new_ub = integer_trail_->
LowerBound(
var) + div;
741 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
752 if (exprs_.empty())
return true;
756 const IntegerValue current_min_ub = integer_trail_->
UpperBound(min_var_);
757 int num_intervals_that_can_be_min = 0;
758 int last_possible_min_interval = 0;
762 for (
int i = 0;
i < exprs_.size(); ++
i) {
763 const IntegerValue lb = exprs_[
i].Min(*integer_trail_);
764 expr_lbs_.push_back(lb);
765 min_of_linear_expression_lb = std::min(min_of_linear_expression_lb, lb);
766 if (lb <= current_min_ub) {
767 ++num_intervals_that_can_be_min;
768 last_possible_min_interval =
i;
776 if (min_of_linear_expression_lb > current_min_ub) {
777 min_of_linear_expression_lb = current_min_ub + 1;
779 if (min_of_linear_expression_lb > integer_trail_->
LowerBound(min_var_)) {
780 local_reason_.clear();
781 for (
int i = 0;
i < exprs_.size(); ++
i) {
782 const IntegerValue slack = expr_lbs_[
i] - min_of_linear_expression_lb;
784 exprs_[
i].vars, &local_reason_);
787 min_var_, min_of_linear_expression_lb),
788 {}, local_reason_)) {
797 if (num_intervals_that_can_be_min == 1) {
798 const IntegerValue ub_of_only_candidate =
799 exprs_[last_possible_min_interval].Max(*integer_trail_);
800 if (current_min_ub < ub_of_only_candidate) {
803 if (rev_unique_candidate_ == 0) {
804 integer_reason_for_unique_candidate_.clear();
808 integer_reason_for_unique_candidate_.push_back(
810 for (
int i = 0;
i < exprs_.size(); ++
i) {
811 if (
i == last_possible_min_interval)
continue;
812 const IntegerValue slack = expr_lbs_[
i] - (current_min_ub + 1);
814 slack, exprs_[
i].coeffs, exprs_[
i].vars,
815 &integer_reason_for_unique_candidate_);
817 rev_unique_candidate_ = 1;
820 return PropagateLinearUpperBound(
821 last_possible_min_interval, exprs_[last_possible_min_interval].vars,
822 exprs_[last_possible_min_interval].coeffs,
823 current_min_ub - exprs_[last_possible_min_interval].offset);
831 const int id = watcher->
Register(
this);
833 for (
int i = 0;
i < expr.vars.size(); ++
i) {
834 const IntegerVariable&
var = expr.vars[
i];
835 const IntegerValue coeff = expr.coeffs[
i];
850 : a_(
a), b_(
b), p_(p), integer_trail_(integer_trail) {}
853bool ProductPropagator::CanonicalizeCases() {
867 p_.
GreaterOrEqual(0), {a_.GreaterOrEqual(0), b_.GreaterOrEqual(0)});
893bool ProductPropagator::PropagateWhenAllNonNegative() {
895 const IntegerValue max_a = integer_trail_->
UpperBound(a_);
896 const IntegerValue max_b = integer_trail_->
UpperBound(b_);
897 const IntegerValue new_max =
CapProdI(max_a, max_b);
898 if (new_max < integer_trail_->
UpperBound(p_)) {
901 {integer_trail_->UpperBoundAsLiteral(a_),
902 integer_trail_->UpperBoundAsLiteral(b_), a_.GreaterOrEqual(0),
903 b_.GreaterOrEqual(0)})) {
910 const IntegerValue min_a = integer_trail_->
LowerBound(a_);
911 const IntegerValue min_b = integer_trail_->
LowerBound(b_);
912 const IntegerValue new_min =
CapProdI(min_a, min_b);
916 if (new_min > integer_trail_->
UpperBound(p_)) {
922 if (new_min > integer_trail_->
LowerBound(p_)) {
925 {integer_trail_->LowerBoundAsLiteral(a_),
926 integer_trail_->LowerBoundAsLiteral(b_)})) {
932 for (
int i = 0;
i < 2; ++
i) {
933 const AffineExpression
a =
i == 0 ? a_ : b_;
934 const AffineExpression
b =
i == 0 ? b_ : a_;
935 const IntegerValue max_a = integer_trail_->
UpperBound(
a);
936 const IntegerValue min_b = integer_trail_->
LowerBound(
b);
937 const IntegerValue min_p = integer_trail_->
LowerBound(p_);
938 const IntegerValue max_p = integer_trail_->
UpperBound(p_);
939 const IntegerValue prod =
CapProdI(max_a, min_b);
947 }
else if (prod < min_p && max_a != 0) {
964bool ProductPropagator::PropagateMaxOnPositiveProduct(AffineExpression
a,
967 IntegerValue max_p) {
968 const IntegerValue max_a = integer_trail_->
UpperBound(
a);
969 if (max_a <= 0)
return true;
972 if (max_a >= min_p) {
975 a.LowerOrEqual(max_p),
976 {p_.LowerOrEqual(max_p), p_.GreaterOrEqual(1)})) {
983 const IntegerValue min_pos_b =
CeilRatio(min_p, max_a);
986 b.LowerOrEqual(0), {integer_trail_->LowerBoundAsLiteral(p_),
987 integer_trail_->UpperBoundAsLiteral(a),
988 integer_trail_->UpperBoundAsLiteral(b)})) {
994 const IntegerValue new_max_a =
FloorRatio(max_p, min_pos_b);
997 a.LowerOrEqual(new_max_a),
998 {integer_trail_->LowerBoundAsLiteral(p_),
999 integer_trail_->UpperBoundAsLiteral(a),
1000 integer_trail_->UpperBoundAsLiteral(p_)})) {
1008 if (!CanonicalizeCases())
return false;
1012 const int64_t min_a = integer_trail_->
LowerBound(a_).value();
1013 const int64_t min_b = integer_trail_->
LowerBound(b_).value();
1014 if (min_a >= 0 && min_b >= 0) {
1016 DCHECK_GE(integer_trail_->
LowerBound(p_), 0);
1017 return PropagateWhenAllNonNegative();
1026 const IntegerValue max_a = integer_trail_->
UpperBound(a_);
1027 const IntegerValue max_b = integer_trail_->
UpperBound(b_);
1028 const IntegerValue p1 =
CapProdI(max_a, max_b);
1029 const IntegerValue p2 =
CapProdI(max_a, min_b);
1030 const IntegerValue p3 =
CapProdI(min_a, max_b);
1031 const IntegerValue p4 =
CapProdI(min_a, min_b);
1032 const IntegerValue new_max_p = std::max({p1, p2, p3, p4});
1033 if (new_max_p < integer_trail_->
UpperBound(p_)) {
1036 {integer_trail_->LowerBoundAsLiteral(a_),
1037 integer_trail_->LowerBoundAsLiteral(b_),
1038 integer_trail_->UpperBoundAsLiteral(a_),
1039 integer_trail_->UpperBoundAsLiteral(b_)})) {
1043 const IntegerValue new_min_p = std::min({p1, p2, p3, p4});
1044 if (new_min_p > integer_trail_->
LowerBound(p_)) {
1047 {integer_trail_->LowerBoundAsLiteral(a_),
1048 integer_trail_->LowerBoundAsLiteral(b_),
1049 integer_trail_->UpperBoundAsLiteral(a_),
1050 integer_trail_->UpperBoundAsLiteral(b_)})) {
1056 const IntegerValue min_p = integer_trail_->
LowerBound(p_);
1057 const IntegerValue max_p = integer_trail_->
UpperBound(p_);
1060 const bool zero_is_possible = min_p <= 0;
1061 if (!zero_is_possible) {
1065 {p_.GreaterOrEqual(1), a_.GreaterOrEqual(0)})) {
1072 {p_.GreaterOrEqual(1), b_.GreaterOrEqual(0)})) {
1079 b_.
GreaterOrEqual(1), {a_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1084 a_.
GreaterOrEqual(1), {b_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1088 for (
int i = 0;
i < 2; ++
i) {
1092 const IntegerValue max_b = integer_trail_->
UpperBound(
b);
1093 const IntegerValue min_b = integer_trail_->
LowerBound(
b);
1097 if (zero_is_possible && min_b <= 0)
continue;
1100 if (min_b < 0 && max_b > 0) {
1105 if (!PropagateMaxOnPositiveProduct(
a,
b, min_p, max_p)) {
1108 if (!PropagateMaxOnPositiveProduct(
a.Negated(),
b.Negated(), min_p,
1117 if (min_b <= 0)
continue;
1120 a.GreaterOrEqual(0), {p_.GreaterOrEqual(0), b.GreaterOrEqual(1)});
1124 a.LowerOrEqual(0), {p_.LowerOrEqual(0), b.GreaterOrEqual(1)});
1128 const IntegerValue new_max_a =
FloorRatio(max_p, min_b);
1131 a.LowerOrEqual(new_max_a),
1132 {integer_trail_->UpperBoundAsLiteral(p_),
1133 integer_trail_->LowerBoundAsLiteral(b)})) {
1137 const IntegerValue new_min_a =
CeilRatio(min_p, min_b);
1140 a.GreaterOrEqual(new_min_a),
1141 {integer_trail_->LowerBoundAsLiteral(p_),
1142 integer_trail_->LowerBoundAsLiteral(b)})) {
1152 const int id = watcher->
Register(
this);
1161 : x_(
x), s_(s), integer_trail_(integer_trail) {
1168 const IntegerValue min_x = integer_trail_->
LowerBound(x_);
1169 const IntegerValue min_s = integer_trail_->
LowerBound(s_);
1170 const IntegerValue min_x_square =
CapProdI(min_x, min_x);
1171 if (min_x_square > min_s) {
1173 {x_.GreaterOrEqual(min_x)})) {
1176 }
else if (min_x_square < min_s) {
1180 {s_.GreaterOrEqual((new_min - 1) * (new_min - 1) + 1)})) {
1185 const IntegerValue max_x = integer_trail_->
UpperBound(x_);
1186 const IntegerValue max_s = integer_trail_->
UpperBound(s_);
1187 const IntegerValue max_x_square =
CapProdI(max_x, max_x);
1188 if (max_x_square < max_s) {
1190 {x_.LowerOrEqual(max_x)})) {
1193 }
else if (max_x_square > max_s) {
1197 {s_.LowerOrEqual(CapProdI(new_max + 1, new_max + 1) - 1)})) {
1206 const int id = watcher->
Register(
this);
1219 negated_denom_(denom.Negated()),
1220 negated_num_(num.Negated()),
1221 negated_div_(div.Negated()),
1222 integer_trail_(integer_trail) {}
1229 if (integer_trail_->
LowerBound(denom_) < 0 &&
1240 std::swap(num, negated_num);
1241 std::swap(denom, negated_denom);
1244 if (!PropagateSigns(num, denom, div_))
return false;
1248 !PropagateUpperBounds(num, denom, div_)) {
1252 if (integer_trail_->
UpperBound(negated_num) >= 0 &&
1253 integer_trail_->
UpperBound(negated_div_) >= 0 &&
1254 !PropagateUpperBounds(negated_num, denom, negated_div_)) {
1260 return PropagatePositiveDomains(num, denom, div_);
1263 if (integer_trail_->
LowerBound(negated_num) >= 0 &&
1264 integer_trail_->
LowerBound(negated_div_) >= 0) {
1265 return PropagatePositiveDomains(negated_num, denom, negated_div_);
1274 const IntegerValue min_num = integer_trail_->
LowerBound(num);
1275 const IntegerValue max_num = integer_trail_->
UpperBound(num);
1276 const IntegerValue min_div = integer_trail_->
LowerBound(div);
1277 const IntegerValue max_div = integer_trail_->
UpperBound(div);
1280 if (min_num >= 0 && min_div < 0) {
1283 {num.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1289 if (min_num <= 0 && min_div > 0) {
1292 {div.GreaterOrEqual(1), denom.GreaterOrEqual(1)})) {
1298 if (max_num <= 0 && max_div > 0) {
1301 {num.LowerOrEqual(0), denom.GreaterOrEqual(1)})) {
1307 if (max_num >= 0 && max_div < 0) {
1310 {div.LowerOrEqual(-1), denom.GreaterOrEqual(1)})) {
1318bool DivisionPropagator::PropagateUpperBounds(AffineExpression num,
1319 AffineExpression denom,
1320 AffineExpression div) {
1321 const IntegerValue max_num = integer_trail_->
UpperBound(num);
1322 const IntegerValue min_denom = integer_trail_->
LowerBound(denom);
1323 const IntegerValue max_denom = integer_trail_->
UpperBound(denom);
1324 const IntegerValue max_div = integer_trail_->
UpperBound(div);
1326 const IntegerValue new_max_div = max_num / min_denom;
1327 if (max_div > new_max_div) {
1329 div.LowerOrEqual(new_max_div),
1330 {num.LowerOrEqual(max_num), denom.GreaterOrEqual(min_denom)})) {
1338 const IntegerValue new_max_num =
1340 if (max_num > new_max_num) {
1342 num.LowerOrEqual(new_max_num),
1343 {denom.LowerOrEqual(max_denom), denom.GreaterOrEqual(1),
1344 div.LowerOrEqual(max_div)})) {
1352bool DivisionPropagator::PropagatePositiveDomains(AffineExpression num,
1353 AffineExpression denom,
1354 AffineExpression div) {
1355 const IntegerValue min_num = integer_trail_->
LowerBound(num);
1356 const IntegerValue max_num = integer_trail_->
UpperBound(num);
1357 const IntegerValue min_denom = integer_trail_->
LowerBound(denom);
1358 const IntegerValue max_denom = integer_trail_->
UpperBound(denom);
1359 const IntegerValue min_div = integer_trail_->
LowerBound(div);
1360 const IntegerValue max_div = integer_trail_->
UpperBound(div);
1362 const IntegerValue new_min_div = min_num / max_denom;
1363 if (min_div < new_min_div) {
1365 div.GreaterOrEqual(new_min_div),
1366 {num.GreaterOrEqual(min_num), denom.LowerOrEqual(max_denom),
1367 denom.GreaterOrEqual(1)})) {
1375 const IntegerValue new_min_num =
CapProdI(min_denom, min_div);
1376 if (min_num < new_min_num) {
1378 num.GreaterOrEqual(new_min_num),
1379 {denom.GreaterOrEqual(min_denom), div.GreaterOrEqual(min_div)})) {
1389 const IntegerValue new_max_denom = max_num / min_div;
1390 if (max_denom > new_max_denom) {
1392 denom.LowerOrEqual(new_max_denom),
1393 {num.LowerOrEqual(max_num), num.GreaterOrEqual(0),
1394 div.GreaterOrEqual(min_div), denom.GreaterOrEqual(1)})) {
1402 const IntegerValue new_min_denom =
CeilRatio(min_num + 1, max_div + 1);
1403 if (min_denom < new_min_denom) {
1405 denom.GreaterOrEqual(new_min_denom),
1406 {num.GreaterOrEqual(min_num), div.LowerOrEqual(max_div),
1407 div.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1416 const int id = watcher->
Register(
this);
1427 : a_(
a), b_(
b), c_(c), integer_trail_(integer_trail) {
1432 const IntegerValue min_a = integer_trail_->
LowerBound(a_);
1433 const IntegerValue max_a = integer_trail_->
UpperBound(a_);
1434 IntegerValue min_c = integer_trail_->
LowerBound(c_);
1435 IntegerValue max_c = integer_trail_->
UpperBound(c_);
1437 if (max_a / b_ < max_c) {
1441 {integer_trail_->UpperBoundAsLiteral(a_)})) {
1444 }
else if (max_a / b_ > max_c) {
1445 const IntegerValue new_max_a =
1446 max_c >= 0 ? max_c * b_ + b_ - 1 :
CapProdI(max_c, b_);
1447 CHECK_LT(new_max_a, max_a);
1450 {integer_trail_->UpperBoundAsLiteral(c_)})) {
1455 if (min_a / b_ > min_c) {
1459 {integer_trail_->LowerBoundAsLiteral(a_)})) {
1462 }
else if (min_a / b_ < min_c) {
1463 const IntegerValue new_min_a =
1464 min_c > 0 ?
CapProdI(min_c, b_) : min_c * b_ - b_ + 1;
1465 CHECK_GT(new_min_a, min_a);
1468 {integer_trail_->LowerBoundAsLiteral(c_)})) {
1477 const int id = watcher->
Register(
this);
1486 :
expr_(expr), mod_(mod), target_(target), integer_trail_(integer_trail) {
1491 if (!PropagateSignsAndTargetRange())
return false;
1492 if (!PropagateOuterBounds())
return false;
1494 if (integer_trail_->
LowerBound(expr_) >= 0) {
1495 if (!PropagateBoundsWhenExprIsPositive(expr_, target_))
return false;
1496 }
else if (integer_trail_->
UpperBound(expr_) <= 0) {
1497 if (!PropagateBoundsWhenExprIsPositive(expr_.
Negated(),
1506bool FixedModuloPropagator::PropagateSignsAndTargetRange() {
1508 if (integer_trail_->
UpperBound(target_) >= mod_) {
1514 if (integer_trail_->
LowerBound(target_) <= -mod_) {
1521 if (integer_trail_->
LowerBound(expr_) >= 0 &&
1524 {expr_.GreaterOrEqual(0)})) {
1529 if (integer_trail_->
UpperBound(expr_) <= 0 &&
1532 {expr_.LowerOrEqual(0)})) {
1540bool FixedModuloPropagator::PropagateOuterBounds() {
1541 const IntegerValue min_expr = integer_trail_->
LowerBound(expr_);
1542 const IntegerValue max_expr = integer_trail_->
UpperBound(expr_);
1543 const IntegerValue min_target = integer_trail_->
LowerBound(target_);
1544 const IntegerValue max_target = integer_trail_->
UpperBound(target_);
1546 if (max_expr % mod_ > max_target) {
1548 expr_.
LowerOrEqual((max_expr / mod_) * mod_ + max_target),
1549 {integer_trail_->UpperBoundAsLiteral(target_),
1550 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1555 if (min_expr % mod_ < min_target) {
1558 {integer_trail_->LowerBoundAsLiteral(expr_),
1559 integer_trail_->LowerBoundAsLiteral(target_)})) {
1564 if (min_expr / mod_ == max_expr / mod_) {
1565 if (min_target < min_expr % mod_) {
1568 {integer_trail_->LowerBoundAsLiteral(target_),
1569 integer_trail_->UpperBoundAsLiteral(target_),
1570 integer_trail_->LowerBoundAsLiteral(expr_),
1571 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1576 if (max_target > max_expr % mod_) {
1578 target_.
LowerOrEqual(max_expr - (max_expr / mod_) * mod_),
1579 {integer_trail_->LowerBoundAsLiteral(target_),
1580 integer_trail_->UpperBoundAsLiteral(target_),
1581 integer_trail_->LowerBoundAsLiteral(expr_),
1582 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1586 }
else if (min_expr / mod_ == 0 && min_target < 0) {
1588 if (min_target < min_expr) {
1591 {integer_trail_->LowerBoundAsLiteral(target_),
1592 integer_trail_->LowerBoundAsLiteral(expr_)})) {
1596 }
else if (max_expr / mod_ == 0 && max_target > 0) {
1598 if (max_target > max_expr) {
1601 {integer_trail_->UpperBoundAsLiteral(target_),
1602 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1611bool FixedModuloPropagator::PropagateBoundsWhenExprIsPositive(
1612 AffineExpression expr, AffineExpression target) {
1613 const IntegerValue min_target = integer_trail_->
LowerBound(target);
1614 DCHECK_GE(min_target, 0);
1615 const IntegerValue max_target = integer_trail_->
UpperBound(target);
1619 if (min_target == 0 && max_target == mod_ - 1)
return true;
1621 const IntegerValue min_expr = integer_trail_->
LowerBound(expr);
1622 const IntegerValue max_expr = integer_trail_->
UpperBound(expr);
1624 if (max_expr % mod_ < min_target) {
1625 DCHECK_GE(max_expr, 0);
1627 expr.LowerOrEqual((max_expr / mod_ - 1) * mod_ + max_target),
1628 {integer_trail_->UpperBoundAsLiteral(expr),
1629 integer_trail_->LowerBoundAsLiteral(target),
1630 integer_trail_->UpperBoundAsLiteral(target)})) {
1635 if (min_expr % mod_ > max_target) {
1636 DCHECK_GE(min_expr, 0);
1638 expr.GreaterOrEqual((min_expr / mod_ + 1) * mod_ + min_target),
1639 {integer_trail_->LowerBoundAsLiteral(target),
1640 integer_trail_->UpperBoundAsLiteral(target),
1641 integer_trail_->LowerBoundAsLiteral(expr)})) {
1650 const int id = watcher->
Register(
this);
1657 const std::vector<Literal>& selectors,
1658 const std::vector<IntegerValue>& values) {
1663 CHECK(!values.empty());
1664 CHECK_EQ(values.size(), selectors.size());
1665 std::vector<int64_t> unique_values;
1666 absl::flat_hash_map<int64_t, std::vector<Literal>> value_to_selector;
1667 for (
int i = 0;
i < values.size(); ++
i) {
1668 unique_values.push_back(values[
i].
value());
1669 value_to_selector[values[
i].value()].push_back(selectors[
i]);
1674 if (unique_values.size() == 1) {
1681 for (
const int64_t v : unique_values) {
1682 const std::vector<Literal>& selectors = value_to_selector[v];
1683 if (selectors.size() == 1) {
1684 encoder->AssociateToIntegerEqualValue(selectors[0],
var,
1689 encoder->AssociateToIntegerEqualValue(l,
var, IntegerValue(v));
const std::vector< IntVar * > vars_
static Domain FromValues(std::vector< int64_t > values)
static int64_t GCD64(int64_t x, int64_t y)
void AdvanceDeterministicTime(double deterministic_duration)
void RegisterWith(GenericLiteralWatcher *watcher)
DivisionPropagator(AffineExpression num, AffineExpression denom, AffineExpression div, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
FixedDivisionPropagator(AffineExpression a, IntegerValue b, AffineExpression c, IntegerTrail *integer_trail)
FixedModuloPropagator(AffineExpression expr, IntegerValue mod, AffineExpression target, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
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)
Registers a propagator and returns its unique ids.
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
bool VariableLowerBoundIsFromLevelZero(IntegerVariable var) const
Returns true if the variable lower bound is still the one from level zero.
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
Same as above but take in IntegerVariables instead of IntegerLiterals.
ABSL_MUST_USE_RESULT bool EnqueueWithLazyReason(IntegerLiteral i_lit, int id, IntegerValue propagation_slack, LazyReasonInterface *explainer)
Lazy reason API.
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
bool IsFixed(IntegerVariable i) const
Checks if the variable is fixed.
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
LevelZeroEquality(IntegerVariable target, const std::vector< IntegerVariable > &vars, const std::vector< IntegerValue > &coeffs, Model *model)
LinMinPropagator(const std::vector< LinearExpression > &exprs, IntegerVariable min_var, Model *model)
void RegisterWith(GenericLiteralWatcher *watcher)
void Explain(int id, IntegerValue propagation_slack, IntegerVariable var_to_explain, int trail_index, std::vector< Literal > *literals_reason, std::vector< int > *trail_indices_reason) final
For LazyReasonInterface.
std::pair< IntegerValue, IntegerValue > ConditionalLb(IntegerLiteral integer_literal, IntegerVariable target_var) const
NOTE(user): This is only used with int128, so we code only a single version.
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)
void Explain(int id, IntegerValue propagation_slack, IntegerVariable var_to_explain, int trail_index, std::vector< Literal > *literals_reason, std::vector< int > *trail_indices_reason) final
For LazyReasonInterface.
bool PropagateAtLevelZero()
void RegisterWith(GenericLiteralWatcher *watcher)
MinPropagator(const std::vector< IntegerVariable > &vars, IntegerVariable min_var, IntegerTrail *integer_trail)
ProductPropagator(AffineExpression a, AffineExpression b, AffineExpression p, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
SquarePropagator(AffineExpression x, AffineExpression s, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
int CurrentDecisionLevel() const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::function< void(Model *)> IsOneOf(IntegerVariable var, const std::vector< Literal > &selectors, const std::vector< IntegerValue > &values)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> ReifiedBoolOr(const std::vector< Literal > &literals, Literal r)
r <=> (at least one literal is true). This is a reified clause.
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
int64_t FloorSquareRoot(int64_t a)
The argument must be non-negative.
std::function< BooleanVariable(Model *)> NewBooleanVariable()
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
int64_t CeilSquareRoot(int64_t a)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
IntegerVariable PositiveVariable(IntegerVariable i)
IntegerValue PositiveRemainder(IntegerValue dividend, IntegerValue positive_divisor)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
Overflows and saturated arithmetic.
In SWIG mode, we don't want anything besides these top-level includes.
static int input(yyscan_t yyscanner)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
var * coeff + constant >= bound.
AffineExpression Negated() const
IntegerLiteral LowerOrEqual(IntegerValue bound) const
var * coeff + constant <= bound.
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerLiteral Negated() const
The negation of x >= bound is x <= bound - 1.
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
ModelSharedTimeLimit *const time_limit