23 #include "absl/container/flat_hash_map.h"
24 #include "absl/types/span.h"
44 const std::vector<IntegerVariable>& vars,
45 const std::vector<IntegerValue>& coeffs,
47 : enforcement_literals_(enforcement_literals),
52 rev_integer_value_repository_(
57 CHECK(!vars_.empty());
58 max_variations_.resize(vars_.size());
61 for (
int i = 0; i < vars.size(); ++i) {
64 coeffs_[i] = -coeffs_[i];
70 literal_reason_.push_back(
literal.Negated());
74 rev_num_fixed_vars_ = 0;
75 rev_lb_fixed_vars_ = IntegerValue(0);
78 void IntegerSumLE::FillIntegerReason() {
79 integer_reason_.clear();
80 reason_coeffs_.clear();
81 const int num_vars = vars_.size();
82 for (
int i = 0; i < num_vars; ++i) {
83 const IntegerVariable
var = vars_[i];
86 reason_coeffs_.push_back(coeffs_[i]);
92 IntegerLiteral integer_literal, IntegerVariable target_var)
const {
96 if (integer_literal.
var == target_var) {
104 bool literal_var_present =
false;
105 bool literal_var_present_positively =
false;
106 IntegerValue var_coeff;
108 bool target_var_present_negatively =
false;
109 IntegerValue target_coeff;
114 IntegerValue implied_lb(0);
115 for (
int i = 0; i < vars_.size(); ++i) {
116 const IntegerVariable
var = vars_[i];
117 const IntegerValue coeff = coeffs_[i];
119 target_coeff = coeff;
120 target_var_present_negatively =
true;
124 implied_lb += coeff * lb;
127 literal_var_present =
true;
128 literal_var_present_positively = (
var == integer_literal.
var);
132 if (!literal_var_present || !target_var_present_negatively) {
138 const IntegerValue slack = upper_bound_ - implied_lb;
139 const IntegerValue target_lb = integer_trail_->
LowerBound(target_var);
140 const IntegerValue target_ub = integer_trail_->
UpperBound(target_var);
144 return {target_ub, target_ub};
147 const IntegerValue target_diff = target_ub - target_lb;
148 const IntegerValue
delta =
std::min(slack / target_coeff, target_diff);
151 if (literal_var_present_positively) {
156 IntegerValue(0), integer_literal.
bound -
158 const IntegerValue tighter_slack =
159 std::max(IntegerValue(0), slack - var_coeff * diff);
160 const IntegerValue tighter_delta =
161 std::min(tighter_slack / target_coeff, target_diff);
162 return {target_ub -
delta, target_ub - tighter_delta};
168 IntegerValue(0), integer_trail_->
UpperBound(integer_literal.
var) -
169 integer_literal.
bound + 1);
170 const IntegerValue tighter_slack =
171 std::max(IntegerValue(0), slack - var_coeff * diff);
172 const IntegerValue tighter_delta =
173 std::min(tighter_slack / target_coeff, target_diff);
174 return {target_ub - tighter_delta, target_ub -
delta};
181 int num_unassigned_enforcement_literal = 0;
186 ++num_unassigned_enforcement_literal;
187 unique_unnasigned_literal =
literal.Index();
193 if (num_unassigned_enforcement_literal > 1)
return true;
196 if (is_registered_) {
197 rev_integer_value_repository_->
SaveState(&rev_lb_fixed_vars_);
199 rev_num_fixed_vars_ = 0;
200 rev_lb_fixed_vars_ = 0;
204 IntegerValue lb_unfixed_vars = IntegerValue(0);
205 const int num_vars = vars_.size();
206 for (
int i = rev_num_fixed_vars_; i < num_vars; ++i) {
207 const IntegerVariable
var = vars_[i];
208 const IntegerValue coeff = coeffs_[i];
212 max_variations_[i] = (ub - lb) * coeff;
213 lb_unfixed_vars += lb * coeff;
216 std::swap(vars_[i], vars_[rev_num_fixed_vars_]);
217 std::swap(coeffs_[i], coeffs_[rev_num_fixed_vars_]);
218 std::swap(max_variations_[i], max_variations_[rev_num_fixed_vars_]);
219 rev_num_fixed_vars_++;
220 rev_lb_fixed_vars_ += lb * coeff;
224 static_cast<double>(num_vars - rev_num_fixed_vars_) * 1e-9);
227 const IntegerValue slack =
228 upper_bound_ - (rev_lb_fixed_vars_ + lb_unfixed_vars);
234 if (num_unassigned_enforcement_literal == 1) {
237 std::vector<Literal> tmp = literal_reason_;
238 tmp.erase(std::find(tmp.begin(), tmp.end(), to_propagate));
239 integer_trail_->
EnqueueLiteral(to_propagate, tmp, integer_reason_);
242 return integer_trail_->
ReportConflict(literal_reason_, integer_reason_);
246 if (num_unassigned_enforcement_literal > 0)
return true;
250 for (
int i = rev_num_fixed_vars_; i < num_vars; ++i) {
251 if (max_variations_[i] <= slack)
continue;
255 const IntegerVariable
var = vars_[i];
256 const IntegerValue coeff = coeffs_[i];
257 const IntegerValue div = slack / coeff;
258 const IntegerValue new_ub = integer_trail_->
LowerBound(
var) + div;
259 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
262 [
this, propagation_slack](
264 std::vector<Literal>* literal_reason,
265 std::vector<int>* trail_indices_reason) {
266 *literal_reason = literal_reason_;
267 trail_indices_reason->clear();
268 reason_coeffs_.clear();
269 const int size = vars_.size();
270 for (int i = 0; i < size; ++i) {
271 const IntegerVariable var = vars_[i];
272 if (PositiveVariable(var) == PositiveVariable(i_lit.var)) {
276 integer_trail_->FindTrailIndexOfVarBefore(var, trail_index);
278 trail_indices_reason->push_back(index);
279 if (propagation_slack > 0) {
280 reason_coeffs_.push_back(coeffs_[i]);
284 if (propagation_slack > 0) {
286 propagation_slack, reason_coeffs_, trail_indices_reason);
296 bool IntegerSumLE::PropagateAtLevelZero() {
299 if (!enforcement_literals_.empty())
return true;
302 IntegerValue min_activity = IntegerValue(0);
303 const int num_vars =
vars_.size();
304 for (
int i = 0; i < num_vars; ++i) {
305 const IntegerVariable
var =
vars_[i];
306 const IntegerValue coeff = coeffs_[i];
307 const IntegerValue lb = integer_trail_->LevelZeroLowerBound(
var);
308 const IntegerValue ub = integer_trail_->LevelZeroUpperBound(
var);
309 max_variations_[i] = (ub - lb) * coeff;
310 min_activity += lb * coeff;
312 time_limit_->AdvanceDeterministicTime(
static_cast<double>(num_vars * 1e-9));
315 const IntegerValue slack = upper_bound_ - min_activity;
317 return integer_trail_->ReportConflict({}, {});
322 for (
int i = 0; i < num_vars; ++i) {
323 if (max_variations_[i] <= slack)
continue;
325 const IntegerVariable
var =
vars_[i];
326 const IntegerValue coeff = coeffs_[i];
327 const IntegerValue div = slack / coeff;
328 const IntegerValue new_ub = integer_trail_->LevelZeroLowerBound(
var) + div;
339 is_registered_ =
true;
340 const int id = watcher->
Register(
this);
341 for (
const IntegerVariable&
var :
vars_) {
354 LevelZeroEquality::LevelZeroEquality(IntegerVariable target,
355 const std::vector<IntegerVariable>& vars,
356 const std::vector<IntegerValue>& coeffs,
364 const int id = watcher->
Register(
this);
365 watcher->SetPropagatorPriority(
id, 2);
366 watcher->WatchIntegerVariable(target,
id);
367 for (
const IntegerVariable&
var : vars_) {
368 watcher->WatchIntegerVariable(
var,
id);
386 for (
int i = 0; i < vars_.size(); ++i) {
387 if (integer_trail_->
IsFixed(vars_[i])) {
388 sum += coeffs_[i] * integer_trail_->
LowerBound(vars_[i]);
394 if (gcd == 0)
return true;
397 VLOG(1) <<
"Objective gcd: " << gcd;
400 gcd_ = IntegerValue(gcd);
402 const IntegerValue lb = integer_trail_->
LowerBound(target_);
404 if (lb_remainder != 0) {
411 const IntegerValue ub = integer_trail_->
UpperBound(target_);
412 const IntegerValue ub_remainder =
414 if (ub_remainder != 0) {
424 IntegerVariable min_var,
426 :
vars_(vars), min_var_(min_var), integer_trail_(integer_trail) {}
429 if (vars_.empty())
return true;
435 const IntegerValue current_min_ub = integer_trail_->
UpperBound(min_var_);
436 int num_intervals_that_can_be_min = 0;
437 int last_possible_min_interval = 0;
440 for (
int i = 0; i < vars_.size(); ++i) {
441 const IntegerValue lb = integer_trail_->
LowerBound(vars_[i]);
443 if (lb <= current_min_ub) {
444 ++num_intervals_that_can_be_min;
445 last_possible_min_interval = i;
451 integer_reason_.clear();
452 for (
const IntegerVariable
var : vars_) {
456 {}, integer_reason_)) {
462 if (num_intervals_that_can_be_min == 1) {
463 const IntegerValue ub_of_only_candidate =
464 integer_trail_->
UpperBound(vars_[last_possible_min_interval]);
465 if (current_min_ub < ub_of_only_candidate) {
466 integer_reason_.clear();
470 integer_reason_.push_back(min_ub_literal);
471 for (
const IntegerVariable
var : vars_) {
472 if (
var ==
vars_[last_possible_min_interval])
continue;
473 integer_reason_.push_back(
479 {}, integer_reason_)) {
491 if (num_intervals_that_can_be_min == 0) {
492 integer_reason_.clear();
495 integer_reason_.push_back(min_ub_literal);
496 for (
const IntegerVariable
var : vars_) {
497 integer_reason_.push_back(
507 const int id = watcher->
Register(
this);
508 for (
const IntegerVariable&
var : vars_) {
521 bool LinMinPropagator::PropagateLinearUpperBound(
522 const std::vector<IntegerVariable>& vars,
523 const std::vector<IntegerValue>& coeffs,
const IntegerValue
upper_bound) {
524 IntegerValue sum_lb = IntegerValue(0);
525 const int num_vars = vars.size();
526 max_variations_.resize(num_vars);
527 for (
int i = 0; i < num_vars; ++i) {
528 const IntegerVariable
var = vars[i];
529 const IntegerValue coeff = coeffs[i];
534 max_variations_[i] = (ub - lb) * coeff;
535 sum_lb += lb * coeff;
539 static_cast<double>(num_vars) * 1e-9);
544 local_reason_.clear();
545 reason_coeffs_.clear();
546 for (
int i = 0; i < num_vars; ++i) {
547 const IntegerVariable
var = vars[i];
550 reason_coeffs_.push_back(coeffs[i]);
555 local_reason_.insert(local_reason_.end(),
556 integer_reason_for_unique_candidate_.begin(),
557 integer_reason_for_unique_candidate_.end());
563 for (
int i = 0; i < num_vars; ++i) {
564 if (max_variations_[i] <= slack)
continue;
566 const IntegerVariable
var = vars[i];
567 const IntegerValue coeff = coeffs[i];
568 const IntegerValue div = slack / coeff;
569 const IntegerValue new_ub = integer_trail_->
LowerBound(
var) + div;
571 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
574 [
this, &vars, &coeffs, propagation_slack](
575 IntegerLiteral i_lit,
int trail_index,
576 std::vector<Literal>* literal_reason,
577 std::vector<int>* trail_indices_reason) {
578 literal_reason->clear();
579 trail_indices_reason->clear();
580 std::vector<IntegerValue> reason_coeffs;
581 const int size = vars.size();
582 for (
int i = 0; i < size; ++i) {
583 const IntegerVariable
var = vars[i];
590 trail_indices_reason->push_back(
index);
591 if (propagation_slack > 0) {
592 reason_coeffs.push_back(coeffs[i]);
596 if (propagation_slack > 0) {
598 propagation_slack, reason_coeffs, trail_indices_reason);
601 for (IntegerLiteral reason_lit :
602 integer_reason_for_unique_candidate_) {
604 reason_lit.var, trail_index);
606 trail_indices_reason->push_back(
index);
617 if (exprs_.empty())
return true;
621 const IntegerValue current_min_ub = integer_trail_->
UpperBound(min_var_);
622 int num_intervals_that_can_be_min = 0;
623 int last_possible_min_interval = 0;
627 for (
int i = 0; i < exprs_.size(); ++i) {
628 const IntegerValue lb = exprs_[i].Min(*integer_trail_);
629 expr_lbs_.push_back(lb);
630 min_of_linear_expression_lb =
std::min(min_of_linear_expression_lb, lb);
631 if (lb <= current_min_ub) {
632 ++num_intervals_that_can_be_min;
633 last_possible_min_interval = i;
641 if (min_of_linear_expression_lb > current_min_ub) {
642 min_of_linear_expression_lb = current_min_ub + 1;
644 if (min_of_linear_expression_lb > integer_trail_->
LowerBound(min_var_)) {
645 local_reason_.clear();
646 for (
int i = 0; i < exprs_.size(); ++i) {
647 const IntegerValue slack = expr_lbs_[i] - min_of_linear_expression_lb;
649 exprs_[i].vars, &local_reason_);
652 min_var_, min_of_linear_expression_lb),
653 {}, local_reason_)) {
662 if (num_intervals_that_can_be_min == 1) {
663 const IntegerValue ub_of_only_candidate =
664 exprs_[last_possible_min_interval].Max(*integer_trail_);
665 if (current_min_ub < ub_of_only_candidate) {
668 if (rev_unique_candidate_ == 0) {
669 integer_reason_for_unique_candidate_.clear();
673 integer_reason_for_unique_candidate_.push_back(
675 for (
int i = 0; i < exprs_.size(); ++i) {
676 if (i == last_possible_min_interval)
continue;
677 const IntegerValue slack = expr_lbs_[i] - (current_min_ub + 1);
679 slack, exprs_[i].coeffs, exprs_[i].vars,
680 &integer_reason_for_unique_candidate_);
682 rev_unique_candidate_ = 1;
685 return PropagateLinearUpperBound(
686 exprs_[last_possible_min_interval].vars,
687 exprs_[last_possible_min_interval].coeffs,
688 current_min_ub - exprs_[last_possible_min_interval].offset);
696 const int id = watcher->
Register(
this);
698 for (
int i = 0; i < expr.vars.size(); ++i) {
699 const IntegerVariable&
var = expr.vars[i];
700 const IntegerValue coeff = expr.coeffs[i];
715 : a_(
a), b_(
b), p_(p), integer_trail_(integer_trail) {}
718 bool ProductPropagator::CanonicalizeCases() {
732 p_.
GreaterOrEqual(0), {a_.GreaterOrEqual(0), b_.GreaterOrEqual(0)});
758 bool ProductPropagator::PropagateWhenAllNonNegative() {
760 const IntegerValue max_a = integer_trail_->
UpperBound(a_);
761 const IntegerValue max_b = integer_trail_->
UpperBound(b_);
762 const IntegerValue new_max(
CapProd(max_a.value(), max_b.value()));
763 if (new_max < integer_trail_->
UpperBound(p_)) {
766 {integer_trail_->UpperBoundAsLiteral(a_),
767 integer_trail_->UpperBoundAsLiteral(b_), a_.GreaterOrEqual(0),
768 b_.GreaterOrEqual(0)})) {
775 const IntegerValue min_a = integer_trail_->
LowerBound(a_);
776 const IntegerValue min_b = integer_trail_->
LowerBound(b_);
777 const IntegerValue new_min(
CapProd(min_a.value(), min_b.value()));
781 if (new_min > integer_trail_->
UpperBound(p_)) {
787 if (new_min > integer_trail_->
LowerBound(p_)) {
790 {integer_trail_->LowerBoundAsLiteral(a_),
791 integer_trail_->LowerBoundAsLiteral(b_)})) {
797 for (
int i = 0; i < 2; ++i) {
798 const AffineExpression
a = i == 0 ? a_ : b_;
799 const AffineExpression
b = i == 0 ? b_ : a_;
800 const IntegerValue max_a = integer_trail_->
UpperBound(
a);
801 const IntegerValue min_b = integer_trail_->
LowerBound(
b);
802 const IntegerValue min_p = integer_trail_->
LowerBound(p_);
803 const IntegerValue max_p = integer_trail_->
UpperBound(p_);
804 const IntegerValue prod(
CapProd(max_a.value(), min_b.value()));
812 }
else if (prod < min_p && max_a != 0) {
829 bool ProductPropagator::PropagateMaxOnPositiveProduct(AffineExpression
a,
832 IntegerValue max_p) {
833 const IntegerValue max_a = integer_trail_->
UpperBound(
a);
834 if (max_a <= 0)
return true;
837 if (max_a >= min_p) {
840 a.LowerOrEqual(max_p),
841 {p_.LowerOrEqual(max_p), p_.GreaterOrEqual(1)})) {
848 const IntegerValue min_pos_b =
CeilRatio(min_p, max_a);
851 b.LowerOrEqual(0), {integer_trail_->LowerBoundAsLiteral(p_),
852 integer_trail_->UpperBoundAsLiteral(a),
853 integer_trail_->UpperBoundAsLiteral(b)})) {
859 const IntegerValue new_max_a =
FloorRatio(max_p, min_pos_b);
862 a.LowerOrEqual(new_max_a),
863 {integer_trail_->LowerBoundAsLiteral(p_),
864 integer_trail_->UpperBoundAsLiteral(a),
865 integer_trail_->UpperBoundAsLiteral(p_)})) {
873 if (!CanonicalizeCases())
return false;
877 const int64_t min_a = integer_trail_->
LowerBound(a_).value();
878 const int64_t min_b = integer_trail_->
LowerBound(b_).value();
879 if (min_a >= 0 && min_b >= 0) {
882 return PropagateWhenAllNonNegative();
891 const int64_t max_a = integer_trail_->
UpperBound(a_).value();
892 const int64_t max_b = integer_trail_->
UpperBound(b_).value();
893 const IntegerValue p1(
CapProd(max_a, max_b));
894 const IntegerValue p2(
CapProd(max_a, min_b));
895 const IntegerValue p3(
CapProd(min_a, max_b));
896 const IntegerValue p4(
CapProd(min_a, min_b));
897 const IntegerValue new_max_p =
std::max({p1, p2, p3, p4});
898 if (new_max_p < integer_trail_->
UpperBound(p_)) {
901 {integer_trail_->LowerBoundAsLiteral(a_),
902 integer_trail_->LowerBoundAsLiteral(b_),
903 integer_trail_->UpperBoundAsLiteral(a_),
904 integer_trail_->UpperBoundAsLiteral(b_)})) {
908 const IntegerValue new_min_p =
std::min({p1, p2, p3, p4});
909 if (new_min_p > integer_trail_->
LowerBound(p_)) {
912 {integer_trail_->LowerBoundAsLiteral(a_),
913 integer_trail_->LowerBoundAsLiteral(b_),
914 integer_trail_->UpperBoundAsLiteral(a_),
915 integer_trail_->UpperBoundAsLiteral(b_)})) {
921 const IntegerValue min_p = integer_trail_->
LowerBound(p_);
922 const IntegerValue max_p = integer_trail_->
UpperBound(p_);
925 const bool zero_is_possible = min_p <= 0;
926 if (!zero_is_possible) {
930 {p_.GreaterOrEqual(1), a_.GreaterOrEqual(0)})) {
937 {p_.GreaterOrEqual(1), b_.GreaterOrEqual(0)})) {
944 b_.
GreaterOrEqual(1), {a_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
949 a_.
GreaterOrEqual(1), {b_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
953 for (
int i = 0; i < 2; ++i) {
957 const IntegerValue max_b = integer_trail_->
UpperBound(
b);
958 const IntegerValue min_b = integer_trail_->
LowerBound(
b);
962 if (zero_is_possible && min_b <= 0)
continue;
965 if (min_b < 0 && max_b > 0) {
970 if (!PropagateMaxOnPositiveProduct(
a,
b, min_p, max_p)) {
973 if (!PropagateMaxOnPositiveProduct(
a.Negated(),
b.Negated(), min_p,
982 if (min_b <= 0)
continue;
985 a.GreaterOrEqual(0), {p_.GreaterOrEqual(0), b.GreaterOrEqual(1)});
989 a.LowerOrEqual(0), {p_.LowerOrEqual(0), b.GreaterOrEqual(1)});
993 const IntegerValue new_max_a =
FloorRatio(max_p, min_b);
996 a.LowerOrEqual(new_max_a),
997 {integer_trail_->UpperBoundAsLiteral(p_),
998 integer_trail_->LowerBoundAsLiteral(b)})) {
1002 const IntegerValue new_min_a =
CeilRatio(min_p, min_b);
1005 a.GreaterOrEqual(new_min_a),
1006 {integer_trail_->LowerBoundAsLiteral(p_),
1007 integer_trail_->LowerBoundAsLiteral(b)})) {
1017 const int id = watcher->
Register(
this);
1026 : x_(x), s_(s), integer_trail_(integer_trail) {
1033 const IntegerValue min_x = integer_trail_->
LowerBound(x_);
1034 const IntegerValue min_s = integer_trail_->
LowerBound(s_);
1035 const IntegerValue min_x_square(
CapProd(min_x.value(), min_x.value()));
1036 if (min_x_square > min_s) {
1038 {x_.GreaterOrEqual(min_x)})) {
1041 }
else if (min_x_square < min_s) {
1045 {s_.GreaterOrEqual((new_min - 1) * (new_min - 1) + 1)})) {
1050 const IntegerValue max_x = integer_trail_->
UpperBound(x_);
1051 const IntegerValue max_s = integer_trail_->
UpperBound(s_);
1052 const IntegerValue max_x_square(
CapProd(max_x.value(), max_x.value()));
1053 if (max_x_square < max_s) {
1055 {x_.LowerOrEqual(max_x)})) {
1058 }
else if (max_x_square > max_s) {
1062 {s_.LowerOrEqual(IntegerValue(CapProd(new_max.value() + 1,
1063 new_max.value() + 1)) -
1073 const int id = watcher->
Register(
this);
1086 negated_num_(num.Negated()),
1087 negated_div_(div.Negated()),
1088 integer_trail_(integer_trail) {
1094 if (!PropagateSigns())
return false;
1098 !PropagateUpperBounds(num_, denom_, div_)) {
1102 if (integer_trail_->
UpperBound(negated_num_) >= 0 &&
1103 integer_trail_->
UpperBound(negated_div_) >= 0 &&
1104 !PropagateUpperBounds(negated_num_, denom_, negated_div_)) {
1110 return PropagatePositiveDomains(num_, denom_, div_);
1115 return PropagatePositiveDomains(negated_num_, denom_, negated_div_);
1121 bool DivisionPropagator::PropagateSigns() {
1122 const IntegerValue min_num = integer_trail_->
LowerBound(num_);
1123 const IntegerValue max_num = integer_trail_->
UpperBound(num_);
1124 const IntegerValue min_div = integer_trail_->
LowerBound(div_);
1125 const IntegerValue max_div = integer_trail_->
UpperBound(div_);
1128 if (min_num >= 0 && min_div < 0) {
1130 {num_.GreaterOrEqual(0)})) {
1136 if (min_num <= 0 && min_div > 0) {
1138 {div_.GreaterOrEqual(1)})) {
1144 if (max_num <= 0 && max_div > 0) {
1146 {num_.LowerOrEqual(0)})) {
1152 if (max_num >= 0 && max_div < 0) {
1154 {div_.LowerOrEqual(-1)})) {
1162 bool DivisionPropagator::PropagateUpperBounds(AffineExpression num,
1163 AffineExpression denom,
1164 AffineExpression div) {
1165 const IntegerValue max_num = integer_trail_->
UpperBound(num);
1166 const IntegerValue min_denom = integer_trail_->
LowerBound(denom);
1167 const IntegerValue max_denom = integer_trail_->
UpperBound(denom);
1168 const IntegerValue max_div = integer_trail_->
UpperBound(div);
1170 const IntegerValue new_max_div = max_num / min_denom;
1171 if (max_div > new_max_div) {
1173 div.LowerOrEqual(new_max_div),
1174 {integer_trail_->UpperBoundAsLiteral(num),
1175 integer_trail_->LowerBoundAsLiteral(denom)})) {
1183 const IntegerValue new_max_num =
1184 IntegerValue(
CapAdd(
CapProd(max_div.value() + 1, max_denom.value()), -1));
1185 if (max_num > new_max_num) {
1187 num.LowerOrEqual(new_max_num),
1188 {integer_trail_->UpperBoundAsLiteral(denom),
1189 integer_trail_->UpperBoundAsLiteral(div)})) {
1197 bool DivisionPropagator::PropagatePositiveDomains(AffineExpression num,
1198 AffineExpression denom,
1199 AffineExpression div) {
1200 const IntegerValue min_num = integer_trail_->
LowerBound(num);
1201 const IntegerValue max_num = integer_trail_->
UpperBound(num);
1202 const IntegerValue min_denom = integer_trail_->
LowerBound(denom);
1203 const IntegerValue max_denom = integer_trail_->
UpperBound(denom);
1204 const IntegerValue min_div = integer_trail_->
LowerBound(div);
1205 const IntegerValue max_div = integer_trail_->
UpperBound(div);
1207 const IntegerValue new_min_div = min_num / max_denom;
1208 if (min_div < new_min_div) {
1210 div.GreaterOrEqual(new_min_div),
1211 {integer_trail_->LowerBoundAsLiteral(num),
1212 integer_trail_->UpperBoundAsLiteral(denom)})) {
1220 const IntegerValue new_min_num =
1221 IntegerValue(
CapProd(min_denom.value(), min_div.value()));
1222 if (min_num < new_min_num) {
1224 num.GreaterOrEqual(new_min_num),
1225 {integer_trail_->LowerBoundAsLiteral(denom),
1226 integer_trail_->LowerBoundAsLiteral(div)})) {
1236 const IntegerValue new_max_denom = max_num / min_div;
1237 if (max_denom > new_max_denom) {
1239 denom.LowerOrEqual(new_max_denom),
1240 {integer_trail_->UpperBoundAsLiteral(num), num.GreaterOrEqual(0),
1241 integer_trail_->LowerBoundAsLiteral(div)})) {
1249 const IntegerValue new_min_denom =
CeilRatio(min_num + 1, max_div + 1);
1250 if (min_denom < new_min_denom) {
1251 if (!integer_trail_->
SafeEnqueue(denom.GreaterOrEqual(new_min_denom),
1252 {integer_trail_->LowerBoundAsLiteral(num),
1253 integer_trail_->UpperBoundAsLiteral(div),
1254 div.GreaterOrEqual(0)})) {
1263 const int id = watcher->
Register(
this);
1274 : a_(
a), b_(
b), c_(c), integer_trail_(integer_trail) {
1279 const IntegerValue min_a = integer_trail_->
LowerBound(a_);
1280 const IntegerValue max_a = integer_trail_->
UpperBound(a_);
1281 IntegerValue min_c = integer_trail_->
LowerBound(c_);
1282 IntegerValue max_c = integer_trail_->
UpperBound(c_);
1284 if (max_a / b_ < max_c) {
1288 {integer_trail_->UpperBoundAsLiteral(a_)})) {
1291 }
else if (max_a / b_ > max_c) {
1292 const IntegerValue new_max_a =
1293 max_c >= 0 ? max_c * b_ + b_ - 1
1294 : IntegerValue(
CapProd(max_c.value(), b_.value()));
1295 CHECK_LT(new_max_a, max_a);
1298 {integer_trail_->UpperBoundAsLiteral(c_)})) {
1303 if (min_a / b_ > min_c) {
1307 {integer_trail_->LowerBoundAsLiteral(a_)})) {
1310 }
else if (min_a / b_ < min_c) {
1311 const IntegerValue new_min_a =
1312 min_c > 0 ? IntegerValue(
CapProd(min_c.value(), b_.value()))
1313 : min_c * b_ - b_ + 1;
1314 CHECK_GT(new_min_a, min_a);
1317 {integer_trail_->LowerBoundAsLiteral(c_)})) {
1326 const int id = watcher->
Register(
this);
1335 :
expr_(expr), mod_(mod), target_(target), integer_trail_(integer_trail) {
1340 if (!PropagateSignsAndTargetRange())
return false;
1341 if (!PropagateOuterBounds())
return false;
1343 if (integer_trail_->
LowerBound(expr_) >= 0) {
1344 if (!PropagateBoundsWhenExprIsPositive(expr_, target_))
return false;
1345 }
else if (integer_trail_->
UpperBound(expr_) <= 0) {
1346 if (!PropagateBoundsWhenExprIsPositive(expr_.
Negated(),
1355 bool FixedModuloPropagator::PropagateSignsAndTargetRange() {
1357 if (integer_trail_->
UpperBound(target_) >= mod_) {
1363 if (integer_trail_->
LowerBound(target_) <= -mod_) {
1370 if (integer_trail_->
LowerBound(expr_) >= 0 &&
1373 {expr_.GreaterOrEqual(0)})) {
1378 if (integer_trail_->
UpperBound(expr_) <= 0 &&
1381 {expr_.LowerOrEqual(0)})) {
1389 bool FixedModuloPropagator::PropagateOuterBounds() {
1390 const IntegerValue min_expr = integer_trail_->
LowerBound(expr_);
1391 const IntegerValue max_expr = integer_trail_->
UpperBound(expr_);
1392 const IntegerValue min_target = integer_trail_->
LowerBound(target_);
1393 const IntegerValue max_target = integer_trail_->
UpperBound(target_);
1395 if (max_expr % mod_ > max_target) {
1397 expr_.
LowerOrEqual((max_expr / mod_) * mod_ + max_target),
1398 {integer_trail_->UpperBoundAsLiteral(target_),
1399 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1404 if (min_expr % mod_ < min_target) {
1407 {integer_trail_->LowerBoundAsLiteral(expr_),
1408 integer_trail_->LowerBoundAsLiteral(target_)})) {
1413 if (min_expr / mod_ == max_expr / mod_) {
1414 if (min_target < min_expr % mod_) {
1417 {integer_trail_->LowerBoundAsLiteral(target_),
1418 integer_trail_->UpperBoundAsLiteral(target_),
1419 integer_trail_->LowerBoundAsLiteral(expr_),
1420 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1425 if (max_target > max_expr % mod_) {
1427 target_.
LowerOrEqual(max_expr - (max_expr / mod_) * mod_),
1428 {integer_trail_->LowerBoundAsLiteral(target_),
1429 integer_trail_->UpperBoundAsLiteral(target_),
1430 integer_trail_->LowerBoundAsLiteral(expr_),
1431 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1435 }
else if (min_expr / mod_ == 0 && min_target < 0) {
1437 if (min_target < min_expr) {
1440 {integer_trail_->LowerBoundAsLiteral(target_),
1441 integer_trail_->LowerBoundAsLiteral(expr_)})) {
1445 }
else if (max_expr / mod_ == 0 && max_target > 0) {
1447 if (max_target > max_expr) {
1450 {integer_trail_->UpperBoundAsLiteral(target_),
1451 integer_trail_->UpperBoundAsLiteral(expr_)})) {
1460 bool FixedModuloPropagator::PropagateBoundsWhenExprIsPositive(
1461 AffineExpression expr, AffineExpression target) {
1462 const IntegerValue min_target = integer_trail_->
LowerBound(target);
1463 DCHECK_GE(min_target, 0);
1464 const IntegerValue max_target = integer_trail_->
UpperBound(target);
1468 if (min_target == 0 && max_target == mod_ - 1)
return true;
1470 const IntegerValue min_expr = integer_trail_->
LowerBound(expr);
1471 const IntegerValue max_expr = integer_trail_->
UpperBound(expr);
1473 if (max_expr % mod_ < min_target) {
1474 DCHECK_GE(max_expr, 0);
1476 expr.LowerOrEqual((max_expr / mod_ - 1) * mod_ + max_target),
1477 {integer_trail_->UpperBoundAsLiteral(expr),
1478 integer_trail_->LowerBoundAsLiteral(target),
1479 integer_trail_->UpperBoundAsLiteral(target)})) {
1484 if (min_expr % mod_ > max_target) {
1485 DCHECK_GE(min_expr, 0);
1487 expr.GreaterOrEqual((min_expr / mod_ + 1) * mod_ + min_target),
1488 {integer_trail_->LowerBoundAsLiteral(target),
1489 integer_trail_->UpperBoundAsLiteral(target),
1490 integer_trail_->LowerBoundAsLiteral(expr)})) {
1499 const int id = watcher->
Register(
this);
1506 const std::vector<Literal>& selectors,
1507 const std::vector<IntegerValue>& values) {
1512 CHECK(!values.empty());
1513 CHECK_EQ(values.size(), selectors.size());
1514 std::vector<int64_t> unique_values;
1515 absl::flat_hash_map<int64_t, std::vector<Literal>> value_to_selector;
1516 for (
int i = 0; i < values.size(); ++i) {
1517 unique_values.push_back(values[i].
value());
1518 value_to_selector[values[i].value()].push_back(selectors[i]);
1523 if (unique_values.size() == 1) {
1530 for (
const int64_t v : unique_values) {
1531 const std::vector<Literal>& selectors = value_to_selector[v];
1532 if (selectors.size() == 1) {
1533 encoder->AssociateToIntegerEqualValue(selectors[0],
var,
1538 encoder->AssociateToIntegerEqualValue(l,
var, IntegerValue(v));
const std::vector< IntVar * > vars_
static Domain FromValues(std::vector< int64_t > values)
Creates a domain from the union of an unsorted list of integer values.
static int64_t GCD64(int64_t x, int64_t y)
void SaveState(T *object)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
DivisionPropagator(AffineExpression num, AffineExpression denom, AffineExpression div, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
void RegisterWith(GenericLiteralWatcher *watcher)
FixedDivisionPropagator(AffineExpression a, IntegerValue b, AffineExpression c, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
FixedModuloPropagator(AffineExpression expr, IntegerValue mod, AffineExpression target, IntegerTrail *integer_trail)
void RegisterReversibleInt(int id, int *rev)
void WatchLiteral(Literal l, int id, int watch_index=-1)
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
void WatchAffineExpression(AffineExpression e, int id)
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
int Register(PropagatorInterface *propagator)
void NotifyThatPropagatorMayNotReachFixedPointInOnePass(int id)
std::pair< IntegerValue, IntegerValue > ConditionalLb(IntegerLiteral integer_literal, IntegerVariable target_var) const
IntegerSumLE(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const std::vector< IntegerValue > &coeffs, IntegerValue upper_bound, Model *model)
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
int FindTrailIndexOfVarBefore(IntegerVariable var, int threshold) const
bool IsFixed(IntegerVariable i) const
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue UpperBound(IntegerVariable i) const
ABSL_MUST_USE_RESULT bool SafeEnqueue(IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
bool VariableLowerBoundIsFromLevelZero(IntegerVariable var) const
void AppendRelaxedLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, absl::Span< const IntegerVariable > vars, std::vector< IntegerLiteral > *reason) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
IntegerValue LowerBound(IntegerVariable i) const
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
LinMinPropagator(const std::vector< LinearExpression > &exprs, IntegerVariable min_var, Model *model)
void RegisterWith(GenericLiteralWatcher *watcher)
void RegisterWith(GenericLiteralWatcher *watcher)
MinPropagator(const std::vector< IntegerVariable > &vars, IntegerVariable min_var, IntegerTrail *integer_trail)
Class that owns everything related to a particular optimization model.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
void RegisterWith(GenericLiteralWatcher *watcher)
ProductPropagator(AffineExpression a, AffineExpression b, AffineExpression p, IntegerTrail *integer_trail)
void RegisterWith(GenericLiteralWatcher *watcher)
SquarePropagator(AffineExpression x, AffineExpression s, IntegerTrail *integer_trail)
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
std::function< void(Model *)> IsOneOf(IntegerVariable var, const std::vector< Literal > &selectors, const std::vector< IntegerValue > &values)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
int64_t CeilSquareRoot(int64_t a)
IntegerVariable PositiveVariable(IntegerVariable i)
IntegerValue PositiveRemainder(IntegerValue dividend, IntegerValue positive_divisor)
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
int64_t FloorSquareRoot(int64_t a)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> ReifiedBoolOr(const std::vector< Literal > &literals, Literal r)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
AffineExpression Negated() const
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
IntegerLiteral LowerOrEqual(IntegerValue bound) const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerLiteral Negated() const
#define VLOG(verboselevel)