OR-Tools  9.6
sat/linear_constraint.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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_LINEAR_CONSTRAINT_H_
15 #define OR_TOOLS_SAT_LINEAR_CONSTRAINT_H_
16 
17 #include <algorithm>
18 #include <ostream>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/base/attributes.h"
24 #include "absl/strings/str_cat.h"
26 #include "ortools/sat/integer.h"
27 #include "ortools/sat/model.h"
28 #include "ortools/sat/sat_base.h"
30 
31 namespace operations_research {
32 namespace sat {
33 
34 // One linear constraint on a set of Integer variables.
35 // Important: there should be no duplicate variables.
36 //
37 // We also assume that we never have integer overflow when evaluating such
38 // constraint at the ROOT node. This should be enforced by the checker for user
39 // given constraints, and we must enforce it ourselves for the newly created
40 // constraint. See ValidateLinearConstraintForOverflow().
42  IntegerValue lb;
43  IntegerValue ub;
44  std::vector<IntegerVariable> vars;
45  std::vector<IntegerValue> coeffs;
46 
48  LinearConstraint(IntegerValue _lb, IntegerValue _ub) : lb(_lb), ub(_ub) {}
49 
50  void AddTerm(IntegerVariable var, IntegerValue coeff) {
51  vars.push_back(var);
52  coeffs.push_back(coeff);
53  }
54 
55  void Clear() {
56  lb = ub = IntegerValue(0);
57  ClearTerms();
58  }
59 
60  void ClearTerms() {
61  vars.clear();
62  coeffs.clear();
63  }
64 
65  std::string DebugString() const {
66  std::string result;
67  if (lb.value() > kMinIntegerValue) {
68  absl::StrAppend(&result, lb.value(), " <= ");
69  }
70  for (int i = 0; i < vars.size(); ++i) {
71  absl::StrAppend(&result, i > 0 ? " " : "",
73  }
74  if (ub.value() < kMaxIntegerValue) {
75  absl::StrAppend(&result, " <= ", ub.value());
76  }
77  return result;
78  }
79 
80  bool operator==(const LinearConstraint other) const {
81  if (this->lb != other.lb) return false;
82  if (this->ub != other.ub) return false;
83  if (this->vars != other.vars) return false;
84  if (this->coeffs != other.coeffs) return false;
85  return true;
86  }
87 };
88 
89 inline std::ostream& operator<<(std::ostream& os, const LinearConstraint& ct) {
90  os << ct.DebugString();
91  return os;
92 }
93 
94 // Helper struct to model linear expression for lin_min/lin_max constraints. The
95 // canonical expression should only contain positive coefficients.
97  std::vector<IntegerVariable> vars;
98  std::vector<IntegerValue> coeffs;
99  IntegerValue offset = IntegerValue(0);
100 
101  // Return[s] the evaluation of the linear expression.
102  double LpValue(
103  const absl::StrongVector<IntegerVariable, double>& lp_values) const;
104 
105  IntegerValue LevelZeroMin(IntegerTrail* integer_trail) const;
106 
107  // Returns lower bound of linear expression using variable bounds of the
108  // variables in expression.
109  IntegerValue Min(const IntegerTrail& integer_trail) const;
110 
111  // Returns upper bound of linear expression using variable bounds of the
112  // variables in expression.
113  IntegerValue Max(const IntegerTrail& integer_trail) const;
114 
115  std::string DebugString() const;
116 };
117 
118 // Returns the same expression in the canonical form (all positive
119 // coefficients).
121 
122 // Makes sure that any of our future computation on this constraint will not
123 // cause overflow. We use the level zero bounds and use the same definition as
124 // in PossibleIntegerOverflow() in the cp_model.proto checker.
125 //
126 // Namely, the sum of positive terms, the sum of negative terms and their
127 // difference shouldn't overflow. Note that we don't validate the rhs, but if
128 // the bounds are properly relaxed, then this shouldn't cause any issues.
129 //
130 // Note(user): We should avoid doing this test too often as it can be slow. At
131 // least do not do it more than once on each constraint.
133  const IntegerTrail& integer_trail);
134 
135 // Preserves canonicality.
137 
138 // Returns the same expression with positive variables.
140 
141 // Returns the coefficient of the variable in the expression. Works in linear
142 // time.
143 // Note: GetCoefficient(NegationOf(var, expr)) == -GetCoefficient(var, expr).
144 IntegerValue GetCoefficient(const IntegerVariable var,
145  const LinearExpression& expr);
146 IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
147  const LinearExpression& expr);
148 
149 // Allow to build a LinearConstraint while making sure there is no duplicate
150 // variables. Note that we do not simplify literal/variable that are currently
151 // fixed here.
152 //
153 // All the functions manipulate a linear expression with an offset. The final
154 // constraint bounds will include this offset.
155 //
156 // TODO(user): Rename to LinearExpressionBuilder?
158  public:
159  // We support "sticky" kMinIntegerValue for lb and kMaxIntegerValue for ub
160  // for one-sided constraints.
161  //
162  // Assumes that the 'model' has IntegerEncoder. The bounds can either be
163  // specified at construction or during the Build() call.
165  : encoder_(model->Get<IntegerEncoder>()), lb_(0), ub_(0) {}
166  LinearConstraintBuilder(const Model* model, IntegerValue lb, IntegerValue ub)
167  : encoder_(model->Get<IntegerEncoder>()), lb_(lb), ub_(ub) {}
168 
169  // Warning: this version without encoder cannot be used to add literals, so
170  // one shouldn't call AddLiteralTerm() on it. All other functions works.
171  //
172  // TODO(user): Have a subclass so we can enforce than caller using
173  // AddLiteralTerm() must construct the Builder with an encoder.
174  LinearConstraintBuilder() : encoder_(nullptr), lb_(0), ub_(0) {}
175 
176  // Adds the corresponding term to the current linear expression.
177  void AddConstant(IntegerValue value);
178  void AddTerm(IntegerVariable var, IntegerValue coeff);
179  void AddTerm(AffineExpression expr, IntegerValue coeff);
180  void AddLinearExpression(const LinearExpression& expr);
181  void AddLinearExpression(const LinearExpression& expr, IntegerValue coeff);
182 
183  // Add the corresponding decomposed products (obtained from
184  // TryToDecomposeProduct). The code assumes all literals to be in an
185  // exactly_one relation.
186  // It returns false if one literal does not have an integer view, as it
187  // actually calls AddLiteralTerm().
188  ABSL_MUST_USE_RESULT bool AddDecomposedProduct(
189  const std::vector<LiteralValueValue>& product);
190 
191  // Add literal * coeff to the constaint. Returns false and do nothing if the
192  // given literal didn't have an integer view.
193  ABSL_MUST_USE_RESULT bool AddLiteralTerm(
194  Literal lit, IntegerValue coeff = IntegerValue(1));
195 
196  // Add an under linearization of the product of two affine expressions.
197  // If at least one of them is fixed, then we add the exact product (which is
198  // linear). Otherwise, we use McCormick relaxation:
199  // left * right = (left_min + delta_left) * (right_min + delta_right) =
200  // left_min * right_min + delta_left * right_min +
201  // delta_right * left_min + delta_left * delta_right
202  // which is >= (by ignoring the quatratic term)
203  // right_min * left + left_min * right - right_min * left_min
204  //
205  // TODO(user): We could use (max - delta) instead of (min + delta) for each
206  // expression instead. This would depend on the LP value of the left and
207  // right.
209  IntegerTrail* integer_trail,
210  bool* is_quadratic = nullptr);
211 
212  // Clears all added terms and constants. Keeps the original bounds.
213  void Clear() {
214  offset_ = IntegerValue(0);
215  terms_.clear();
216  }
217 
218  // Reset the bounds passed at construction time.
219  void ResetBounds(IntegerValue lb, IntegerValue ub) {
220  lb_ = lb;
221  ub_ = ub;
222  }
223 
224  // Builds and returns the corresponding constraint in a canonical form.
225  // All the IntegerVariable will be positive and appear in increasing index
226  // order.
227  //
228  // The bounds can be changed here or taken at construction.
229  //
230  // TODO(user): this doesn't invalidate the builder object, but if one wants
231  // to do a lot of dynamic editing to the constraint, then then underlying
232  // algorithm needs to be optimized for that.
234  LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub);
235 
236  // Returns the linear expression part of the constraint only, without the
237  // bounds.
239 
240  private:
241  const IntegerEncoder* encoder_;
242  IntegerValue lb_;
243  IntegerValue ub_;
244 
245  IntegerValue offset_ = IntegerValue(0);
246 
247  // Initially we push all AddTerm() here, and during Build() we merge terms
248  // on the same variable.
249  std::vector<std::pair<IntegerVariable, IntegerValue>> terms_;
250 };
251 
252 // Returns the activity of the given constraint. That is the current value of
253 // the linear terms.
254 double ComputeActivity(
255  const LinearConstraint& constraint,
257 
258 // Returns sqrt(sum square(coeff)).
259 double ComputeL2Norm(const LinearConstraint& constraint);
260 
261 // Returns the maximum absolute value of the coefficients.
262 IntegerValue ComputeInfinityNorm(const LinearConstraint& constraint);
263 
264 // Returns the scalar product of given constraint coefficients. This method
265 // assumes that the constraint variables are in sorted order.
266 double ScalarProduct(const LinearConstraint& constraint1,
267  const LinearConstraint& constraint2);
268 
269 // Computes the GCD of the constraint coefficient, and divide them by it. This
270 // also tighten the constraint bounds assumming all the variables are integer.
271 void DivideByGCD(LinearConstraint* constraint);
272 
273 // Removes the entries with a coefficient of zero.
274 void RemoveZeroTerms(LinearConstraint* constraint);
275 
276 // Makes all coefficients positive by transforming a variable to its negation.
277 void MakeAllCoefficientsPositive(LinearConstraint* constraint);
278 
279 // Makes all variables "positive" by transforming a variable to its negation.
280 void MakeAllVariablesPositive(LinearConstraint* constraint);
281 
282 // Sorts the terms and makes all IntegerVariable positive. This assumes that a
283 // variable or its negation only appear once.
284 //
285 // Note that currently this allocates some temporary memory.
286 void CanonicalizeConstraint(LinearConstraint* ct);
287 
288 // Returns false if duplicate variables are found in ct.
289 bool NoDuplicateVariable(const LinearConstraint& ct);
290 
291 // Sorts and merges duplicate IntegerVariable in the given "terms".
292 // Fills the given LinearConstraint or LinearExpression with the result.
293 //
294 // TODO(user): This actually only sort the terms, we don't clean them.
295 template <class ClassWithVarsAndCoeffs>
297  std::vector<std::pair<IntegerVariable, IntegerValue>>* terms,
298  ClassWithVarsAndCoeffs* output) {
299  output->vars.clear();
300  output->coeffs.clear();
301 
302  // Sort and add coeff of duplicate variables. Note that a variable and
303  // its negation will appear one after another in the natural order.
304  std::sort(terms->begin(), terms->end());
305  IntegerVariable previous_var = kNoIntegerVariable;
306  IntegerValue current_coeff(0);
307  for (const std::pair<IntegerVariable, IntegerValue>& entry : *terms) {
308  if (previous_var == entry.first) {
309  current_coeff += entry.second;
310  } else if (previous_var == NegationOf(entry.first)) {
311  current_coeff -= entry.second;
312  } else {
313  if (current_coeff != 0) {
314  output->vars.push_back(previous_var);
315  output->coeffs.push_back(current_coeff);
316  }
317  previous_var = entry.first;
318  current_coeff = entry.second;
319  }
320  }
321  if (current_coeff != 0) {
322  output->vars.push_back(previous_var);
323  output->coeffs.push_back(current_coeff);
324  }
325 }
326 
327 } // namespace sat
328 } // namespace operations_research
329 
330 #endif // OR_TOOLS_SAT_LINEAR_CONSTRAINT_H_
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(const std::vector< LiteralValueValue > &product)
void AddLinearExpression(const LinearExpression &expr)
LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub)
void ResetBounds(IntegerValue lb, IntegerValue ub)
LinearConstraintBuilder(const Model *model, IntegerValue lb, IntegerValue ub)
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc:88
std::string IntegerTermDebugString(IntegerVariable var, IntegerValue coeff)
Definition: integer.h:159
void RemoveZeroTerms(LinearConstraint *constraint)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
double ScalarProduct(const LinearConstraint &constraint1, const LinearConstraint &constraint2)
const IntegerVariable kNoIntegerVariable(-1)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
void CanonicalizeConstraint(LinearConstraint *ct)
bool NoDuplicateVariable(const LinearConstraint &ct)
double ComputeL2Norm(const LinearConstraint &constraint)
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
void MakeAllVariablesPositive(LinearConstraint *constraint)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:46
IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var, const LinearExpression &expr)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue >> *terms, ClassWithVarsAndCoeffs *output)
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
Collection of objects used to extend the Constraint Solver library.
bool operator==(const LinearConstraint other) const
LinearConstraint(IntegerValue _lb, IntegerValue _ub)
void AddTerm(IntegerVariable var, IntegerValue coeff)
IntegerValue LevelZeroMin(IntegerTrail *integer_trail) const
IntegerValue Max(const IntegerTrail &integer_trail) const
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const
IntegerValue Min(const IntegerTrail &integer_trail) const