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