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