Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
integer_expr.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdint>
18#include <cstdlib>
19#include <cstring>
20#include <limits>
21#include <utility>
22#include <vector>
23
24#include "absl/log/check.h"
25#include "absl/numeric/int128.h"
26#include "absl/types/span.h"
30#include "ortools/sat/integer.h"
33#include "ortools/sat/model.h"
35#include "ortools/sat/util.h"
38
39namespace operations_research {
40namespace sat {
41
42template <bool use_int128>
44 absl::Span<const Literal> enforcement_literals,
45 absl::Span<const IntegerVariable> vars,
46 absl::Span<const IntegerValue> coeffs, IntegerValue upper, Model* model)
47 : upper_bound_(upper),
48 shared_(
49 model->GetOrCreate<LinearConstraintPropagator<use_int128>::Shared>()),
50 size_(vars.size()),
51 vars_(new IntegerVariable[size_]),
52 coeffs_(new IntegerValue[size_]),
53 max_variations_(new IntegerValue[size_]) {
54 // TODO(user): deal with this corner case.
55 CHECK(!vars.empty());
56
57 // Copy data.
58 memcpy(vars_.get(), vars.data(), size_ * sizeof(IntegerVariable));
59 memcpy(coeffs_.get(), coeffs.data(), size_ * sizeof(IntegerValue));
60
61 // Handle negative coefficients.
62 for (int i = 0; i < size_; ++i) {
63 if (coeffs_[i] < 0) {
64 vars_[i] = NegationOf(vars_[i]);
65 coeffs_[i] = -coeffs_[i];
66 }
67 }
68
69 // Literal reason will only be used with the negation of enforcement_literals.
70 // It will stay constant. We also do not store enforcement_literals, but
71 // retrieve them from there.
72 literal_reason_.reserve(enforcement_literals.size());
73 for (const Literal literal : enforcement_literals) {
74 literal_reason_.push_back(literal.Negated());
75 }
76
77 // Initialize the reversible numbers.
78 rev_num_fixed_vars_ = 0;
79 rev_lb_fixed_vars_ = IntegerValue(0);
80}
81
82// TODO(user): Avoid duplication with other constructor.
83template <bool use_int128>
85 LinearConstraint ct, Model* model)
86 : upper_bound_(ct.ub),
87 shared_(
88 model->GetOrCreate<LinearConstraintPropagator<use_int128>::Shared>()),
89 size_(ct.num_terms),
90 vars_(std::move(ct.vars)),
91 coeffs_(std::move(ct.coeffs)),
92 max_variations_(new IntegerValue[size_]) {
93 // TODO(user): deal with this corner case.
94 CHECK_GT(size_, 0);
95
96 // Handle negative coefficients.
97 for (int i = 0; i < size_; ++i) {
98 if (coeffs_[i] < 0) {
99 vars_[i] = NegationOf(vars_[i]);
100 coeffs_[i] = -coeffs_[i];
101 }
102 }
103
104 // Initialize the reversible numbers.
105 rev_num_fixed_vars_ = 0;
106 rev_lb_fixed_vars_ = IntegerValue(0);
107}
108
109template <bool use_int128>
110void LinearConstraintPropagator<use_int128>::FillIntegerReason() {
111 shared_->integer_reason.clear();
112 shared_->reason_coeffs.clear();
113 for (int i = 0; i < size_; ++i) {
114 const IntegerVariable var = vars_[i];
115 if (!shared_->integer_trail->VariableLowerBoundIsFromLevelZero(var)) {
116 shared_->integer_reason.push_back(
117 shared_->integer_trail->LowerBoundAsLiteral(var));
118 shared_->reason_coeffs.push_back(coeffs_[i]);
119 }
120 }
121}
122
123namespace {
124IntegerValue CappedCast(absl::int128 input, IntegerValue cap) {
125 if (input >= absl::int128(cap.value())) {
126 return cap;
127 }
128 return IntegerValue(static_cast<int64_t>(input));
129}
130
131} // namespace
132
133// NOTE(user): This is only used with int128, so we code only a single version.
134template <bool use_int128>
135std::pair<IntegerValue, IntegerValue>
137 IntegerLiteral integer_literal, IntegerVariable target_var) const {
138 // The code below is wrong if integer_literal and target_var are the same.
139 // In this case we return the trivial bounds.
140 if (PositiveVariable(integer_literal.var) == PositiveVariable(target_var)) {
141 if (integer_literal.var == target_var) {
142 return {kMinIntegerValue, integer_literal.bound};
143 } else {
144 return {integer_literal.Negated().bound, kMinIntegerValue};
145 }
146 }
147
148 // Recall that all our coefficient are positive.
149 bool literal_var_present = false;
150 bool literal_var_present_positively = false;
151 IntegerValue var_coeff;
152
153 bool target_var_present_negatively = false;
154 absl::int128 target_coeff;
155
156 // Warning: It is important to do the computation like the propagation is
157 // doing it to be sure we don't have overflow, since this is what we check
158 // when creating constraints.
159 absl::int128 lb_128 = 0;
160 for (int i = 0; i < size_; ++i) {
161 const IntegerVariable var = vars_[i];
162 const IntegerValue coeff = coeffs_[i];
163 if (var == NegationOf(target_var)) {
164 target_coeff = absl::int128(coeff.value());
165 target_var_present_negatively = true;
166 }
167
168 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
169 lb_128 += absl::int128(coeff.value()) * absl::int128(lb.value());
170 if (PositiveVariable(var) == PositiveVariable(integer_literal.var)) {
171 var_coeff = coeff;
172 literal_var_present = true;
173 literal_var_present_positively = (var == integer_literal.var);
174 }
175 }
176
177 if (!literal_var_present || !target_var_present_negatively) {
179 }
180
181 // The upper bound on NegationOf(target_var) are lb(-target) + slack / coeff.
182 // So the lower bound on target_var is ub - slack / coeff.
183 const absl::int128 slack128 = absl::int128(upper_bound_.value()) - lb_128;
184 const IntegerValue target_lb = shared_->integer_trail->LowerBound(target_var);
185 const IntegerValue target_ub = shared_->integer_trail->UpperBound(target_var);
186 if (slack128 <= 0) {
187 // TODO(user): If there is a conflict (negative slack) we can be more
188 // precise.
189 return {target_ub, target_ub};
190 }
191
192 const IntegerValue target_diff = target_ub - target_lb;
193 const IntegerValue delta = CappedCast(slack128 / target_coeff, target_diff);
194
195 // A literal means var >= bound.
196 if (literal_var_present_positively) {
197 // We have var_coeff * var in the expression, the literal is var >= bound.
198 // When it is false, it is not relevant as implied_lb used var >= lb.
199 // When it is true, the diff is bound - lb.
200 const IntegerValue diff =
201 std::max(IntegerValue(0),
202 integer_literal.bound -
203 shared_->integer_trail->LowerBound(integer_literal.var));
204 const absl::int128 tighter_slack =
205 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
206 absl::int128(diff.value()));
207 const IntegerValue tighter_delta =
208 CappedCast(tighter_slack / target_coeff, target_diff);
209 return {target_ub - delta, target_ub - tighter_delta};
210 } else {
211 // We have var_coeff * -var in the expression, the literal is var >= bound.
212 // When it is true, it is not relevant as implied_lb used -var >= -ub.
213 // And when it is false it means var < bound, so -var >= -bound + 1
214 const IntegerValue diff =
215 std::max(IntegerValue(0),
216 shared_->integer_trail->UpperBound(integer_literal.var) -
217 integer_literal.bound + 1);
218 const absl::int128 tighter_slack =
219 std::max(absl::int128(0), slack128 - absl::int128(var_coeff.value()) *
220 absl::int128(diff.value()));
221 const IntegerValue tighter_delta =
222 CappedCast(tighter_slack / target_coeff, target_diff);
223 return {target_ub - tighter_delta, target_ub - delta};
224 }
225}
226
227template <bool use_int128>
229 int /*id*/, IntegerLiteral /*to_explain*/, IntegerReason* reason) {
230 reason->boolean_literals_at_false = literal_reason_;
231 reason->vars = absl::MakeSpan(vars_.get(), size_);
232 reason->coeffs = absl::MakeSpan(coeffs_.get(), size_);
233}
234
235template <bool use_int128>
237 // Reified case: If any of the enforcement_literals are false, we ignore the
238 // constraint.
239 int num_unassigned_enforcement_literal = 0;
240 LiteralIndex unique_unnasigned_literal = kNoLiteralIndex;
241 for (const Literal negated_enforcement : literal_reason_) {
242 const Literal literal = negated_enforcement.Negated();
243 if (shared_->assignment.LiteralIsFalse(literal)) return true;
244 if (!shared_->assignment.LiteralIsTrue(literal)) {
245 ++num_unassigned_enforcement_literal;
246 unique_unnasigned_literal = literal.Index();
247 }
248 }
249
250 // Unfortunately, we can't propagate anything if we have more than one
251 // unassigned enforcement literal.
252 if (num_unassigned_enforcement_literal > 1) return true;
253
254 int num_fixed_vars = rev_num_fixed_vars_;
255 IntegerValue lb_fixed_vars = rev_lb_fixed_vars_;
256
257 // Compute the new lower bound and update the reversible structures.
258 absl::int128 lb_128 = 0;
259 IntegerValue lb_unfixed_vars = IntegerValue(0);
260 for (int i = num_fixed_vars; i < size_; ++i) {
261 const IntegerVariable var = vars_[i];
262 const IntegerValue coeff = coeffs_[i];
263 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
264 const IntegerValue ub = shared_->integer_trail->UpperBound(var);
265 if (use_int128) {
266 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
267 continue;
268 }
269
270 if (lb != ub) {
271 max_variations_[i] = (ub - lb) * coeff;
272 lb_unfixed_vars += lb * coeff;
273 } else {
274 // Update the set of fixed variables.
275 std::swap(vars_[i], vars_[num_fixed_vars]);
276 std::swap(coeffs_[i], coeffs_[num_fixed_vars]);
277 std::swap(max_variations_[i], max_variations_[num_fixed_vars]);
278 num_fixed_vars++;
279 lb_fixed_vars += lb * coeff;
280 }
281 }
282 shared_->time_limit->AdvanceDeterministicTime(
283 static_cast<double>(size_ - num_fixed_vars) * 5e-9);
284
285 // Save the current sum of fixed variables.
286 if (is_registered_ && num_fixed_vars != rev_num_fixed_vars_) {
287 CHECK(!use_int128);
288 shared_->rev_integer_value_repository->SaveState(&rev_lb_fixed_vars_);
289 shared_->rev_int_repository->SaveState(&rev_num_fixed_vars_);
290 rev_num_fixed_vars_ = num_fixed_vars;
291 rev_lb_fixed_vars_ = lb_fixed_vars;
292 }
293
294 // If use_int128 is true, the slack or propagation slack can be larger than
295 // this. To detect overflow with capped arithmetic, it is important the slack
296 // used in our algo never exceed this value.
297 const absl::int128 max_slack = std::numeric_limits<int64_t>::max() - 1;
298
299 // Conflict?
300 IntegerValue slack;
301 absl::int128 slack128;
302 if (use_int128) {
303 slack128 = absl::int128(upper_bound_.value()) - lb_128;
304 if (slack128 < 0) {
305 // It is fine if we don't relax as much as possible.
306 // Note that RelaxLinearReason() is overflow safe.
307 slack = static_cast<int64_t>(std::max(-max_slack, slack128));
308 }
309 } else {
310 slack = upper_bound_ - (lb_fixed_vars + lb_unfixed_vars);
311 }
312 if (slack < 0) {
313 FillIntegerReason();
314 shared_->integer_trail->RelaxLinearReason(
315 -slack - 1, shared_->reason_coeffs, &shared_->integer_reason);
316
317 if (num_unassigned_enforcement_literal == 1) {
318 // Propagate the only non-true literal to false.
319 const Literal to_propagate = Literal(unique_unnasigned_literal).Negated();
320 std::vector<Literal> tmp = literal_reason_;
321 tmp.erase(std::find(tmp.begin(), tmp.end(), to_propagate));
322 return shared_->integer_trail->EnqueueLiteral(to_propagate, tmp,
323 shared_->integer_reason);
324 }
325 return shared_->integer_trail->ReportConflict(literal_reason_,
326 shared_->integer_reason);
327 }
328
329 // We can only propagate more if all the enforcement literals are true.
330 if (num_unassigned_enforcement_literal > 0) return true;
331
332 // The lower bound of all the variables except one can be used to update the
333 // upper bound of the last one.
334 for (int i = num_fixed_vars; i < size_; ++i) {
335 if (!use_int128 && max_variations_[i] <= slack) continue;
336
337 // TODO(user): If the new ub fall into an hole of the variable, we can
338 // actually relax the reason more by computing a better slack.
339 const IntegerVariable var = vars_[i];
340 const IntegerValue coeff = coeffs_[i];
341 const IntegerValue lb = shared_->integer_trail->LowerBound(var);
342
343 IntegerValue new_ub;
344 IntegerValue propagation_slack;
345 if (use_int128) {
346 const absl::int128 coeff128 = absl::int128(coeff.value());
347 const absl::int128 div128 = slack128 / coeff128;
348 const IntegerValue ub = shared_->integer_trail->UpperBound(var);
349 if (absl::int128(lb.value()) + div128 >= absl::int128(ub.value())) {
350 continue;
351 }
352 new_ub = lb + IntegerValue(static_cast<int64_t>(div128));
353 propagation_slack = static_cast<int64_t>(
354 std::min(max_slack, (div128 + 1) * coeff128 - slack128 - 1));
355 } else {
356 const IntegerValue div = slack / coeff;
357 new_ub = lb + div;
358 propagation_slack = (div + 1) * coeff - slack - 1;
359 }
360 if (!shared_->integer_trail->EnqueueWithLazyReason(
361 IntegerLiteral::LowerOrEqual(var, new_ub), 0, propagation_slack,
362 this)) {
363 // TODO(user): this is never supposed to happen since if we didn't have a
364 // conflict above, we should be able to reduce the upper bound. It might
365 // indicate an issue with our Boolean <-> integer encoding.
366 return false;
367 }
368 }
369
370 return true;
371}
372
373template <bool use_int128>
375 // TODO(user): Deal with enforcements. It is just a bit of code to read the
376 // value of the literals at level zero.
377 if (!literal_reason_.empty()) return true;
378
379 // Compute the new lower bound and update the reversible structures.
380 absl::int128 lb_128 = 0;
381 IntegerValue min_activity = IntegerValue(0);
382 for (int i = 0; i < size_; ++i) {
383 const IntegerVariable var = vars_[i];
384 const IntegerValue coeff = coeffs_[i];
385 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(var);
386 if (use_int128) {
387 lb_128 += absl::int128(lb.value()) * absl::int128(coeff.value());
388 } else {
389 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(var);
390 max_variations_[i] = (ub - lb) * coeff;
391 min_activity += lb * coeff;
392 }
393 }
394 shared_->time_limit->AdvanceDeterministicTime(
395 static_cast<double>(size_ * 1e-9));
396
397 // Conflict?
398 IntegerValue slack;
399 absl::int128 slack128;
400 if (use_int128) {
401 slack128 = absl::int128(upper_bound_.value()) - lb_128;
402 if (slack128 < 0) {
403 return shared_->integer_trail->ReportConflict({}, {});
404 }
405 } else {
406 slack = upper_bound_ - min_activity;
407 if (slack < 0) {
408 return shared_->integer_trail->ReportConflict({}, {});
409 }
410 }
411
412 // The lower bound of all the variables except one can be used to update the
413 // upper bound of the last one.
414 for (int i = 0; i < size_; ++i) {
415 if (!use_int128 && max_variations_[i] <= slack) continue;
416
417 const IntegerVariable var = vars_[i];
418 const IntegerValue coeff = coeffs_[i];
419 const IntegerValue lb = shared_->integer_trail->LevelZeroLowerBound(var);
420
421 IntegerValue new_ub;
422 if (use_int128) {
423 const IntegerValue ub = shared_->integer_trail->LevelZeroUpperBound(var);
424 if (absl::int128((ub - lb).value()) * absl::int128(coeff.value()) <=
425 slack128) {
426 continue;
427 }
428 const absl::int128 div128 = slack128 / absl::int128(coeff.value());
429 new_ub = lb + IntegerValue(static_cast<int64_t>(div128));
430 } else {
431 const IntegerValue div = slack / coeff;
432 new_ub = lb + div;
433 }
434 if (!shared_->integer_trail->Enqueue(
435 IntegerLiteral::LowerOrEqual(var, new_ub), {}, {})) {
436 return false;
437 }
438 }
439
440 return true;
441}
442
443template <bool use_int128>
445 GenericLiteralWatcher* watcher) {
446 is_registered_ = true;
447 const int id = watcher->Register(this);
448 for (int i = 0; i < size_; ++i) {
449 watcher->WatchLowerBound(vars_[i], id);
450 }
451 for (const Literal negated_enforcement : literal_reason_) {
452 // We only watch the true direction.
453 //
454 // TODO(user): if there is more than one, maybe we should watch more to
455 // propagate a "conflict" as soon as only one is unassigned?
456 watcher->WatchLiteral(negated_enforcement.Negated(), id);
457 }
458}
459
460// Explicit declaration.
463
465 const std::vector<IntegerVariable>& vars,
466 const std::vector<IntegerValue>& coeffs,
467 Model* model)
468 : target_(target),
469 vars_(vars),
470 coeffs_(coeffs),
471 trail_(model->GetOrCreate<Trail>()),
472 integer_trail_(model->GetOrCreate<IntegerTrail>()) {
473 auto* watcher = model->GetOrCreate<GenericLiteralWatcher>();
474 const int id = watcher->Register(this);
475 watcher->SetPropagatorPriority(id, 2);
476 watcher->WatchIntegerVariable(target, id);
477 for (const IntegerVariable& var : vars_) {
478 watcher->WatchIntegerVariable(var, id);
479 }
480}
481
482// TODO(user): We could go even further than just the GCD, and do more
483// arithmetic to tighten the target bounds. See for instance a problem like
484// ej.mps.gz that we don't solve easily, but has just 3 variables! the goal is
485// to minimize X, given 31013 X - 41014 Y - 51015 Z = -31013 (all >=0, Y and Z
486// bounded with high values). I know some MIP solvers have a basic linear
487// diophantine equation support.
489 // TODO(user): Once the GCD is not 1, we could at any level make sure the
490 // objective is of the correct form. For now, this only happen in a few
491 // miplib problem that we close quickly, so I didn't add the extra code yet.
492 if (trail_->CurrentDecisionLevel() != 0) return true;
493
494 int64_t gcd = 0;
495 IntegerValue sum(0);
496 for (int i = 0; i < vars_.size(); ++i) {
497 if (integer_trail_->IsFixed(vars_[i])) {
498 sum += coeffs_[i] * integer_trail_->LowerBound(vars_[i]);
499 continue;
500 }
501 gcd = MathUtil::GCD64(gcd, std::abs(coeffs_[i].value()));
502 if (gcd == 1) break;
503 }
504 if (gcd == 0) return true; // All fixed.
505
506 if (gcd > gcd_) {
507 VLOG(1) << "Objective gcd: " << gcd;
508 }
509 CHECK_GE(gcd, gcd_);
510 gcd_ = IntegerValue(gcd);
511
512 const IntegerValue lb = integer_trail_->LowerBound(target_);
513 const IntegerValue lb_remainder = PositiveRemainder(lb - sum, gcd_);
514 if (lb_remainder != 0) {
515 if (!integer_trail_->Enqueue(
516 IntegerLiteral::GreaterOrEqual(target_, lb + gcd_ - lb_remainder),
517 {}, {}))
518 return false;
519 }
520
521 const IntegerValue ub = integer_trail_->UpperBound(target_);
522 const IntegerValue ub_remainder =
523 PositiveRemainder(ub - sum, IntegerValue(gcd));
524 if (ub_remainder != 0) {
525 if (!integer_trail_->Enqueue(
526 IntegerLiteral::LowerOrEqual(target_, ub - ub_remainder), {}, {}))
527 return false;
528 }
529
530 return true;
531}
532
533MinPropagator::MinPropagator(std::vector<AffineExpression> vars,
534 AffineExpression min_var,
535 IntegerTrail* integer_trail)
536 : vars_(std::move(vars)),
537 min_var_(min_var),
538 integer_trail_(integer_trail) {}
539
541 if (vars_.empty()) return true;
542
543 // Count the number of interval that are possible candidate for the min.
544 // Only the intervals for which lb > current_min_ub cannot.
545 const IntegerLiteral min_ub_literal =
546 integer_trail_->UpperBoundAsLiteral(min_var_);
547 const IntegerValue current_min_ub = integer_trail_->UpperBound(min_var_);
548 int num_intervals_that_can_be_min = 0;
549 int last_possible_min_interval = 0;
550
551 IntegerValue min = kMaxIntegerValue;
552 for (int i = 0; i < vars_.size(); ++i) {
553 const IntegerValue lb = integer_trail_->LowerBound(vars_[i]);
554 min = std::min(min, lb);
555 if (lb <= current_min_ub) {
556 ++num_intervals_that_can_be_min;
557 last_possible_min_interval = i;
558 }
559 }
560
561 // Propagation a)
562 if (min > integer_trail_->LowerBound(min_var_)) {
563 integer_reason_.clear();
564 for (const AffineExpression& var : vars_) {
565 integer_reason_.push_back(var.GreaterOrEqual(min));
566 }
567 if (!integer_trail_->SafeEnqueue(min_var_.GreaterOrEqual(min),
568 integer_reason_)) {
569 return false;
570 }
571 }
572
573 // Propagation b)
574 if (num_intervals_that_can_be_min == 1) {
575 const IntegerValue ub_of_only_candidate =
576 integer_trail_->UpperBound(vars_[last_possible_min_interval]);
577 if (current_min_ub < ub_of_only_candidate) {
578 integer_reason_.clear();
579
580 // The reason is that all the other interval start after current_min_ub.
581 // And that min_ub has its current value.
582 integer_reason_.push_back(min_ub_literal);
583 for (const AffineExpression& var : vars_) {
584 if (var == vars_[last_possible_min_interval]) continue;
585 integer_reason_.push_back(var.GreaterOrEqual(current_min_ub + 1));
586 }
587 if (!integer_trail_->SafeEnqueue(
588 vars_[last_possible_min_interval].LowerOrEqual(current_min_ub),
589 integer_reason_)) {
590 return false;
591 }
592 }
593 }
594
595 // Conflict.
596 //
597 // TODO(user): Not sure this code is useful since this will be detected
598 // by the fact that the [lb, ub] of the min is empty. It depends on the
599 // propagation order though, but probably the precedences propagator would
600 // propagate before this one. So change this to a CHECK?
601 if (num_intervals_that_can_be_min == 0) {
602 integer_reason_.clear();
603
604 // Almost the same as propagation b).
605 integer_reason_.push_back(min_ub_literal);
606 for (const AffineExpression& var : vars_) {
607 IntegerLiteral lit = var.GreaterOrEqual(current_min_ub + 1);
608 if (lit != IntegerLiteral::TrueLiteral()) {
609 integer_reason_.push_back(lit);
610 }
611 }
612 return integer_trail_->ReportConflict(integer_reason_);
613 }
614
615 return true;
616}
617
619 const int id = watcher->Register(this);
620 for (const AffineExpression& var : vars_) {
621 watcher->WatchLowerBound(var, id);
622 }
623 watcher->WatchUpperBound(min_var_, id);
624}
625
627 absl::Span<const Literal> enforcement_literals,
628 std::vector<LinearExpression> exprs, IntegerVariable min_var, Model* model)
629 : exprs_(std::move(exprs)),
630 min_var_(min_var),
631 model_(model),
632 integer_trail_(*model_->GetOrCreate<IntegerTrail>()),
633 enforcement_helper_(*model_->GetOrCreate<EnforcementHelper>()) {
635 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
636 RegisterWith(watcher));
637
638 // Precondition.
639 for (const LinearExpression& expr : exprs_) {
640 for (const IntegerValue coeff : expr.coeffs) CHECK_GT(coeff, 0);
641 }
642}
643
645 IntegerLiteral /*to_explain*/,
646 IntegerReason* reason) {
648 enforcement_helper_.GetEnforcementLiterals(enforcement_id_);
649
650 // Fill the linear reason.
651 // Note that index_at_propagation and slack are already filled.
652 tmp_vars_.assign(exprs_[id].vars.begin(), exprs_[id].vars.end());
653 tmp_coeffs_.assign(exprs_[id].coeffs.begin(), exprs_[id].coeffs.end());
654
655 // Tricky: we propagated using exprs_[id] <= min_var_, so we do need the
656 // min_var_ ub at the time of propagation. Note that we already have an
657 // "older" min_var_ ub in integer_reason_for_unique_candidate_. But since this
658 // one could be relaxed, I think it is easier to just include it twice.
659 tmp_vars_.push_back(NegationOf(min_var_));
660 tmp_coeffs_.push_back(1);
661
662 reason->vars = tmp_vars_;
663 reason->coeffs = tmp_coeffs_;
664
665 // Now add the old integer_reason that triggered this propagation.
666 reason->integer_literals = integer_reason_for_unique_candidate_;
667}
668
669bool GreaterThanMinOfExprsPropagator::PropagateLinearUpperBound(
670 int id, absl::Span<const IntegerVariable> vars,
671 absl::Span<const IntegerValue> coeffs, const IntegerValue upper_bound) {
672 IntegerValue sum_lb = IntegerValue(0);
673 const int num_vars = vars.size();
674 max_variations_.resize(num_vars);
675 for (int i = 0; i < num_vars; ++i) {
676 const IntegerVariable var = vars[i];
677 const IntegerValue coeff = coeffs[i];
678 // The coefficients are assumed to be positive for this to work properly.
679 DCHECK_GE(coeff, 0);
680 const IntegerValue lb = integer_trail_.LowerBound(var);
681 const IntegerValue ub = integer_trail_.UpperBound(var);
682 max_variations_[i] = (ub - lb) * coeff;
683 sum_lb += lb * coeff;
684 }
685
686 model_->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
687 static_cast<double>(num_vars) * 1e-9);
688
689 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
690 const IntegerValue slack = upper_bound - sum_lb;
691 if (slack < 0) {
692 // Conflict.
693 local_reason_.clear();
694 reason_coeffs_.clear();
695 for (int i = 0; i < num_vars; ++i) {
696 const IntegerVariable var = vars[i];
697 if (!integer_trail_.VariableLowerBoundIsFromLevelZero(var)) {
698 local_reason_.push_back(integer_trail_.LowerBoundAsLiteral(var));
699 reason_coeffs_.push_back(coeffs[i]);
700 }
701 }
702 integer_trail_.RelaxLinearReason(-slack - 1, reason_coeffs_,
703 &local_reason_);
704 local_reason_.insert(local_reason_.end(),
705 integer_reason_for_unique_candidate_.begin(),
706 integer_reason_for_unique_candidate_.end());
707 if (status == EnforcementStatus::IS_ENFORCED) {
708 return enforcement_helper_.ReportConflict(enforcement_id_, local_reason_);
709 } else {
710 return enforcement_helper_.PropagateWhenFalse(
711 enforcement_id_, /*literal_reason=*/{}, local_reason_);
712 }
713 }
714
715 // The lower bound of all the variables except one can be used to update the
716 // upper bound of the last one.
717 if (status != EnforcementStatus::IS_ENFORCED) return true;
718 for (int i = 0; i < num_vars; ++i) {
719 if (max_variations_[i] <= slack) continue;
720
721 const IntegerVariable var = vars[i];
722 const IntegerValue coeff = coeffs[i];
723 const IntegerValue div = slack / coeff;
724 const IntegerValue new_ub = integer_trail_.LowerBound(var) + div;
725 const IntegerValue propagation_slack = (div + 1) * coeff - slack - 1;
726 if (!integer_trail_.EnqueueWithLazyReason(
727 IntegerLiteral::LowerOrEqual(var, new_ub), id, propagation_slack,
728 this)) {
729 return false;
730 }
731 }
732 return true;
733}
734
736 // The case of empty exprs is handled in cp_model_loader.cc.
737 DCHECK(!exprs_.empty());
738
739 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
740 if (status != EnforcementStatus::IS_ENFORCED &&
742 return true;
743 }
744
745 // Count the number of interval that are possible candidate for the min.
746 // Only the intervals for which lb > current_min_ub cannot.
747 const IntegerValue current_min_ub = integer_trail_.UpperBound(min_var_);
748 int num_intervals_that_can_be_min = 0;
749 int last_possible_min_interval = 0;
750
751 expr_lbs_.clear();
752 IntegerValue min_of_linear_expression_lb = kMaxIntegerValue;
753 for (int i = 0; i < exprs_.size(); ++i) {
754 const IntegerValue lb = exprs_[i].Min(integer_trail_);
755 expr_lbs_.push_back(lb);
756 min_of_linear_expression_lb = std::min(min_of_linear_expression_lb, lb);
757 if (lb <= current_min_ub) {
758 ++num_intervals_that_can_be_min;
759 last_possible_min_interval = i;
760 }
761 }
762
763 // Propagation a) lb(min) >= lb(MIN(exprs)) = MIN(lb(exprs));
764
765 // Conflict will be detected by the fact that the [lb, ub] of the min is
766 // empty. In case of conflict, we just need the reason for pushing UB + 1.
767 if (min_of_linear_expression_lb > current_min_ub) {
768 min_of_linear_expression_lb = current_min_ub + 1;
769 }
770 if (min_of_linear_expression_lb > integer_trail_.LowerBound(min_var_)) {
771 local_reason_.clear();
772 for (int i = 0; i < exprs_.size(); ++i) {
773 const IntegerValue slack = expr_lbs_[i] - min_of_linear_expression_lb;
774 integer_trail_.AppendRelaxedLinearReason(slack, exprs_[i].coeffs,
775 exprs_[i].vars, &local_reason_);
776 }
777 if (status == EnforcementStatus::IS_ENFORCED) {
778 if (!enforcement_helper_.SafeEnqueue(
779 enforcement_id_,
781 min_of_linear_expression_lb),
782 local_reason_)) {
783 return false;
784 }
785 } else if (min_of_linear_expression_lb > current_min_ub) {
786 // If the upper bound of min_var_ is strictly smaller than the lower bound
787 // of all the expressions, then the enforcement must be false.
788 local_reason_.push_back(
789 IntegerLiteral::LowerOrEqual(min_var_, current_min_ub));
790 return enforcement_helper_.PropagateWhenFalse(
791 enforcement_id_, /*literal_reason=*/{}, local_reason_);
792 }
793 }
794
795 // Propagation b) ub(min) >= ub(MIN(exprs)) and we can't propagate anything
796 // here unless there is just one possible expression 'e' that can be the min:
797 // for all u != e, lb(u) > ub(min);
798 // In this case, ub(min) >= ub(e).
799 if (num_intervals_that_can_be_min == 1) {
800 const IntegerValue ub_of_only_candidate =
801 exprs_[last_possible_min_interval].Max(integer_trail_);
802 if (current_min_ub < ub_of_only_candidate) {
803 // For this propagation, we only need to fill the integer reason once at
804 // the lowest level. At higher levels this reason still remains valid.
805 if (rev_unique_candidate_ == 0) {
806 integer_reason_for_unique_candidate_.clear();
807
808 // The reason is that all the other expressions start after
809 // current_min_ub. And that min_ub has its current value.
810 integer_reason_for_unique_candidate_.push_back(
811 integer_trail_.UpperBoundAsLiteral(min_var_));
812 for (int i = 0; i < exprs_.size(); ++i) {
813 if (i == last_possible_min_interval) continue;
814 const IntegerValue slack = expr_lbs_[i] - (current_min_ub + 1);
815 integer_trail_.AppendRelaxedLinearReason(
816 slack, exprs_[i].coeffs, exprs_[i].vars,
817 &integer_reason_for_unique_candidate_);
818 }
819 rev_unique_candidate_ = 1;
820 }
821
822 return PropagateLinearUpperBound(
823 last_possible_min_interval, exprs_[last_possible_min_interval].vars,
824 exprs_[last_possible_min_interval].coeffs,
825 current_min_ub - exprs_[last_possible_min_interval].offset);
826 }
827 }
828
829 return true;
830}
831
832int GreaterThanMinOfExprsPropagator::RegisterWith(
833 GenericLiteralWatcher* watcher) {
834 const int id = watcher->Register(this);
835 bool has_var_also_in_exprs = false;
836 for (const LinearExpression& expr : exprs_) {
837 for (int i = 0; i < expr.vars.size(); ++i) {
838 const IntegerVariable& var = expr.vars[i];
839 const IntegerValue coeff = expr.coeffs[i];
840 if (coeff > 0) {
841 watcher->WatchLowerBound(var, id);
842 } else {
843 watcher->WatchUpperBound(var, id);
844 }
845 has_var_also_in_exprs |= (var == min_var_);
846 }
847 }
848 watcher->WatchUpperBound(min_var_, id);
849 watcher->RegisterReversibleInt(id, &rev_unique_candidate_);
850 if (has_var_also_in_exprs) {
852 }
853 return id;
854}
855
857 absl::Span<const Literal> enforcement_literals, AffineExpression a,
859 : a_(a),
860 b_(b),
861 p_(p),
862 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
863 enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
865 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
866 RegisterWith(watcher));
867}
868
869// We want all affine expression to be either non-negative or across zero.
870bool ProductPropagator::CanonicalizeCases() {
871 if (integer_trail_.UpperBound(a_) <= 0) {
872 a_ = a_.Negated();
873 p_ = p_.Negated();
874 }
875 if (integer_trail_.UpperBound(b_) <= 0) {
876 b_ = b_.Negated();
877 p_ = p_.Negated();
878 }
879
880 // If both a and b positive, p must be too.
881 if (integer_trail_.LowerBound(a_) >= 0 &&
882 integer_trail_.LowerBound(b_) >= 0) {
883 return enforcement_helper_.SafeEnqueue(
884 enforcement_id_, p_.GreaterOrEqual(0),
885 {a_.GreaterOrEqual(0), b_.GreaterOrEqual(0)});
886 }
887
888 // Otherwise, make sure p is non-negative or across zero.
889 if (integer_trail_.UpperBound(p_) <= 0) {
890 if (integer_trail_.LowerBound(a_) < 0) {
891 DCHECK_GT(integer_trail_.UpperBound(a_), 0);
892 a_ = a_.Negated();
893 p_ = p_.Negated();
894 } else {
895 DCHECK_LT(integer_trail_.LowerBound(b_), 0);
896 DCHECK_GT(integer_trail_.UpperBound(b_), 0);
897 b_ = b_.Negated();
898 p_ = p_.Negated();
899 }
900 }
901
902 return true;
903}
904
905// Note that this propagation is exact, except on the domain of p as this
906// involves more complex arithmetic.
907//
908// TODO(user): We could tighten the bounds on p by removing extreme value that
909// do not contains divisor in the domains of a or b. There is an algo in O(
910// smallest domain size between a or b).
911bool ProductPropagator::PropagateWhenAllNonNegative() {
912 {
913 const IntegerValue max_a = integer_trail_.UpperBound(a_);
914 const IntegerValue max_b = integer_trail_.UpperBound(b_);
915 const IntegerValue new_max = CapProdI(max_a, max_b);
916 if (new_max < integer_trail_.UpperBound(p_)) {
917 if (!enforcement_helper_.SafeEnqueue(
918 enforcement_id_, p_.LowerOrEqual(new_max),
919 {integer_trail_.UpperBoundAsLiteral(a_),
920 integer_trail_.UpperBoundAsLiteral(b_), a_.GreaterOrEqual(0),
921 b_.GreaterOrEqual(0)})) {
922 return false;
923 }
924 }
925 }
926
927 {
928 const IntegerValue min_a = integer_trail_.LowerBound(a_);
929 const IntegerValue min_b = integer_trail_.LowerBound(b_);
930 const IntegerValue new_min = CapProdI(min_a, min_b);
931
932 // The conflict test is needed because when new_min is large, we could
933 // have an overflow in p_.GreaterOrEqual(new_min);
934 if (new_min > integer_trail_.UpperBound(p_)) {
935 return enforcement_helper_.ReportConflict(
936 enforcement_id_, {integer_trail_.UpperBoundAsLiteral(p_),
937 integer_trail_.LowerBoundAsLiteral(a_),
938 integer_trail_.LowerBoundAsLiteral(b_)});
939 }
940 if (new_min > integer_trail_.LowerBound(p_)) {
941 if (!enforcement_helper_.SafeEnqueue(
942 enforcement_id_, p_.GreaterOrEqual(new_min),
943 {integer_trail_.LowerBoundAsLiteral(a_),
944 integer_trail_.LowerBoundAsLiteral(b_)})) {
945 return false;
946 }
947 }
948 }
949
950 for (int i = 0; i < 2; ++i) {
951 const AffineExpression a = i == 0 ? a_ : b_;
952 const AffineExpression b = i == 0 ? b_ : a_;
953 const IntegerValue max_a = integer_trail_.UpperBound(a);
954 const IntegerValue min_b = integer_trail_.LowerBound(b);
955 const IntegerValue min_p = integer_trail_.LowerBound(p_);
956 const IntegerValue max_p = integer_trail_.UpperBound(p_);
957 const IntegerValue prod = CapProdI(max_a, min_b);
958 if (prod > max_p) {
959 if (!enforcement_helper_.SafeEnqueue(
960 enforcement_id_, a.LowerOrEqual(FloorRatio(max_p, min_b)),
961 {integer_trail_.LowerBoundAsLiteral(b),
962 integer_trail_.UpperBoundAsLiteral(p_), p_.GreaterOrEqual(0)})) {
963 return false;
964 }
965 } else if (prod < min_p && max_a != 0) {
966 if (!enforcement_helper_.SafeEnqueue(
967 enforcement_id_, b.GreaterOrEqual(CeilRatio(min_p, max_a)),
968 {integer_trail_.UpperBoundAsLiteral(a),
969 integer_trail_.LowerBoundAsLiteral(p_), a.GreaterOrEqual(0)})) {
970 return false;
971 }
972 }
973 }
974
975 return true;
976}
977
978// This assumes p > 0, p = a * X, and X can take any value.
979// We can propagate max of a by computing a bound on the min b when positive.
980// The expression b is just used to detect when there is no solution given the
981// upper bound of b.
982bool ProductPropagator::PropagateMaxOnPositiveProduct(AffineExpression a,
984 IntegerValue min_p,
985 IntegerValue max_p) {
986 const IntegerValue max_a = integer_trail_.UpperBound(a);
987 if (max_a <= 0) return true;
988 DCHECK_GT(min_p, 0);
989
990 if (max_a >= min_p) {
991 if (max_p < max_a) {
992 if (!enforcement_helper_.SafeEnqueue(
993 enforcement_id_, a.LowerOrEqual(max_p),
994 {p_.LowerOrEqual(max_p), p_.GreaterOrEqual(1)})) {
995 return false;
996 }
997 }
998 return true;
999 }
1000
1001 const IntegerValue min_pos_b = CeilRatio(min_p, max_a);
1002 if (min_pos_b > integer_trail_.UpperBound(b)) {
1003 if (!enforcement_helper_.SafeEnqueue(
1004 enforcement_id_, b.LowerOrEqual(0),
1005 {integer_trail_.LowerBoundAsLiteral(p_),
1006 integer_trail_.UpperBoundAsLiteral(a),
1007 integer_trail_.UpperBoundAsLiteral(b)})) {
1008 return false;
1009 }
1010 return true;
1011 }
1012
1013 const IntegerValue new_max_a = FloorRatio(max_p, min_pos_b);
1014 if (new_max_a < integer_trail_.UpperBound(a)) {
1015 if (!enforcement_helper_.SafeEnqueue(
1016 enforcement_id_, a.LowerOrEqual(new_max_a),
1017 {integer_trail_.LowerBoundAsLiteral(p_),
1018 integer_trail_.UpperBoundAsLiteral(a),
1019 integer_trail_.UpperBoundAsLiteral(p_)})) {
1020 return false;
1021 }
1022 }
1023 return true;
1024}
1025
1027 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
1029 const int64_t min_a = integer_trail_.LowerBound(a_).value();
1030 const int64_t max_a = integer_trail_.UpperBound(a_).value();
1031 const int64_t min_b = integer_trail_.LowerBound(b_).value();
1032 const int64_t max_b = integer_trail_.UpperBound(b_).value();
1033 const int64_t min_p = integer_trail_.LowerBound(p_).value();
1034 const int64_t max_p = integer_trail_.UpperBound(p_).value();
1035 const int64_t p1 = CapProdI(max_a, max_b).value();
1036 const int64_t p2 = CapProdI(max_a, min_b).value();
1037 const int64_t p3 = CapProdI(min_a, max_b).value();
1038 const int64_t p4 = CapProdI(min_a, min_b).value();
1039 const int64_t min_ab = std::min({p1, p2, p3, p4});
1040 const int64_t max_ab = std::max({p1, p2, p3, p4});
1041 // If the bounds of a * b and p are disjoint, the enforcement must be false.
1042 // TODO(user): relax the reason in a better way.
1043 if (min_ab > max_p) {
1044 return enforcement_helper_.PropagateWhenFalse(
1045 enforcement_id_,
1046 /*literal_reason=*/{},
1047 {a_.GreaterOrEqual(min_a), a_.LowerOrEqual(max_a),
1048 b_.GreaterOrEqual(min_b), b_.LowerOrEqual(max_b),
1049 p_.LowerOrEqual(max_p)});
1050 }
1051 if (min_p > max_ab) {
1052 return enforcement_helper_.PropagateWhenFalse(
1053 enforcement_id_,
1054 /*literal_reason=*/{},
1055 {a_.GreaterOrEqual(min_a), a_.LowerOrEqual(max_a),
1056 b_.GreaterOrEqual(min_b), b_.LowerOrEqual(max_b),
1057 p_.GreaterOrEqual(min_p)});
1058 }
1059 // Otherwise we cannot propagate anything since the enforcement is unknown.
1060 return true;
1061 }
1062
1063 if (status != EnforcementStatus::IS_ENFORCED) return true;
1064 if (!CanonicalizeCases()) return false;
1065
1066 // In the most common case, we use better reasons even though the code
1067 // below would propagate the same.
1068 const int64_t min_a = integer_trail_.LowerBound(a_).value();
1069 const int64_t min_b = integer_trail_.LowerBound(b_).value();
1070 if (min_a >= 0 && min_b >= 0) {
1071 // This was done by CanonicalizeCases().
1072 DCHECK_GE(integer_trail_.LowerBound(p_), 0);
1073 return PropagateWhenAllNonNegative();
1074 }
1075
1076 // Lets propagate on p_ first, the max/min is given by one of: max_a * max_b,
1077 // max_a * min_b, min_a * max_b, min_a * min_b. This is true, because any
1078 // product x * y, depending on the sign, is dominated by one of these.
1079 //
1080 // TODO(user): In the reasons, including all 4 bounds is always correct, but
1081 // we might be able to relax some of them.
1082 const IntegerValue max_a = integer_trail_.UpperBound(a_);
1083 const IntegerValue max_b = integer_trail_.UpperBound(b_);
1084 const IntegerValue p1 = CapProdI(max_a, max_b);
1085 const IntegerValue p2 = CapProdI(max_a, min_b);
1086 const IntegerValue p3 = CapProdI(min_a, max_b);
1087 const IntegerValue p4 = CapProdI(min_a, min_b);
1088 const IntegerValue new_max_p = std::max({p1, p2, p3, p4});
1089 if (new_max_p < integer_trail_.UpperBound(p_)) {
1090 if (!enforcement_helper_.SafeEnqueue(
1091 enforcement_id_, p_.LowerOrEqual(new_max_p),
1092 {integer_trail_.LowerBoundAsLiteral(a_),
1093 integer_trail_.LowerBoundAsLiteral(b_),
1094 integer_trail_.UpperBoundAsLiteral(a_),
1095 integer_trail_.UpperBoundAsLiteral(b_)})) {
1096 return false;
1097 }
1098 }
1099 const IntegerValue new_min_p = std::min({p1, p2, p3, p4});
1100 if (new_min_p > integer_trail_.LowerBound(p_)) {
1101 if (!enforcement_helper_.SafeEnqueue(
1102 enforcement_id_, p_.GreaterOrEqual(new_min_p),
1103 {integer_trail_.LowerBoundAsLiteral(a_),
1104 integer_trail_.LowerBoundAsLiteral(b_),
1105 integer_trail_.UpperBoundAsLiteral(a_),
1106 integer_trail_.UpperBoundAsLiteral(b_)})) {
1107 return false;
1108 }
1109 }
1110
1111 // Lets propagate on a and b.
1112 const IntegerValue min_p = integer_trail_.LowerBound(p_);
1113 const IntegerValue max_p = integer_trail_.UpperBound(p_);
1114
1115 // We need a bit more propagation to avoid bad cases below.
1116 const bool zero_is_possible = min_p <= 0;
1117 if (!zero_is_possible) {
1118 if (integer_trail_.LowerBound(a_) == 0) {
1119 if (!enforcement_helper_.SafeEnqueue(
1120 enforcement_id_, a_.GreaterOrEqual(1),
1121 {p_.GreaterOrEqual(1), a_.GreaterOrEqual(0)})) {
1122 return false;
1123 }
1124 }
1125 if (integer_trail_.LowerBound(b_) == 0) {
1126 if (!enforcement_helper_.SafeEnqueue(
1127 enforcement_id_, b_.GreaterOrEqual(1),
1128 {p_.GreaterOrEqual(1), b_.GreaterOrEqual(0)})) {
1129 return false;
1130 }
1131 }
1132 if (integer_trail_.LowerBound(a_) >= 0 &&
1133 integer_trail_.LowerBound(b_) <= 0) {
1134 return enforcement_helper_.SafeEnqueue(
1135 enforcement_id_, b_.GreaterOrEqual(1),
1136 {a_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1137 }
1138 if (integer_trail_.LowerBound(b_) >= 0 &&
1139 integer_trail_.LowerBound(a_) <= 0) {
1140 return enforcement_helper_.SafeEnqueue(
1141 enforcement_id_, a_.GreaterOrEqual(1),
1142 {b_.GreaterOrEqual(0), p_.GreaterOrEqual(1)});
1143 }
1144 }
1145
1146 for (int i = 0; i < 2; ++i) {
1147 // p = a * b, what is the min/max of a?
1148 const AffineExpression a = i == 0 ? a_ : b_;
1149 const AffineExpression b = i == 0 ? b_ : a_;
1150 const IntegerValue max_b = integer_trail_.UpperBound(b);
1151 const IntegerValue min_b = integer_trail_.LowerBound(b);
1152
1153 // If the domain of b contain zero, we can't propagate anything on a.
1154 // Because of CanonicalizeCases(), we just deal with min_b > 0 here.
1155 if (zero_is_possible && min_b <= 0) continue;
1156
1157 // Here both a and b are across zero, but zero is not possible.
1158 if (min_b < 0 && max_b > 0) {
1159 CHECK_GT(min_p, 0); // Because zero is not possible.
1160
1161 // If a is not across zero, we will deal with this on the next
1162 // Propagate() call.
1163 if (!PropagateMaxOnPositiveProduct(a, b, min_p, max_p)) {
1164 return false;
1165 }
1166 if (!PropagateMaxOnPositiveProduct(a.Negated(), b.Negated(), min_p,
1167 max_p)) {
1168 return false;
1169 }
1170 continue;
1171 }
1172
1173 // This shouldn't happen here.
1174 // If it does, we should reach the fixed point on the next iteration.
1175 if (min_b <= 0) continue;
1176 if (min_p >= 0) {
1177 return enforcement_helper_.SafeEnqueue(
1178 enforcement_id_, a.GreaterOrEqual(0),
1179 {p_.GreaterOrEqual(0), b.GreaterOrEqual(1)});
1180 }
1181 if (max_p <= 0) {
1182 return enforcement_helper_.SafeEnqueue(
1183 enforcement_id_, a.LowerOrEqual(0),
1184 {p_.LowerOrEqual(0), b.GreaterOrEqual(1)});
1185 }
1186
1187 // So min_b > 0 and p is across zero: min_p < 0 and max_p > 0.
1188 const IntegerValue new_max_a = FloorRatio(max_p, min_b);
1189 if (new_max_a < integer_trail_.UpperBound(a)) {
1190 if (!enforcement_helper_.SafeEnqueue(
1191 enforcement_id_, a.LowerOrEqual(new_max_a),
1192 {integer_trail_.UpperBoundAsLiteral(p_),
1193 integer_trail_.LowerBoundAsLiteral(b)})) {
1194 return false;
1195 }
1196 }
1197 const IntegerValue new_min_a = CeilRatio(min_p, min_b);
1198 if (new_min_a > integer_trail_.LowerBound(a)) {
1199 if (!enforcement_helper_.SafeEnqueue(
1200 enforcement_id_, a.GreaterOrEqual(new_min_a),
1201 {integer_trail_.LowerBoundAsLiteral(p_),
1202 integer_trail_.LowerBoundAsLiteral(b)})) {
1203 return false;
1204 }
1205 }
1206 }
1207
1208 return true;
1209}
1210
1211int ProductPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
1212 const int id = watcher->Register(this);
1213 watcher->WatchAffineExpression(a_, id);
1214 watcher->WatchAffineExpression(b_, id);
1215 watcher->WatchAffineExpression(p_, id);
1217 return id;
1218}
1219
1221 absl::Span<const Literal> enforcement_literals, AffineExpression x,
1222 AffineExpression s, Model* model)
1223 : x_(x),
1224 s_(s),
1225 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
1226 enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
1228 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
1229 RegisterWith(watcher));
1230 CHECK_GE(integer_trail_.LevelZeroLowerBound(x), 0);
1231}
1232
1233// Propagation from x to s: s in [min_x * min_x, max_x * max_x].
1234// Propagation from s to x: x in [ceil(sqrt(min_s)), floor(sqrt(max_s))].
1236 const IntegerValue min_x = integer_trail_.LowerBound(x_);
1237 const IntegerValue min_s = integer_trail_.LowerBound(s_);
1238 const IntegerValue min_x_square = CapProdI(min_x, min_x);
1239 const IntegerValue max_x = integer_trail_.UpperBound(x_);
1240 const IntegerValue max_s = integer_trail_.UpperBound(s_);
1241 const IntegerValue max_x_square = CapProdI(max_x, max_x);
1242
1243 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
1245 // If the bounds of x * x and s are disjoint, the enforcement must be false.
1246 // TODO(user): relax the reason in a better way.
1247 if (min_x_square > max_s) {
1248 return enforcement_helper_.PropagateWhenFalse(
1249 enforcement_id_,
1250 /*literal_reason=*/{},
1251 {x_.GreaterOrEqual(min_x), s_.LowerOrEqual(min_x_square - 1)});
1252 }
1253 if (min_s > max_x_square) {
1254 return enforcement_helper_.PropagateWhenFalse(
1255 enforcement_id_,
1256 /*literal_reason=*/{},
1257 {x_.LowerOrEqual(max_x), s_.GreaterOrEqual(max_x_square + 1)});
1258 }
1259 // Otherwise we cannot propagate anything since the enforcement is unknown.
1260 return true;
1261 }
1262
1263 if (status != EnforcementStatus::IS_ENFORCED) return true;
1264 if (min_x_square > min_s) {
1265 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1266 s_.GreaterOrEqual(min_x_square),
1267 {x_.GreaterOrEqual(min_x)})) {
1268 return false;
1269 }
1270 } else if (min_x_square < min_s) {
1271 const IntegerValue new_min(CeilSquareRoot(min_s.value()));
1272 if (!enforcement_helper_.SafeEnqueue(
1273 enforcement_id_, x_.GreaterOrEqual(new_min),
1274 {s_.GreaterOrEqual((new_min - 1) * (new_min - 1) + 1)})) {
1275 return false;
1276 }
1277 }
1278 if (max_x_square < max_s) {
1279 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1280 s_.LowerOrEqual(max_x_square),
1281 {x_.LowerOrEqual(max_x)})) {
1282 return false;
1283 }
1284 } else if (max_x_square > max_s) {
1285 const IntegerValue new_max(FloorSquareRoot(max_s.value()));
1286 if (!enforcement_helper_.SafeEnqueue(
1287 enforcement_id_, x_.LowerOrEqual(new_max),
1288 {s_.LowerOrEqual(CapProdI(new_max + 1, new_max + 1) - 1)})) {
1289 return false;
1290 }
1291 }
1292
1293 return true;
1294}
1295
1296int SquarePropagator::RegisterWith(GenericLiteralWatcher* watcher) {
1297 const int id = watcher->Register(this);
1298 watcher->WatchAffineExpression(x_, id);
1299 watcher->WatchAffineExpression(s_, id);
1301 return id;
1302}
1303
1305 absl::Span<const Literal> enforcement_literals, AffineExpression num,
1306 AffineExpression denom, AffineExpression div, Model* model)
1307 : num_(num),
1308 denom_(denom),
1309 div_(div),
1310 negated_div_(div.Negated()),
1311 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
1312 enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
1314 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
1315 RegisterWith(watcher));
1316}
1317
1318// TODO(user): We can propagate more, especially in the case where denom
1319// spans across 0.
1320// TODO(user): We can propagate a bit more if min_div = 0:
1321// (min_num > -min_denom).
1323 if (integer_trail_.LowerBound(denom_) < 0 &&
1324 integer_trail_.UpperBound(denom_) > 0) {
1325 return true;
1326 }
1327
1328 if (integer_trail_.UpperBound(denom_) < 0) {
1329 num_ = num_.Negated();
1330 denom_ = denom_.Negated();
1331 }
1332
1333 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
1335 const IntegerValue min_num = integer_trail_.LowerBound(num_);
1336 const IntegerValue max_num = integer_trail_.UpperBound(num_);
1337 const IntegerValue min_denom = integer_trail_.LowerBound(denom_);
1338 const IntegerValue max_denom = integer_trail_.UpperBound(denom_);
1339 const IntegerValue min_div = integer_trail_.LowerBound(div_);
1340 const IntegerValue max_div = integer_trail_.UpperBound(div_);
1341 // If the bounds of num / denom and div are disjoint, the enforcement must
1342 // be false. TODO(user): relax the reason in a better way.
1343 if (min_num / max_denom > max_div) {
1344 return enforcement_helper_.PropagateWhenFalse(
1345 enforcement_id_,
1346 /*literal_reason=*/{},
1347 {num_.GreaterOrEqual(min_num), denom_.LowerOrEqual(max_denom),
1348 div_.LowerOrEqual(max_div)});
1349 }
1350 if (max_num / min_denom < min_div) {
1351 return enforcement_helper_.PropagateWhenFalse(
1352 enforcement_id_,
1353 /*literal_reason=*/{},
1354 {num_.LowerOrEqual(max_num), denom_.GreaterOrEqual(min_denom),
1355 div_.GreaterOrEqual(min_div)});
1356 }
1357 // Otherwise we cannot propagate anything since the enforcement is unknown.
1358 return true;
1359 }
1360
1361 if (status != EnforcementStatus::IS_ENFORCED) return true;
1362 if (!PropagateSigns(num_, denom_, div_)) return false;
1363
1364 if (integer_trail_.UpperBound(num_) >= 0 &&
1365 integer_trail_.UpperBound(div_) >= 0 &&
1366 !PropagateUpperBounds(num_, denom_, div_)) {
1367 return false;
1368 }
1369
1370 if (integer_trail_.LowerBound(num_) <= 0 &&
1371 integer_trail_.UpperBound(negated_div_) >= 0 &&
1372 !PropagateUpperBounds(num_.Negated(), denom_, negated_div_)) {
1373 return false;
1374 }
1375
1376 if (integer_trail_.LowerBound(num_) >= 0 &&
1377 integer_trail_.LowerBound(div_) >= 0) {
1378 return PropagatePositiveDomains(num_, denom_, div_);
1379 }
1380
1381 if (integer_trail_.UpperBound(num_) <= 0 &&
1382 integer_trail_.LowerBound(negated_div_) >= 0) {
1383 return PropagatePositiveDomains(num_.Negated(), denom_, negated_div_);
1384 }
1385
1386 return true;
1387}
1388
1389bool DivisionPropagator::PropagateSigns(AffineExpression num,
1390 AffineExpression denom,
1391 AffineExpression div) {
1392 const IntegerValue min_num = integer_trail_.LowerBound(num);
1393 const IntegerValue max_num = integer_trail_.UpperBound(num);
1394 const IntegerValue min_div = integer_trail_.LowerBound(div);
1395 const IntegerValue max_div = integer_trail_.UpperBound(div);
1396
1397 // If num >= 0, as denom > 0, then div must be >= 0.
1398 if (min_num >= 0 && min_div < 0) {
1399 if (!enforcement_helper_.SafeEnqueue(
1400 enforcement_id_, div.GreaterOrEqual(0),
1401 {num.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1402 return false;
1403 }
1404 }
1405
1406 // If div > 0, as denom > 0, then num must be > 0.
1407 if (min_num <= 0 && min_div > 0) {
1408 if (!enforcement_helper_.SafeEnqueue(
1409 enforcement_id_, num.GreaterOrEqual(1),
1410 {div.GreaterOrEqual(1), denom.GreaterOrEqual(1)})) {
1411 return false;
1412 }
1413 }
1414
1415 // If num <= 0, as denom > 0, then div must be <= 0.
1416 if (max_num <= 0 && max_div > 0) {
1417 if (!enforcement_helper_.SafeEnqueue(
1418 enforcement_id_, div.LowerOrEqual(0),
1419 {num.LowerOrEqual(0), denom.GreaterOrEqual(1)})) {
1420 return false;
1421 }
1422 }
1423
1424 // If div < 0, as denom > 0, then num must be < 0.
1425 if (max_num >= 0 && max_div < 0) {
1426 if (!enforcement_helper_.SafeEnqueue(
1427 enforcement_id_, num.LowerOrEqual(-1),
1428 {div.LowerOrEqual(-1), denom.GreaterOrEqual(1)})) {
1429 return false;
1430 }
1431 }
1432
1433 return true;
1434}
1435
1436bool DivisionPropagator::PropagateUpperBounds(AffineExpression num,
1437 AffineExpression denom,
1438 AffineExpression div) {
1439 const IntegerValue max_num = integer_trail_.UpperBound(num);
1440 const IntegerValue min_denom = integer_trail_.LowerBound(denom);
1441 const IntegerValue max_denom = integer_trail_.UpperBound(denom);
1442 const IntegerValue max_div = integer_trail_.UpperBound(div);
1443
1444 const IntegerValue new_max_div = max_num / min_denom;
1445 if (max_div > new_max_div) {
1446 if (!enforcement_helper_.SafeEnqueue(
1447 enforcement_id_, div.LowerOrEqual(new_max_div),
1448 {num.LowerOrEqual(max_num), denom.GreaterOrEqual(min_denom)})) {
1449 return false;
1450 }
1451 }
1452
1453 // We start from num / denom <= max_div.
1454 // num < (max_div + 1) * denom
1455 // num + 1 <= (max_div + 1) * max_denom.
1456 const IntegerValue new_max_num =
1457 CapAddI(CapProdI(max_div + 1, max_denom), -1);
1458 if (max_num > new_max_num) {
1459 if (!enforcement_helper_.SafeEnqueue(
1460 enforcement_id_, num.LowerOrEqual(new_max_num),
1461 {denom.LowerOrEqual(max_denom), denom.GreaterOrEqual(1),
1462 div.LowerOrEqual(max_div)})) {
1463 return false;
1464 }
1465 }
1466
1467 return true;
1468}
1469
1470bool DivisionPropagator::PropagatePositiveDomains(AffineExpression num,
1471 AffineExpression denom,
1472 AffineExpression div) {
1473 const IntegerValue min_num = integer_trail_.LowerBound(num);
1474 const IntegerValue max_num = integer_trail_.UpperBound(num);
1475 const IntegerValue min_denom = integer_trail_.LowerBound(denom);
1476 const IntegerValue max_denom = integer_trail_.UpperBound(denom);
1477 const IntegerValue min_div = integer_trail_.LowerBound(div);
1478 const IntegerValue max_div = integer_trail_.UpperBound(div);
1479
1480 const IntegerValue new_min_div = min_num / max_denom;
1481 if (min_div < new_min_div) {
1482 if (!enforcement_helper_.SafeEnqueue(
1483 enforcement_id_, div.GreaterOrEqual(new_min_div),
1484 {num.GreaterOrEqual(min_num), denom.LowerOrEqual(max_denom),
1485 denom.GreaterOrEqual(1)})) {
1486 return false;
1487 }
1488 }
1489
1490 // We start from num / denom >= min_div.
1491 // num >= min_div * denom.
1492 // num >= min_div * min_denom.
1493 const IntegerValue new_min_num = CapProdI(min_denom, min_div);
1494 if (min_num < new_min_num) {
1495 if (!enforcement_helper_.SafeEnqueue(
1496 enforcement_id_, num.GreaterOrEqual(new_min_num),
1497 {denom.GreaterOrEqual(min_denom), div.GreaterOrEqual(min_div)})) {
1498 return false;
1499 }
1500 }
1501
1502 // We start with num / denom >= min_div.
1503 // So num >= min_div * denom
1504 // If min_div == 0 we can't deduce anything.
1505 // Otherwise, denom <= num / min_div and denom <= max_num / min_div.
1506 if (min_div > 0) {
1507 const IntegerValue new_max_denom = max_num / min_div;
1508 if (max_denom > new_max_denom) {
1509 if (!enforcement_helper_.SafeEnqueue(
1510 enforcement_id_, denom.LowerOrEqual(new_max_denom),
1511 {num.LowerOrEqual(max_num), num.GreaterOrEqual(0),
1512 div.GreaterOrEqual(min_div), denom.GreaterOrEqual(1)})) {
1513 return false;
1514 }
1515 }
1516 }
1517
1518 // denom >= CeilRatio(num + 1, max_div + 1)
1519 // >= CeilRatio(min_num + 1, max_div + 1).
1520 const IntegerValue new_min_denom = CeilRatio(min_num + 1, max_div + 1);
1521 if (min_denom < new_min_denom) {
1522 if (!enforcement_helper_.SafeEnqueue(
1523 enforcement_id_, denom.GreaterOrEqual(new_min_denom),
1524 {num.GreaterOrEqual(min_num), div.LowerOrEqual(max_div),
1525 div.GreaterOrEqual(0), denom.GreaterOrEqual(1)})) {
1526 return false;
1527 }
1528 }
1529
1530 return true;
1531}
1532
1533int DivisionPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
1534 const int id = watcher->Register(this);
1535 watcher->WatchAffineExpression(num_, id);
1536 watcher->WatchAffineExpression(denom_, id);
1537 watcher->WatchAffineExpression(div_, id);
1538 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(id);
1539 return id;
1540}
1541
1543 absl::Span<const Literal> enforcement_literals, AffineExpression a,
1544 IntegerValue b, AffineExpression c, Model* model)
1545 : a_(a),
1546 b_(b),
1547 c_(c),
1548 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
1549 enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
1551 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
1552 RegisterWith(watcher));
1553 CHECK_GT(b_, 0);
1554}
1555
1557 const IntegerValue min_a = integer_trail_.LowerBound(a_);
1558 const IntegerValue max_a = integer_trail_.UpperBound(a_);
1559 IntegerValue min_c = integer_trail_.LowerBound(c_);
1560 IntegerValue max_c = integer_trail_.UpperBound(c_);
1561
1562 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
1564 // If the bounds of a / b and c are disjoint, the enforcement must be false.
1565 // TODO(user): relax the reason in a better way.
1566 if (min_a / b_ > max_c) {
1567 return enforcement_helper_.PropagateWhenFalse(
1568 enforcement_id_,
1569 /*literal_reason=*/{},
1570 {a_.GreaterOrEqual(max_c * b_ + 1), c_.LowerOrEqual(max_c)});
1571 }
1572 if (max_a / b_ < min_c) {
1573 return enforcement_helper_.PropagateWhenFalse(
1574 enforcement_id_,
1575 /*literal_reason=*/{},
1576 {a_.LowerOrEqual(min_c * b_ - 1), c_.GreaterOrEqual(min_c)});
1577 }
1578 // Otherwise we cannot propagate anything since the enforcement is unknown.
1579 return true;
1580 }
1581
1582 if (status != EnforcementStatus::IS_ENFORCED) return true;
1583 if (max_a / b_ < max_c) {
1584 max_c = max_a / b_;
1585 if (!enforcement_helper_.SafeEnqueue(
1586 enforcement_id_, c_.LowerOrEqual(max_c),
1587 {integer_trail_.UpperBoundAsLiteral(a_)})) {
1588 return false;
1589 }
1590 } else if (max_a / b_ > max_c) {
1591 const IntegerValue new_max_a =
1592 max_c >= 0 ? max_c * b_ + b_ - 1 : CapProdI(max_c, b_);
1593 CHECK_LT(new_max_a, max_a);
1594 if (!enforcement_helper_.SafeEnqueue(
1595 enforcement_id_, a_.LowerOrEqual(new_max_a),
1596 {integer_trail_.UpperBoundAsLiteral(c_)})) {
1597 return false;
1598 }
1599 }
1600
1601 if (min_a / b_ > min_c) {
1602 min_c = min_a / b_;
1603 if (!enforcement_helper_.SafeEnqueue(
1604 enforcement_id_, c_.GreaterOrEqual(min_c),
1605 {integer_trail_.LowerBoundAsLiteral(a_)})) {
1606 return false;
1607 }
1608 } else if (min_a / b_ < min_c) {
1609 const IntegerValue new_min_a =
1610 min_c > 0 ? CapProdI(min_c, b_) : min_c * b_ - b_ + 1;
1611 CHECK_GT(new_min_a, min_a);
1612 if (!enforcement_helper_.SafeEnqueue(
1613 enforcement_id_, a_.GreaterOrEqual(new_min_a),
1614 {integer_trail_.LowerBoundAsLiteral(c_)})) {
1615 return false;
1616 }
1617 }
1618
1619 return true;
1620}
1621
1622int FixedDivisionPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
1623 const int id = watcher->Register(this);
1624 watcher->WatchAffineExpression(a_, id);
1625 watcher->WatchAffineExpression(c_, id);
1626 return id;
1627}
1628
1630 absl::Span<const Literal> enforcement_literals, AffineExpression expr,
1631 IntegerValue mod, AffineExpression target, Model* model)
1632 : expr_(expr),
1633 mod_(mod),
1634 target_(target),
1635 negated_expr_(expr.Negated()),
1636 negated_target_(target.Negated()),
1637 integer_trail_(*model->GetOrCreate<IntegerTrail>()),
1638 enforcement_helper_(*model->GetOrCreate<EnforcementHelper>()) {
1639 CHECK_GT(mod_, 0);
1641 enforcement_id_ = enforcement_helper_.Register(enforcement_literals, watcher,
1642 RegisterWith(watcher));
1643}
1644
1646 const EnforcementStatus status = enforcement_helper_.Status(enforcement_id_);
1648 const IntegerValue min_target = integer_trail_.LowerBound(target_);
1649 const IntegerValue max_target = integer_trail_.UpperBound(target_);
1650 if (min_target >= mod_) {
1651 return enforcement_helper_.PropagateWhenFalse(
1652 enforcement_id_, /*literal_reason=*/{},
1653 {target_.GreaterOrEqual(mod_)});
1654 } else if (max_target <= -mod_) {
1655 return enforcement_helper_.PropagateWhenFalse(
1656 enforcement_id_, /*literal_reason=*/{},
1657 {target_.LowerOrEqual(-mod_)});
1658 }
1659 if (min_target > 0) {
1660 if (!PropagateWhenFalseAndTargetIsPositive(expr_, target_)) return false;
1661 } else if (max_target < 0) {
1662 if (!PropagateWhenFalseAndTargetIsPositive(negated_expr_,
1663 negated_target_)) {
1664 return false;
1665 }
1666 } else if (!PropagateWhenFalseAndTargetDomainContainsZero()) {
1667 return false;
1668 }
1669 // Otherwise we cannot propagate anything since the enforcement is unknown.
1670 return true;
1671 }
1672
1673 if (status != EnforcementStatus::IS_ENFORCED) return true;
1674 if (!PropagateSignsAndTargetRange()) return false;
1675 if (!PropagateOuterBounds()) return false;
1676
1677 if (integer_trail_.LowerBound(expr_) >= 0) {
1678 if (!PropagateBoundsWhenExprIsNonNegative(expr_, target_)) return false;
1679 } else if (integer_trail_.UpperBound(expr_) <= 0) {
1680 if (!PropagateBoundsWhenExprIsNonNegative(negated_expr_, negated_target_)) {
1681 return false;
1682 }
1683 }
1684
1685 return true;
1686}
1687
1688bool FixedModuloPropagator::PropagateWhenFalseAndTargetIsPositive(
1689 AffineExpression expr, AffineExpression target) {
1690 const IntegerValue min_expr = integer_trail_.LowerBound(expr);
1691 const IntegerValue max_expr = integer_trail_.UpperBound(expr);
1692
1693 // expr % mod_ must be in the target domain intersected with [0, mod_ - 1],
1694 // noted [min_expr_mod, max_expr_mod]. This interval is non-empty.
1695 const IntegerValue min_expr_mod =
1696 std::max(IntegerValue(0), integer_trail_.LowerBound(target));
1697 const IntegerValue max_expr_mod =
1698 std::min(mod_ - 1, integer_trail_.UpperBound(target));
1699 // expr must be in [min_expr_mod + k * mod_, max_expr_mod + k * mod_], for
1700 // some k >= 0. If the expr domain is in one of the following intervals, the
1701 // constraint is always false:
1702 // - ]-infinity, min_expr_mod[
1703 // - ]max_expr_mod + k * mod_ , min_expr_mod + (k + 1) * mod_[
1704 if (max_expr < min_expr_mod) {
1705 // TODO(user): relax the reason in a better way.
1706 return enforcement_helper_.PropagateWhenFalse(
1707 enforcement_id_, /*literal_reason=*/{},
1708 {expr.LowerOrEqual(min_expr_mod - 1),
1709 target.GreaterOrEqual(min_expr_mod)});
1710 }
1711 // Compute the smallest k such that max_expr < min_expr_mod + (k + 1) * mod_.
1712 const IntegerValue k = MathUtil::FloorOfRatio(max_expr - min_expr_mod, mod_);
1713 if (min_expr > max_expr_mod + k * mod_) {
1714 // TODO(user): relax the reason in a better way.
1715 return enforcement_helper_.PropagateWhenFalse(
1716 enforcement_id_, /*literal_reason=*/{},
1717 {expr.GreaterOrEqual(max_expr_mod + k * mod_ + 1),
1718 expr.LowerOrEqual(min_expr_mod + (k + 1) * mod_ - 1),
1719 target.GreaterOrEqual(min_expr_mod),
1720 target.LowerOrEqual(
1721 max_expr_mod < mod_ - 1
1722 ? max_expr_mod
1723 : integer_trail_.LevelZeroUpperBound(target))});
1724 }
1725 return true;
1726}
1727
1728bool FixedModuloPropagator::PropagateWhenFalseAndTargetDomainContainsZero() {
1729 const IntegerValue neg_max_expr_mod =
1730 std::max(-mod_ + 1, integer_trail_.LowerBound(target_));
1731 const IntegerValue pos_max_expr_mod =
1732 std::min(mod_ - 1, integer_trail_.UpperBound(target_));
1733 // expr must be in [k * mod_, pos_max_expr_mod + k * mod_] or in
1734 // [neg_max_expr_mod - k * mod_, -k * mod_] for some k >= 0. If the expr
1735 // domain is in one of the following intervals, the constraint is always
1736 // false:
1737 // - ]-(k + 1) * mod_, neg_max_expr_mod - k * mod_[
1738 // - ]pos_max_expr_mod + k * mod_ , (k + 1) * mod_[
1739 const IntegerValue min_expr = integer_trail_.LowerBound(expr_);
1740 const IntegerValue max_expr = integer_trail_.UpperBound(expr_);
1741 // Compute the smallest k such that max_expr < (k + 1) * mod_.
1742 IntegerValue k = MathUtil::FloorOfRatio(max_expr, mod_);
1743 if (k >= 0 && min_expr > pos_max_expr_mod + k * mod_) {
1744 const IntegerValue min_target = integer_trail_.LowerBound(target_);
1745 // TODO(user): relax the reason in a better way.
1746 return enforcement_helper_.PropagateWhenFalse(
1747 enforcement_id_, /*literal_reason=*/{},
1748 {expr_.GreaterOrEqual(pos_max_expr_mod + k * mod_ + 1),
1749 expr_.LowerOrEqual((k + 1) * mod_ - 1),
1750 target_.GreaterOrEqual(min_target),
1751 target_.LowerOrEqual(pos_max_expr_mod)});
1752 }
1753 // Compute the smallest k such that min_expr > -(k + 1) * mod_.
1754 k = MathUtil::FloorOfRatio(-min_expr, mod_);
1755 if (k >= 0 && max_expr < neg_max_expr_mod - k * mod_) {
1756 const IntegerValue max_target = integer_trail_.UpperBound(target_);
1757 // TODO(user): relax the reason in a better way.
1758 return enforcement_helper_.PropagateWhenFalse(
1759 enforcement_id_, /*literal_reason=*/{},
1760 {expr_.GreaterOrEqual(-(k + 1) * mod_ + 1),
1761 expr_.LowerOrEqual(neg_max_expr_mod - k * mod_ - 1),
1762 target_.GreaterOrEqual(neg_max_expr_mod),
1763 target_.LowerOrEqual(max_target)});
1764 }
1765 return true;
1766}
1767
1768bool FixedModuloPropagator::PropagateSignsAndTargetRange() {
1769 // Initial domain reduction on the target.
1770 if (integer_trail_.UpperBound(target_) >= mod_) {
1771 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1772 target_.LowerOrEqual(mod_ - 1), {})) {
1773 return false;
1774 }
1775 }
1776
1777 if (integer_trail_.LowerBound(target_) <= -mod_) {
1778 if (!enforcement_helper_.SafeEnqueue(
1779 enforcement_id_, target_.GreaterOrEqual(1 - mod_), {})) {
1780 return false;
1781 }
1782 }
1783
1784 // The sign of target_ is fixed by the sign of expr_.
1785 if (integer_trail_.LowerBound(expr_) >= 0 &&
1786 integer_trail_.LowerBound(target_) < 0) {
1787 // expr >= 0 => target >= 0.
1788 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1789 target_.GreaterOrEqual(0),
1790 {expr_.GreaterOrEqual(0)})) {
1791 return false;
1792 }
1793 }
1794
1795 if (integer_trail_.UpperBound(expr_) <= 0 &&
1796 integer_trail_.UpperBound(target_) > 0) {
1797 // expr <= 0 => target <= 0.
1798 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1799 target_.LowerOrEqual(0),
1800 {expr_.LowerOrEqual(0)})) {
1801 return false;
1802 }
1803 }
1804
1805 if (integer_trail_.LowerBound(target_) > 0 &&
1806 integer_trail_.LowerBound(expr_) <= 0) {
1807 // target > 0 => expr > 0.
1808 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1809 expr_.GreaterOrEqual(1),
1810 {target_.GreaterOrEqual(1)})) {
1811 return false;
1812 }
1813 }
1814
1815 if (integer_trail_.UpperBound(target_) < 0 &&
1816 integer_trail_.UpperBound(expr_) >= 0) {
1817 // target < 0 => expr < 0.
1818 if (!enforcement_helper_.SafeEnqueue(enforcement_id_,
1819 expr_.LowerOrEqual(-1),
1820 {target_.LowerOrEqual(-1)})) {
1821 return false;
1822 }
1823 }
1824
1825 return true;
1826}
1827
1828bool FixedModuloPropagator::PropagateOuterBounds() {
1829 const IntegerValue min_expr = integer_trail_.LowerBound(expr_);
1830 const IntegerValue max_expr = integer_trail_.UpperBound(expr_);
1831 const IntegerValue min_target = integer_trail_.LowerBound(target_);
1832 const IntegerValue max_target = integer_trail_.UpperBound(target_);
1833
1834 if (max_expr % mod_ > max_target) {
1835 if (!enforcement_helper_.SafeEnqueue(
1836 enforcement_id_,
1837 expr_.LowerOrEqual((max_expr / mod_) * mod_ + max_target),
1838 {integer_trail_.UpperBoundAsLiteral(target_),
1839 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1840 return false;
1841 }
1842 }
1843
1844 if (min_expr % mod_ < min_target) {
1845 if (!enforcement_helper_.SafeEnqueue(
1846 enforcement_id_,
1847 expr_.GreaterOrEqual((min_expr / mod_) * mod_ + min_target),
1848 {integer_trail_.LowerBoundAsLiteral(expr_),
1849 integer_trail_.LowerBoundAsLiteral(target_)})) {
1850 return false;
1851 }
1852 }
1853
1854 if (min_expr / mod_ == max_expr / mod_) {
1855 if (min_target < min_expr % mod_) {
1856 if (!enforcement_helper_.SafeEnqueue(
1857 enforcement_id_,
1858 target_.GreaterOrEqual(min_expr - (min_expr / mod_) * mod_),
1859 {integer_trail_.LowerBoundAsLiteral(target_),
1860 integer_trail_.UpperBoundAsLiteral(target_),
1861 integer_trail_.LowerBoundAsLiteral(expr_),
1862 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1863 return false;
1864 }
1865 }
1866
1867 if (max_target > max_expr % mod_) {
1868 if (!enforcement_helper_.SafeEnqueue(
1869 enforcement_id_,
1870 target_.LowerOrEqual(max_expr - (max_expr / mod_) * mod_),
1871 {integer_trail_.LowerBoundAsLiteral(target_),
1872 integer_trail_.UpperBoundAsLiteral(target_),
1873 integer_trail_.LowerBoundAsLiteral(expr_),
1874 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1875 return false;
1876 }
1877 }
1878 } else if (min_expr / mod_ == 0 && min_target < 0) {
1879 // expr == target when expr <= 0.
1880 if (min_target < min_expr) {
1881 if (!enforcement_helper_.SafeEnqueue(
1882 enforcement_id_, target_.GreaterOrEqual(min_expr),
1883 {integer_trail_.LowerBoundAsLiteral(target_),
1884 integer_trail_.LowerBoundAsLiteral(expr_)})) {
1885 return false;
1886 }
1887 }
1888 } else if (max_expr / mod_ == 0 && max_target > 0) {
1889 // expr == target when expr >= 0.
1890 if (max_target > max_expr) {
1891 if (!enforcement_helper_.SafeEnqueue(
1892 enforcement_id_, target_.LowerOrEqual(max_expr),
1893 {integer_trail_.UpperBoundAsLiteral(target_),
1894 integer_trail_.UpperBoundAsLiteral(expr_)})) {
1895 return false;
1896 }
1897 }
1898 }
1899
1900 return true;
1901}
1902
1903bool FixedModuloPropagator::PropagateBoundsWhenExprIsNonNegative(
1904 AffineExpression expr, AffineExpression target) {
1905 const IntegerValue min_target = integer_trail_.LowerBound(target);
1906 DCHECK_GE(min_target, 0);
1907 const IntegerValue max_target = integer_trail_.UpperBound(target);
1908
1909 // The propagation rules below will not be triggered if the domain of target
1910 // covers [0..mod_ - 1].
1911 if (min_target == 0 && max_target == mod_ - 1) return true;
1912
1913 const IntegerValue min_expr = integer_trail_.LowerBound(expr);
1914 const IntegerValue max_expr = integer_trail_.UpperBound(expr);
1915
1916 if (max_expr % mod_ < min_target) {
1917 DCHECK_GE(max_expr, 0);
1918 if (!enforcement_helper_.SafeEnqueue(
1919 enforcement_id_,
1920 expr.LowerOrEqual((max_expr / mod_ - 1) * mod_ + max_target),
1921 {integer_trail_.UpperBoundAsLiteral(expr),
1922 integer_trail_.LowerBoundAsLiteral(target),
1923 integer_trail_.UpperBoundAsLiteral(target)})) {
1924 return false;
1925 }
1926 }
1927
1928 if (min_expr % mod_ > max_target) {
1929 DCHECK_GE(min_expr, 0);
1930 if (!enforcement_helper_.SafeEnqueue(
1931 enforcement_id_,
1932 expr.GreaterOrEqual((min_expr / mod_ + 1) * mod_ + min_target),
1933 {integer_trail_.LowerBoundAsLiteral(target),
1934 integer_trail_.UpperBoundAsLiteral(target),
1935 integer_trail_.LowerBoundAsLiteral(expr)})) {
1936 return false;
1937 }
1938 }
1939
1940 return true;
1941}
1942
1943int FixedModuloPropagator::RegisterWith(GenericLiteralWatcher* watcher) {
1944 const int id = watcher->Register(this);
1945 watcher->WatchAffineExpression(expr_, id);
1946 watcher->WatchAffineExpression(target_, id);
1947 watcher->NotifyThatPropagatorMayNotReachFixedPointInOnePass(id);
1948 return id;
1949}
1950
1951} // namespace sat
1952} // namespace operations_research
static int64_t GCD64(int64_t x, int64_t y)
Definition mathutil.h:107
static IntegralType FloorOfRatio(IntegralType numerator, IntegralType denominator)
Definition mathutil.h:53
DivisionPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression num, AffineExpression denom, AffineExpression div, Model *model)
ABSL_MUST_USE_RESULT bool PropagateWhenFalse(EnforcementId id, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
EnforcementStatus Status(EnforcementId id) const
ABSL_MUST_USE_RESULT bool SafeEnqueue(EnforcementId id, IntegerLiteral i_lit, absl::Span< const IntegerLiteral > integer_reason)
FixedDivisionPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression a, IntegerValue b, AffineExpression c, Model *model)
FixedModuloPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression expr, IntegerValue mod, AffineExpression target, Model *model)
void WatchAffineExpression(AffineExpression e, int id)
Definition integer.h:1382
void WatchLiteral(Literal l, int id, int watch_index=-1)
Definition integer.h:1708
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
Definition integer.h:1734
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
Definition integer.h:1716
int Register(PropagatorInterface *propagator)
Definition integer.cc:2674
GreaterThanMinOfExprsPropagator(absl::Span< const Literal > enforcement_literals, std::vector< LinearExpression > exprs, IntegerVariable min_var, Model *model)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
IntegerValue LowerBound(IntegerVariable i) const
Definition integer.h:1537
bool VariableLowerBoundIsFromLevelZero(IntegerVariable var) const
Definition integer.h:931
IntegerValue UpperBound(IntegerVariable i) const
Definition integer.h:1541
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
Definition integer.h:1580
LevelZeroEquality(IntegerVariable target, const std::vector< IntegerVariable > &vars, const std::vector< IntegerValue > &coeffs, Model *model)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
std::pair< IntegerValue, IntegerValue > ConditionalLb(IntegerLiteral integer_literal, IntegerVariable target_var) const
void RegisterWith(GenericLiteralWatcher *watcher)
LinearConstraintPropagator(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coeffs, IntegerValue upper_bound, Model *model)
LiteralIndex Index() const
Definition sat_base.h:92
void RegisterWith(GenericLiteralWatcher *watcher)
MinPropagator(std::vector< AffineExpression > vars, AffineExpression min_var, IntegerTrail *integer_trail)
ProductPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression a, AffineExpression b, AffineExpression p, Model *model)
SquarePropagator(absl::Span< const Literal > enforcement_literals, AffineExpression x, AffineExpression s, Model *model)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
const LiteralIndex kNoLiteralIndex(-1)
int64_t FloorSquareRoot(int64_t a)
Definition util.cc:289
int64_t CeilSquareRoot(int64_t a)
Definition util.cc:298
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Definition integer.cc:52
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
IntegerVariable PositiveVariable(IntegerVariable i)
IntegerValue PositiveRemainder(IntegerValue dividend, IntegerValue positive_divisor)
IntegerValue CapAddI(IntegerValue a, IntegerValue b)
IntegerValue CapProdI(IntegerValue a, IntegerValue b)
OR-Tools root namespace.
STL namespace.
static int input(yyscan_t yyscanner)
IntegerLiteral GreaterOrEqual(IntegerValue bound) const
IntegerLiteral LowerOrEqual(IntegerValue bound) const
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const Literal > boolean_literals_at_false
Definition integer.h:441
absl::Span< const IntegerValue > coeffs
Definition integer.h:457
absl::Span< const Literal > boolean_literals_at_true
Definition integer.h:440
absl::Span< const IntegerVariable > vars
Definition integer.h:456
absl::Span< const IntegerLiteral > integer_literals
Definition integer.h:442