Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
integer_expr.h
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
14#ifndef ORTOOLS_SAT_INTEGER_EXPR_H_
15#define ORTOOLS_SAT_INTEGER_EXPR_H_
16
17#include <cmath>
18#include <cstdint>
19#include <cstdlib>
20#include <functional>
21#include <memory>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/base/attributes.h"
27#include "absl/log/check.h"
28#include "absl/log/log.h"
29#include "absl/types/span.h"
32#include "ortools/sat/integer.h"
36#include "ortools/sat/model.h"
43
44namespace operations_research {
45namespace sat {
46
47// A really basic implementation of an upper-bounded sum of integer variables.
48// The complexity is in O(num_variables) at each propagation.
49//
50// Note that we assume that there can be NO integer overflow. This must be
51// checked at model validation time before this is even created. If
52// use_int128 is true, then we actually do the computations that could overflow
53// in 128 bits, so that we can deal with constraints that might overflow (like
54// the one scaled from the LP relaxation). Note that we still use some
55// preconditions, such that for each variable the difference between their
56// bounds fit on an int64_t.
57//
58// TODO(user): Technically we could still have an int128 overflow since we
59// sum n terms that cannot overflow but can still be pretty close to the limit.
60// Make sure this never happens! For most problem though, because the variable
61// bounds will be smaller than 10^9, we are pretty safe.
62//
63// TODO(user): If one has many such constraint, it will be more efficient to
64// propagate all of them at once rather than doing it one at the time.
65//
66// TODO(user): Explore tree structure to get a log(n) complexity.
67//
68// TODO(user): When the variables are Boolean, use directly the pseudo-Boolean
69// constraint implementation. But we do need support for enforcement literals
70// there.
71template <bool use_int128 = false>
74 public:
75 // If refied_literal is kNoLiteralIndex then this is a normal constraint,
76 // otherwise we enforce the implication refied_literal => constraint is true.
77 // Note that we don't do the reverse implication here, it is usually done by
78 // another LinearConstraintPropagator constraint on the negated variables.
79 LinearConstraintPropagator(absl::Span<const Literal> enforcement_literals,
80 absl::Span<const IntegerVariable> vars,
81 absl::Span<const IntegerValue> coeffs,
82 IntegerValue upper_bound, Model* model);
83
84 // This version allow to std::move the memory from the LinearConstraint
85 // directly. It Only uses the upper bound. Id does not support
86 // enforcement_literals.
88
89 std::string LazyReasonName() const override {
90 return use_int128 ? "IntegerSumLE128" : "IntegerSumLE";
91 }
92
93 // We propagate:
94 // - If the sum of the individual lower-bound is > upper_bound, we fail.
95 // - For all i, upper-bound of i
96 // <= upper_bound - Sum {individual lower-bound excluding i).
97 bool Propagate() final;
99
100 // Same as Propagate() but only consider current root level bounds. This is
101 // mainly useful for the LP propagator since it can find relevant optimal
102 // really late in the search tree.
104
105 // This is a pretty usage specific function. Returns the implied lower bound
106 // on target_var if the given integer literal is false (resp. true). If the
107 // variables do not appear both in the linear inequality, this returns two
108 // kMinIntegerValue.
109 std::pair<IntegerValue, IntegerValue> ConditionalLb(
110 IntegerLiteral integer_literal, IntegerVariable target_var) const;
111
112 // For LazyReasonInterface.
113 void Explain(int id, IntegerLiteral to_explain, IntegerReason* reason) final;
114
115 private:
116 // Fills integer_reason_ with all the current lower_bounds. The real
117 // explanation may require removing one of them, but as an optimization, we
118 // always keep all the IntegerLiteral in integer_reason_, and swap them as
119 // needed just before pushing something.
120 void FillIntegerReason();
121
122 const IntegerValue upper_bound_;
123
124 // To gain a bit on memory (since we might have many linear constraint),
125 // we share this amongst all of them. Note that this is not accessed by
126 // two different thread though. Also the vector are only used as "temporary"
127 // so they are okay to be shared.
128 struct Shared {
129 explicit Shared(Model* model)
130 : assignment(model->GetOrCreate<Trail>()->Assignment()),
131 integer_trail(model->GetOrCreate<IntegerTrail>()),
132 time_limit(model->GetOrCreate<TimeLimit>()),
133 rev_int_repository(model->GetOrCreate<RevIntRepository>()),
134 rev_integer_value_repository(
136
137 const VariablesAssignment& assignment;
138 IntegerTrail* integer_trail;
139 TimeLimit* time_limit;
140 RevIntRepository* rev_int_repository;
141 RevIntegerValueRepository* rev_integer_value_repository;
142
143 // Parallel vectors.
144 std::vector<IntegerLiteral> integer_reason;
145 std::vector<IntegerValue> reason_coeffs;
146 };
147 Shared* shared_;
148
149 // Reversible sum of the lower bound of the fixed variables.
150 bool is_registered_ = false;
151 IntegerValue rev_lb_fixed_vars_;
152
153 // Reversible number of fixed variables.
154 int rev_num_fixed_vars_;
155
156 // Those vectors are shuffled during search to ensure that the variables
157 // (resp. coefficients) contained in the range [0, rev_num_fixed_vars_) of
158 // vars_ (resp. coeffs_) are fixed (resp. belong to fixed variables).
159 const int size_;
160 const std::unique_ptr<IntegerVariable[]> vars_;
161 const std::unique_ptr<IntegerValue[]> coeffs_;
162 const std::unique_ptr<IntegerValue[]> max_variations_;
163
164 // This is just the negation of the enforcement literal and it never changes.
165 std::vector<Literal> literal_reason_;
166};
167
170
171// Explicit instantiations in integer_expr.cc.
172extern template class LinearConstraintPropagator<true>;
173extern template class LinearConstraintPropagator<false>;
174
175// This assumes target = SUM_i coeffs[i] * vars[i], and detects that the target
176// must be of the form (a*X + b).
177//
178// This propagator is quite specific and runs only at level zero. For now, this
179// is mainly used for the objective variable. As we fix terms with high
180// objective coefficient, it is possible the only terms left have a common
181// divisor. This close app2-2.mps in less than a second instead of running
182// forever to prove the optimal (in single thread).
184 public:
185 LevelZeroEquality(IntegerVariable target,
186 const std::vector<IntegerVariable>& vars,
187 const std::vector<IntegerValue>& coeffs, Model* model);
188
189 bool Propagate() final;
190
191 private:
192 const IntegerVariable target_;
193 const std::vector<IntegerVariable> vars_;
194 const std::vector<IntegerValue> coeffs_;
195
196 IntegerValue gcd_ = IntegerValue(1);
197
198 Trail* trail_;
199 IntegerTrail* integer_trail_;
200};
201
202// A min (resp max) constraint of the form min == MIN(vars) can be decomposed
203// into two inequalities:
204// 1/ min <= MIN(vars), which is the same as for all v in vars, "min <= v".
205// This can be taken care of by the LowerOrEqual(min, v) constraint.
206// 2/ min >= MIN(vars).
207//
208// And in turn, 2/ can be decomposed in:
209// a) lb(min) >= lb(MIN(vars)) = MIN(lb(var));
210// b) ub(min) >= ub(MIN(vars)) and we can't propagate anything here unless
211// there is just one possible variable 'v' that can be the min:
212// for all u != v, lb(u) > ub(min);
213// In this case, ub(min) >= ub(v).
214//
215// This constraint take care of a) and b). That is:
216// - If the min of the lower bound of the vars increase, then the lower bound of
217// the min_var will be >= to it.
218// - If there is only one candidate for the min, then if the ub(min) decrease,
219// the ub of the only candidate will be <= to it.
220//
221// Complexity: This is a basic implementation in O(num_vars) on each call to
222// Propagate(), which will happen each time one or more variables in vars_
223// changed.
224//
225// TODO(user): Implement a more efficient algorithm when the need arise.
227 public:
228 MinPropagator(std::vector<AffineExpression> vars, AffineExpression min_var,
229 IntegerTrail* integer_trail);
230
231 // This type is neither copyable nor movable.
232 MinPropagator(const MinPropagator&) = delete;
234
235 bool Propagate() final;
236 void RegisterWith(GenericLiteralWatcher* watcher);
237
238 private:
239 const std::vector<AffineExpression> vars_;
240 const AffineExpression min_var_;
241 IntegerTrail* integer_trail_;
242
243 std::vector<IntegerLiteral> integer_reason_;
244};
245
246// Same as MinPropagator except this works on min = MIN(exprs) where exprs are
247// linear expressions. It uses IntegerSumLE to propagate bounds on the exprs.
248// Assumes Canonical expressions (all positive coefficients).
251 public:
253 absl::Span<const Literal> enforcement_literals,
254 std::vector<LinearExpression> exprs, IntegerVariable min_var,
255 Model* model);
257 delete;
259 const GreaterThanMinOfExprsPropagator&) = delete;
260
261 std::string LazyReasonName() const override {
262 return "GreaterThanMinOfExprsPropagator";
263 }
264
265 bool Propagate() final;
266
267 // For LazyReasonInterface.
268 void Explain(int id, IntegerLiteral to_explain, IntegerReason* reason) final;
269
270 private:
271 int RegisterWith(GenericLiteralWatcher* watcher);
272
273 // Lighter version of IntegerSumLE. This uses the current value of
274 // integer_reason_ in addition to the reason for propagating the linear
275 // constraint. The coeffs are assumed to be positive here.
276 bool PropagateLinearUpperBound(int id, absl::Span<const IntegerVariable> vars,
277 absl::Span<const IntegerValue> coeffs,
278 IntegerValue upper_bound);
279
280 const std::vector<LinearExpression> exprs_;
281 const IntegerVariable min_var_;
282 std::vector<IntegerValue> expr_lbs_;
283 Model* model_;
284 IntegerTrail& integer_trail_;
285 EnforcementHelper& enforcement_helper_;
286 EnforcementId enforcement_id_;
287 std::vector<IntegerValue> max_variations_;
288 std::vector<IntegerValue> reason_coeffs_;
289 std::vector<IntegerLiteral> local_reason_;
290 std::vector<IntegerLiteral> integer_reason_for_unique_candidate_;
291 int rev_unique_candidate_ = 0;
292
293 std::vector<IntegerVariable> tmp_vars_;
294 std::vector<IntegerValue> tmp_coeffs_;
295};
296
297// Propagates a * b = p.
298//
299// The bounds [min, max] of a and b will be propagated perfectly, but not
300// the bounds on p as this require more complex arithmetics.
302 public:
303 ProductPropagator(absl::Span<const Literal> enforcement_literals,
305 Model* model);
306
307 // This type is neither copyable nor movable.
310
311 bool Propagate() final;
312
313 private:
314 int RegisterWith(GenericLiteralWatcher* watcher);
315
316 // Maybe replace a_, b_ or c_ by their negation to simplify the cases.
317 bool CanonicalizeCases();
318
319 // Special case when all are >= 0.
320 // We use faster code and better reasons than the generic code.
321 bool PropagateWhenAllNonNegative();
322
323 // Internal helper, see code for more details.
324 bool PropagateMaxOnPositiveProduct(AffineExpression a, AffineExpression b,
325 IntegerValue min_p, IntegerValue max_p);
326
327 // Note that we might negate any two terms in CanonicalizeCases() during
328 // each propagation. This is fine.
332 const IntegerTrail& integer_trail_;
333 EnforcementHelper& enforcement_helper_;
334 EnforcementId enforcement_id_;
335};
336
337// Propagates num / denom = div. Basic version, we don't extract any special
338// cases, and we only propagates the bounds. It expects denom to be > 0.
339//
340// TODO(user): Deal with overflow.
342 public:
343 DivisionPropagator(absl::Span<const Literal> enforcement_literals,
345 AffineExpression div, Model* model);
346
347 // This type is neither copyable nor movable.
350
351 bool Propagate() final;
352
353 private:
354 int RegisterWith(GenericLiteralWatcher* watcher);
355
356 // Propagates the fact that the signs of each domain, if fixed, are
357 // compatible.
358 bool PropagateSigns(AffineExpression num, AffineExpression denom,
359 AffineExpression div);
360
361 // If both num and div >= 0, we can propagate their upper bounds.
362 bool PropagateUpperBounds(AffineExpression num, AffineExpression denom,
363 AffineExpression div);
364
365 // When the sign of all 3 expressions are fixed, we can do morel propagation.
366 //
367 // By using negated expressions, we can make sure the domains of num, denom,
368 // and div are positive.
369 bool PropagatePositiveDomains(AffineExpression num, AffineExpression denom,
370 AffineExpression div);
371
372 AffineExpression num_;
373 AffineExpression denom_;
374 const AffineExpression div_;
375 const AffineExpression negated_div_;
376 const IntegerTrail& integer_trail_;
377 EnforcementHelper& enforcement_helper_;
378 EnforcementId enforcement_id_;
379};
380
381// Propagates var_a / cst_b = var_c. Basic version, we don't extract any special
382// cases, and we only propagates the bounds. cst_b must be > 0.
384 public:
385 FixedDivisionPropagator(absl::Span<const Literal> enforcement_literals,
386 AffineExpression a, IntegerValue b,
387 AffineExpression c, Model* model);
388
389 // This type is neither copyable nor movable.
392
393 bool Propagate() final;
394
395 private:
396 int RegisterWith(GenericLiteralWatcher* watcher);
397
398 const AffineExpression a_;
399 const IntegerValue b_;
400 const AffineExpression c_;
401 const IntegerTrail& integer_trail_;
402 EnforcementHelper& enforcement_helper_;
403 EnforcementId enforcement_id_;
404};
405
406// Propagates target == expr % mod. Basic version, we don't extract any special
407// cases, and we only propagates the bounds. mod must be > 0.
409 public:
410 FixedModuloPropagator(absl::Span<const Literal> enforcement_literals,
411 AffineExpression expr, IntegerValue mod,
412 AffineExpression target, Model* model);
413
414 // This type is neither copyable nor movable.
417
418 bool Propagate() final;
419
420 private:
421 int RegisterWith(GenericLiteralWatcher* watcher);
422
423 bool PropagateWhenFalseAndTargetIsPositive(AffineExpression expr,
424 AffineExpression target);
425 bool PropagateWhenFalseAndTargetDomainContainsZero();
426 bool PropagateSignsAndTargetRange();
427 bool PropagateBoundsWhenExprIsNonNegative(AffineExpression expr,
428 AffineExpression target);
429 bool PropagateOuterBounds();
430
431 const AffineExpression expr_;
432 const IntegerValue mod_;
433 const AffineExpression target_;
434 const AffineExpression negated_expr_;
435 const AffineExpression negated_target_;
436 const IntegerTrail& integer_trail_;
437 EnforcementHelper& enforcement_helper_;
438 EnforcementId enforcement_id_;
439};
440
441// Propagates x * x = s.
442// TODO(user): Only works for x nonnegative.
444 public:
445 SquarePropagator(absl::Span<const Literal> enforcement_literals,
447
448 // This type is neither copyable nor movable.
451
452 bool Propagate() final;
453
454 private:
455 int RegisterWith(GenericLiteralWatcher* watcher);
456
457 const AffineExpression x_;
458 const AffineExpression s_;
459 const IntegerTrail& integer_trail_;
460 EnforcementHelper& enforcement_helper_;
461 EnforcementId enforcement_id_;
462};
463
464// =============================================================================
465// Model based functions.
466// =============================================================================
467
468// enforcement_literals => sum <= upper_bound
470 absl::Span<const Literal> enforcement_literals,
471 absl::Span<const IntegerVariable> vars,
472 absl::Span<const IntegerValue> coefficients, int64_t upper_bound,
473 Model* model) {
474 // Linear1.
475 DCHECK_GE(vars.size(), 1);
476 if (vars.size() == 1) {
477 DCHECK_NE(coefficients[0], 0);
478 IntegerVariable var = vars[0];
479 IntegerValue coeff(coefficients[0]);
480 if (coeff < 0) {
481 var = NegationOf(var);
482 coeff = -coeff;
483 }
484 const IntegerValue rhs = FloorRatio(IntegerValue(upper_bound), coeff);
485 if (enforcement_literals.empty()) {
486 model->Add(LowerOrEqual(var, rhs.value()));
487 } else {
488 model->Add(Implication(enforcement_literals,
490 }
491 return;
492 }
493
494 // Detect precedences with 2 and 3 terms.
495 const SatParameters& params = *model->GetOrCreate<SatParameters>();
496 if (!params.new_linear_propagation()) {
497 if (vars.size() == 2 && (coefficients[0] == 1 || coefficients[0] == -1) &&
498 (coefficients[1] == 1 || coefficients[1] == -1)) {
500 enforcement_literals,
501 coefficients[0] == 1 ? vars[0] : NegationOf(vars[0]),
502 coefficients[1] == 1 ? vars[1] : NegationOf(vars[1]), upper_bound,
503 model);
504 return;
505 }
506 if (vars.size() == 3 && (coefficients[0] == 1 || coefficients[0] == -1) &&
507 (coefficients[1] == 1 || coefficients[1] == -1) &&
508 (coefficients[2] == 1 || coefficients[2] == -1)) {
510 enforcement_literals,
511 coefficients[0] == 1 ? vars[0] : NegationOf(vars[0]),
512 coefficients[1] == 1 ? vars[1] : NegationOf(vars[1]),
513 coefficients[2] == 1 ? vars[2] : NegationOf(vars[2]), upper_bound,
514 model);
515 return;
516 }
517 }
518
519 // If value == min(expression), then we can avoid creating the sum.
520 //
521 // TODO(user): Deal with the case with no enforcement literal, in case the
522 // presolve was turned off?
523 if (!enforcement_literals.empty()) {
524 IntegerValue expression_min(0);
525 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
526 for (int i = 0; i < vars.size(); ++i) {
527 expression_min +=
528 coefficients[i] * (coefficients[i] >= 0
529 ? integer_trail->LevelZeroLowerBound(vars[i])
530 : integer_trail->LevelZeroUpperBound(vars[i]));
531 }
532 if (expression_min == upper_bound) {
533 // Tricky: as we create integer literal, we might propagate stuff and
534 // the bounds might change, so if the expression_min increase with the
535 // bound we use, then the literal must be false.
536 IntegerValue non_cached_min;
537 for (int i = 0; i < vars.size(); ++i) {
538 if (coefficients[i] > 0) {
539 const IntegerValue lb = integer_trail->LevelZeroLowerBound(vars[i]);
540 non_cached_min += coefficients[i] * lb;
541 model->Add(Implication(enforcement_literals,
542 IntegerLiteral::LowerOrEqual(vars[i], lb)));
543 } else if (coefficients[i] < 0) {
544 const IntegerValue ub = integer_trail->LevelZeroUpperBound(vars[i]);
545 non_cached_min += coefficients[i] * ub;
546 model->Add(Implication(enforcement_literals,
548 }
549 }
550 if (non_cached_min > expression_min) {
551 std::vector<Literal> clause;
552 for (const Literal l : enforcement_literals) {
553 clause.push_back(l.Negated());
554 }
555 model->Add(ClauseConstraint(clause));
556 }
557 return;
558 }
559 }
560
561 if (params.new_linear_propagation()) {
562 const bool ok = model->GetOrCreate<LinearPropagator>()->AddConstraint(
563 enforcement_literals, vars, coefficients, IntegerValue(upper_bound));
564 if (!ok) {
565 auto* sat_solver = model->GetOrCreate<SatSolver>();
566 if (sat_solver->CurrentDecisionLevel() == 0) {
567 sat_solver->NotifyThatModelIsUnsat();
568 } else {
569 LOG(FATAL) << "We currently do not support adding conflicting "
570 "constraint at positive level.";
571 }
572 }
573 } else {
574 IntegerSumLE* constraint =
575 new IntegerSumLE(enforcement_literals, vars, coefficients,
576 IntegerValue(upper_bound), model);
577 constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
578 model->TakeOwnership(constraint);
579 }
580}
581
582// enforcement_literals => sum >= lower_bound
584 absl::Span<const Literal> enforcement_literals,
585 absl::Span<const IntegerVariable> vars,
586 absl::Span<const IntegerValue> coefficients, int64_t lower_bound,
587 Model* model) {
588 // We just negate everything and use an <= constraint.
589 std::vector<IntegerValue> negated_coeffs(coefficients.begin(),
590 coefficients.end());
591 for (IntegerValue& ref : negated_coeffs) ref = -ref;
592 AddWeightedSumLowerOrEqual(enforcement_literals, vars, negated_coeffs,
593 -lower_bound, model);
594}
595
596// Weighted sum <= constant.
597template <typename VectorInt>
598inline std::function<void(Model*)> WeightedSumLowerOrEqual(
599 absl::Span<const IntegerVariable> vars, const VectorInt& coefficients,
600 int64_t upper_bound) {
601 return [=, vars = std::vector<IntegerVariable>(vars.begin(), vars.end()),
602 coeffs = std::vector<IntegerValue>(
603 coefficients.begin(), coefficients.end())](Model* model) {
604 return AddWeightedSumLowerOrEqual({}, vars, coeffs, upper_bound, model);
605 };
606}
607
608// Weighted sum >= constant.
609template <typename VectorInt>
610inline std::function<void(Model*)> WeightedSumGreaterOrEqual(
611 absl::Span<const IntegerVariable> vars, const VectorInt& coefficients,
612 int64_t lower_bound) {
613 // We just negate everything and use an <= constraints.
614 std::vector<IntegerValue> negated_coeffs(coefficients.begin(),
615 coefficients.end());
616 for (IntegerValue& ref : negated_coeffs) ref = -ref;
617 return WeightedSumLowerOrEqual(vars, negated_coeffs, -lower_bound);
618}
619
620// Weighted sum == constant.
621template <typename VectorInt>
622inline std::function<void(Model*)> FixedWeightedSum(
623 absl::Span<const IntegerVariable> vars, const VectorInt& coefficients,
624 int64_t value) {
625 return [=, vars = std::vector<IntegerVariable>(vars.begin(), vars.end())](
626 Model* model) {
627 model->Add(WeightedSumGreaterOrEqual(vars, coefficients, value));
628 model->Add(WeightedSumLowerOrEqual(vars, coefficients, value));
629 };
630}
631
632// TODO(user): Delete once Telamon use new function.
633inline std::function<void(Model*)> ConditionalWeightedSumLowerOrEqual(
634 absl::Span<const Literal> enforcement_literals,
635 absl::Span<const IntegerVariable> vars,
636 absl::Span<const int64_t> coefficients, int64_t upper_bound) {
637 return [=, vars = std::vector<IntegerVariable>(vars.begin(), vars.end()),
638 coeffs = std::vector<IntegerValue>(coefficients.begin(),
639 coefficients.end()),
640 enforcement_literals =
641 std::vector<Literal>(enforcement_literals.begin(),
642 enforcement_literals.end())](Model* model) {
643 AddWeightedSumLowerOrEqual(enforcement_literals, vars, coeffs, upper_bound,
644 model);
645 };
646}
647inline std::function<void(Model*)> ConditionalWeightedSumGreaterOrEqual(
648 absl::Span<const Literal> enforcement_literals,
649 absl::Span<const IntegerVariable> vars,
650 absl::Span<const int64_t> coefficients, int64_t upper_bound) {
651 return [=,
652 coeffs = std::vector<IntegerValue>(coefficients.begin(),
653 coefficients.end()),
654 vars = std::vector<IntegerVariable>(vars.begin(), vars.end()),
655 enforcement_literals =
656 std::vector<Literal>(enforcement_literals.begin(),
657 enforcement_literals.end())](Model* model) {
658 AddWeightedSumGreaterOrEqual(enforcement_literals, vars, coeffs,
659 upper_bound, model);
660 };
661}
662
663// LinearConstraint version.
665 const absl::Span<const Literal> enforcement_literals,
666 const LinearConstraint& cst, Model* model) {
667 if (cst.num_terms == 0) {
668 if (cst.lb <= 0 && cst.ub >= 0) return;
669
670 // The enforcement literals cannot be all at true.
671 std::vector<Literal> clause;
672 for (const Literal lit : enforcement_literals) {
673 clause.push_back(lit.Negated());
674 }
675 return model->Add(ClauseConstraint(clause));
676 }
677
678 if (cst.ub < kMaxIntegerValue) {
679 AddWeightedSumLowerOrEqual(enforcement_literals, cst.VarsAsSpan(),
680 cst.CoeffsAsSpan(), cst.ub.value(), model);
681 }
682 if (cst.lb > kMinIntegerValue) {
683 AddWeightedSumGreaterOrEqual(enforcement_literals, cst.VarsAsSpan(),
684 cst.CoeffsAsSpan(), cst.lb.value(), model);
685 }
686}
687
688inline void LoadLinearConstraint(const LinearConstraint& cst, Model* model) {
689 LoadConditionalLinearConstraint({}, cst, model);
691
693 const absl::Span<const Literal> enforcement_literals, AffineExpression left,
694 AffineExpression right, Model* model) {
695 LinearConstraintBuilder builder(model, kMinIntegerValue, 0);
696 builder.AddTerm(left, 1);
697 builder.AddTerm(right, -1);
698 LoadConditionalLinearConstraint(enforcement_literals, builder.Build(), model);
699}
700
701// Model-based function to create an IntegerVariable that corresponds to the
702// given weighted sum of other IntegerVariables.
703//
704// Note that this is templated so that it can seamlessly accept vector<int> or
705// vector<int64_t>.
706//
707// TODO(user): invert the coefficients/vars arguments.
708template <typename VectorInt>
709inline std::function<IntegerVariable(Model*)> NewWeightedSum(
710 const VectorInt& coefficients, const std::vector<IntegerVariable>& vars) {
711 return [=](Model* model) {
712 std::vector<IntegerVariable> new_vars = vars;
713 // To avoid overflow in the FixedWeightedSum() constraint, we need to
714 // compute the basic bounds on the sum.
715 //
716 // TODO(user): deal with overflow here too!
717 int64_t sum_lb(0);
718 int64_t sum_ub(0);
719 for (int i = 0; i < new_vars.size(); ++i) {
720 if (coefficients[i] > 0) {
721 sum_lb += coefficients[i] * model->Get(LowerBound(new_vars[i]));
722 sum_ub += coefficients[i] * model->Get(UpperBound(new_vars[i]));
723 } else {
724 sum_lb += coefficients[i] * model->Get(UpperBound(new_vars[i]));
725 sum_ub += coefficients[i] * model->Get(LowerBound(new_vars[i]));
726 }
727 }
728
729 const IntegerVariable sum = model->Add(NewIntegerVariable(sum_lb, sum_ub));
730 new_vars.push_back(sum);
731 std::vector<int64_t> new_coeffs(coefficients.begin(), coefficients.end());
732 new_coeffs.push_back(-1);
733 model->Add(FixedWeightedSum(new_vars, new_coeffs, 0));
734 return sum;
735 };
736}
737
738// Expresses the fact that an existing integer variable is equal to the minimum
739// of linear expressions. Assumes Canonical expressions (all positive
740// coefficients).
741inline void AddIsEqualToMinOf(
742 const absl::Span<const Literal> enforcement_literals,
743 const LinearExpression& min_expr, std::vector<LinearExpression> exprs,
744 Model* model) {
745 IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
746
747 IntegerVariable min_var;
748 if (min_expr.vars.size() == 1 && std::abs(min_expr.coeffs[0].value()) == 1 &&
749 min_expr.offset == 0) {
750 if (min_expr.coeffs[0].value() == 1) {
751 min_var = min_expr.vars[0];
752 } else {
753 min_var = NegationOf(min_expr.vars[0]);
754 }
755 } else {
756 // Create a new variable if the expression is not just a single variable.
757 IntegerValue min_lb = min_expr.Min(*integer_trail);
758 IntegerValue min_ub = min_expr.Max(*integer_trail);
759 min_var = integer_trail->AddIntegerVariable(min_lb, min_ub);
760
761 // min_var = min_expr
762 LinearConstraintBuilder builder(0, 0);
763 builder.AddLinearExpression(min_expr, 1);
764 builder.AddTerm(min_var, -1);
765 LoadLinearConstraint(builder.Build(), model);
766 }
767
768 // Add for all i, enforcement_literals => min <= exprs[i].
769 for (const LinearExpression& expr : exprs) {
771 builder.AddLinearExpression(expr, 1);
772 builder.AddTerm(min_var, -1);
773 LoadConditionalLinearConstraint(enforcement_literals, builder.Build(),
774 model);
775 }
776
778 new GreaterThanMinOfExprsPropagator(enforcement_literals,
779 std::move(exprs), min_var, model);
780 model->TakeOwnership(constraint);
781}
782
783ABSL_DEPRECATED("Use AddIsEqualToMinOf() instead.")
784inline std::function<void(Model*)> IsEqualToMinOf(
785 const LinearExpression& min_expr,
786 const std::vector<LinearExpression>& exprs) {
787 return [&](Model* model) {
788 AddIsEqualToMinOf(/*enforcement_literals=*/{}, min_expr, exprs, model);
789 };
790}
791
792// Adds the constraint: a * b = p.
793inline std::function<void(Model*)> ProductConstraint(
794 absl::Span<const Literal> enforcement_literals, AffineExpression a,
796 return [=](Model* model) {
797 const IntegerTrail& integer_trail = *model->GetOrCreate<IntegerTrail>();
798 // TODO(user): return early if constraint is never enforced.
799 if (a == b) {
800 if (integer_trail.LowerBound(a) >= 0) {
801 model->TakeOwnership(
802 new SquarePropagator(enforcement_literals, a, p, model));
803 return;
804 }
805 if (integer_trail.UpperBound(a) <= 0) {
806 model->TakeOwnership(
807 new SquarePropagator(enforcement_literals, a.Negated(), p, model));
808 return;
809 }
810 }
811 model->TakeOwnership(
812 new ProductPropagator(enforcement_literals, a, b, p, model));
813 };
814}
815
816// Adds the constraint: num / denom = div. (denom > 0).
817inline std::function<void(Model*)> DivisionConstraint(
818 absl::Span<const Literal> enforcement_literals, AffineExpression num,
820 return [=](Model* model) {
821 const IntegerTrail& integer_trail = *model->GetOrCreate<IntegerTrail>();
822 // TODO(user): return early if constraint is never enforced.
823 DivisionPropagator* constraint;
824 if (integer_trail.UpperBound(denom) < 0) {
825 constraint = new DivisionPropagator(enforcement_literals, num.Negated(),
826 denom.Negated(), div, model);
827 } else {
828 constraint =
829 new DivisionPropagator(enforcement_literals, num, denom, div, model);
830 }
831 model->TakeOwnership(constraint);
832 };
833}
834
835// Adds the constraint: a / b = c where b is a constant.
836inline std::function<void(Model*)> FixedDivisionConstraint(
837 absl::Span<const Literal> enforcement_literals, AffineExpression a,
838 IntegerValue b, AffineExpression c) {
839 return [=](Model* model) {
840 // TODO(user): return early if constraint is never enforced.
841 FixedDivisionPropagator* constraint =
842 b > 0
843 ? new FixedDivisionPropagator(enforcement_literals, a, b, c, model)
844 : new FixedDivisionPropagator(enforcement_literals, a.Negated(), -b,
845 c, model);
846 model->TakeOwnership(constraint);
847 };
848}
849
850// Adds the constraint: a % b = c where b is a constant.
851inline std::function<void(Model*)> FixedModuloConstraint(
852 absl::Span<const Literal> enforcement_literals, AffineExpression a,
853 IntegerValue b, AffineExpression c) {
854 return [=](Model* model) {
855 model->TakeOwnership(
856 new FixedModuloPropagator(enforcement_literals, a, b, c, model));
857 };
858}
859
860} // namespace sat
861} // namespace operations_research
862
863#endif // ORTOOLS_SAT_INTEGER_EXPR_H_
Definition model.h:345
DivisionPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression num, AffineExpression denom, AffineExpression div, Model *model)
DivisionPropagator(const DivisionPropagator &)=delete
DivisionPropagator & operator=(const DivisionPropagator &)=delete
FixedDivisionPropagator(const FixedDivisionPropagator &)=delete
FixedDivisionPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression a, IntegerValue b, AffineExpression c, Model *model)
FixedDivisionPropagator & operator=(const FixedDivisionPropagator &)=delete
FixedModuloPropagator & operator=(const FixedModuloPropagator &)=delete
FixedModuloPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression expr, IntegerValue mod, AffineExpression target, Model *model)
FixedModuloPropagator(const FixedModuloPropagator &)=delete
GreaterThanMinOfExprsPropagator(absl::Span< const Literal > enforcement_literals, std::vector< LinearExpression > exprs, IntegerVariable min_var, Model *model)
GreaterThanMinOfExprsPropagator & operator=(const GreaterThanMinOfExprsPropagator &)=delete
GreaterThanMinOfExprsPropagator(const GreaterThanMinOfExprsPropagator &)=delete
IntegerValue LowerBound(IntegerVariable i) const
Definition integer.h:1537
IntegerValue UpperBound(IntegerVariable i) const
Definition integer.h:1541
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition integer.h:1657
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition integer.h:1650
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
Definition integer.cc:856
LevelZeroEquality(IntegerVariable target, const std::vector< IntegerVariable > &vars, const std::vector< IntegerValue > &coeffs, Model *model)
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void Explain(int id, IntegerLiteral to_explain, IntegerReason *reason) final
std::pair< IntegerValue, IntegerValue > ConditionalLb(IntegerLiteral integer_literal, IntegerVariable target_var) const
LinearConstraintPropagator(LinearConstraint ct, Model *model)
LinearConstraintPropagator(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coeffs, IntegerValue upper_bound, Model *model)
MinPropagator(const MinPropagator &)=delete
void RegisterWith(GenericLiteralWatcher *watcher)
MinPropagator(std::vector< AffineExpression > vars, AffineExpression min_var, IntegerTrail *integer_trail)
MinPropagator & operator=(const MinPropagator &)=delete
T Add(std::function< T(Model *)> f)
Definition model.h:104
ProductPropagator(const ProductPropagator &)=delete
ProductPropagator(absl::Span< const Literal > enforcement_literals, AffineExpression a, AffineExpression b, AffineExpression p, Model *model)
ProductPropagator & operator=(const ProductPropagator &)=delete
SquarePropagator(const SquarePropagator &)=delete
SquarePropagator & operator=(const SquarePropagator &)=delete
SquarePropagator(absl::Span< const Literal > enforcement_literals, AffineExpression x, AffineExpression s, Model *model)
const VariablesAssignment & Assignment() const
Definition sat_base.h:654
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
void AddIsEqualToMinOf(const absl::Span< const Literal > enforcement_literals, const LinearExpression &min_expr, std::vector< LinearExpression > exprs, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< void(Model *)> WeightedSumGreaterOrEqual(absl::Span< const IntegerVariable > vars, const VectorInt &coefficients, int64_t lower_bound)
void AddWeightedSumLowerOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coefficients, int64_t upper_bound, Model *model)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition sat_solver.h:994
void AddWeightedSumGreaterOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const IntegerValue > coefficients, int64_t lower_bound, Model *model)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
Definition integer.h:1819
std::function< void(Model *)> LowerOrEqual(IntegerVariable v, int64_t ub)
Definition integer.h:1862
std::function< void(Model *)> FixedDivisionConstraint(absl::Span< const Literal > enforcement_literals, AffineExpression a, IntegerValue b, AffineExpression c)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Definition integer.cc:52
std::function< void(Model *)> FixedWeightedSum(absl::Span< const IntegerVariable > vars, const VectorInt &coefficients, int64_t value)
void LoadConditionalLinearConstraint(const absl::Span< const Literal > enforcement_literals, const LinearConstraint &cst, Model *model)
std::function< void(Model *)> IsEqualToMinOf(const LinearExpression &min_expr, const std::vector< LinearExpression > &exprs)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
std::function< void(Model *)> Implication(absl::Span< const Literal > enforcement_literals, IntegerLiteral i)
Definition integer.h:1889
std::function< void(Model *)> ProductConstraint(absl::Span< const Literal > enforcement_literals, AffineExpression a, AffineExpression b, AffineExpression p)
std::function< void(Model *)> ConditionalWeightedSumLowerOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coefficients, int64_t upper_bound)
void AddConditionalAffinePrecedence(const absl::Span< const Literal > enforcement_literals, AffineExpression left, AffineExpression right, Model *model)
std::function< void(Model *)> DivisionConstraint(absl::Span< const Literal > enforcement_literals, AffineExpression num, AffineExpression denom, AffineExpression div)
std::function< void(Model *)> ConditionalWeightedSumGreaterOrEqual(absl::Span< const Literal > enforcement_literals, absl::Span< const IntegerVariable > vars, absl::Span< const int64_t > coefficients, int64_t upper_bound)
std::function< void(Model *)> FixedModuloConstraint(absl::Span< const Literal > enforcement_literals, AffineExpression a, IntegerValue b, AffineExpression c)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Definition integer.h:1825
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition integer.h:1771
void AddConditionalSum3LowerOrEqual(absl::Span< const Literal > enforcement_literals, IntegerVariable a, IntegerVariable b, IntegerVariable c, int64_t ub, Model *model)
std::function< IntegerVariable(Model *)> NewWeightedSum(const VectorInt &coefficients, const std::vector< IntegerVariable > &vars)
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
LinearConstraintPropagator< true > IntegerSumLE128
LinearConstraintPropagator< false > IntegerSumLE
void AddConditionalSum2LowerOrEqual(absl::Span< const Literal > enforcement_literals, IntegerVariable a, IntegerVariable b, int64_t ub, Model *model)
std::function< void(Model *)> WeightedSumLowerOrEqual(absl::Span< const IntegerVariable > vars, const VectorInt &coefficients, int64_t upper_bound)
OR-Tools root namespace.
STL namespace.
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
absl::Span< const IntegerValue > CoeffsAsSpan() const
absl::Span< const IntegerVariable > VarsAsSpan() const
IntegerValue Min(const IntegerTrail &integer_trail) const
IntegerValue Max(const IntegerTrail &integer_trail) const