OR-Tools  9.1
sat/linear_constraint.cc
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 
15 
16 #include <cstdint>
17 
18 #include "ortools/base/mathutil.h"
20 #include "ortools/sat/integer.h"
21 
22 namespace operations_research {
23 namespace sat {
24 
25 void LinearConstraintBuilder::AddTerm(IntegerVariable var, IntegerValue coeff) {
26  if (coeff == 0) return;
27  // We can either add var or NegationOf(var), and we always choose the
28  // positive one.
29  if (VariableIsPositive(var)) {
30  terms_.push_back({var, coeff});
31  } else {
32  terms_.push_back({NegationOf(var), -coeff});
33  }
34 }
35 
37  IntegerValue coeff) {
38  if (coeff == 0) return;
39  // We can either add var or NegationOf(var), and we always choose the
40  // positive one.
41  if (expr.var != kNoIntegerVariable) {
42  if (VariableIsPositive(expr.var)) {
43  terms_.push_back({expr.var, coeff * expr.coeff});
44  } else {
45  terms_.push_back({NegationOf(expr.var), -coeff * expr.coeff});
46  }
47  }
48  if (lb_ > kMinIntegerValue) lb_ -= coeff * expr.constant;
49  if (ub_ < kMaxIntegerValue) ub_ -= coeff * expr.constant;
50 }
51 
53  const LinearExpression& expr) {
54  AddLinearExpression(expr, IntegerValue(1));
55 }
56 
58  IntegerValue coeff) {
59  for (int i = 0; i < expr.vars.size(); ++i) {
60  // We must use positive variables.
61  if (VariableIsPositive(expr.vars[i])) {
62  terms_.push_back({expr.vars[i], expr.coeffs[i] * coeff});
63  } else {
64  terms_.push_back({NegationOf(expr.vars[i]), -expr.coeffs[i] * coeff});
65  }
66  }
67  if (lb_ > kMinIntegerValue) lb_ -= expr.offset * coeff;
68  if (ub_ < kMaxIntegerValue) ub_ -= expr.offset * coeff;
69 }
70 
73  IntegerTrail* integer_trail) {
74  if (left.IsFixed(integer_trail)) {
75  AddTerm(right, left.Min(integer_trail));
76  } else if (right.IsFixed(integer_trail)) {
77  AddTerm(left, right.Min(integer_trail));
78  } else {
79  const IntegerValue left_min = left.Min(integer_trail);
80  const IntegerValue right_min = right.Min(integer_trail);
81  AddTerm(left, right_min);
82  AddTerm(right, left_min);
83  // Substract the energy counted twice.
84  AddConstant(-left_min * right_min);
85  }
86 }
87 
89  if (lb_ > kMinIntegerValue) lb_ -= value;
90  if (ub_ < kMaxIntegerValue) ub_ -= value;
91 }
92 
93 ABSL_MUST_USE_RESULT bool LinearConstraintBuilder::AddLiteralTerm(
94  Literal lit, IntegerValue coeff) {
95  bool has_direct_view = encoder_.GetLiteralView(lit) != kNoIntegerVariable;
96  bool has_opposite_view =
97  encoder_.GetLiteralView(lit.Negated()) != kNoIntegerVariable;
98 
99  // If a literal has both views, we want to always keep the same
100  // representative: the smallest IntegerVariable. Note that AddTerm() will
101  // also make sure to use the associated positive variable.
102  if (has_direct_view && has_opposite_view) {
103  if (encoder_.GetLiteralView(lit) <=
104  encoder_.GetLiteralView(lit.Negated())) {
105  has_opposite_view = false;
106  } else {
107  has_direct_view = false;
108  }
109  }
110  if (has_direct_view) {
111  AddTerm(encoder_.GetLiteralView(lit), coeff);
112  return true;
113  }
114  if (has_opposite_view) {
115  AddTerm(encoder_.GetLiteralView(lit.Negated()), -coeff);
116  if (lb_ > kMinIntegerValue) lb_ -= coeff;
117  if (ub_ < kMaxIntegerValue) ub_ -= coeff;
118  return true;
119  }
120  return false;
121 }
122 
124  std::vector<std::pair<IntegerVariable, IntegerValue>>* terms,
125  LinearConstraint* constraint) {
126  constraint->vars.clear();
127  constraint->coeffs.clear();
128 
129  // Sort and add coeff of duplicate variables. Note that a variable and
130  // its negation will appear one after another in the natural order.
131  std::sort(terms->begin(), terms->end());
132  IntegerVariable previous_var = kNoIntegerVariable;
133  IntegerValue current_coeff(0);
134  for (const std::pair<IntegerVariable, IntegerValue> entry : *terms) {
135  if (previous_var == entry.first) {
136  current_coeff += entry.second;
137  } else if (previous_var == NegationOf(entry.first)) {
138  current_coeff -= entry.second;
139  } else {
140  if (current_coeff != 0) {
141  constraint->vars.push_back(previous_var);
142  constraint->coeffs.push_back(current_coeff);
143  }
144  previous_var = entry.first;
145  current_coeff = entry.second;
146  }
147  }
148  if (current_coeff != 0) {
149  constraint->vars.push_back(previous_var);
150  constraint->coeffs.push_back(current_coeff);
151  }
152 }
153 
155  LinearConstraint result;
156  result.lb = lb_;
157  result.ub = ub_;
158  CleanTermsAndFillConstraint(&terms_, &result);
159  return result;
160 }
161 
163  const LinearConstraint& constraint,
165  double activity = 0;
166  for (int i = 0; i < constraint.vars.size(); ++i) {
167  const IntegerVariable var = constraint.vars[i];
168  const IntegerValue coeff = constraint.coeffs[i];
169  activity += coeff.value() * values[var];
170  }
171  return activity;
172 }
173 
174 double ComputeL2Norm(const LinearConstraint& constraint) {
175  double sum = 0.0;
176  for (const IntegerValue coeff : constraint.coeffs) {
177  sum += ToDouble(coeff) * ToDouble(coeff);
178  }
179  return std::sqrt(sum);
180 }
181 
182 IntegerValue ComputeInfinityNorm(const LinearConstraint& constraint) {
183  IntegerValue result(0);
184  for (const IntegerValue coeff : constraint.coeffs) {
185  result = std::max(result, IntTypeAbs(coeff));
186  }
187  return result;
188 }
189 
190 double ScalarProduct(const LinearConstraint& constraint1,
191  const LinearConstraint& constraint2) {
192  DCHECK(std::is_sorted(constraint1.vars.begin(), constraint1.vars.end()));
193  DCHECK(std::is_sorted(constraint2.vars.begin(), constraint2.vars.end()));
194  double scalar_product = 0.0;
195  int index_1 = 0;
196  int index_2 = 0;
197  while (index_1 < constraint1.vars.size() &&
198  index_2 < constraint2.vars.size()) {
199  if (constraint1.vars[index_1] == constraint2.vars[index_2]) {
200  scalar_product += ToDouble(constraint1.coeffs[index_1]) *
201  ToDouble(constraint2.coeffs[index_2]);
202  index_1++;
203  index_2++;
204  } else if (constraint1.vars[index_1] > constraint2.vars[index_2]) {
205  index_2++;
206  } else {
207  index_1++;
208  }
209  }
210  return scalar_product;
211 }
212 
213 namespace {
214 
215 // TODO(user): Template for any integer type and expose this?
216 IntegerValue ComputeGcd(const std::vector<IntegerValue>& values) {
217  if (values.empty()) return IntegerValue(1);
218  int64_t gcd = 0;
219  for (const IntegerValue value : values) {
220  gcd = MathUtil::GCD64(gcd, std::abs(value.value()));
221  if (gcd == 1) break;
222  }
223  if (gcd < 0) return IntegerValue(1); // Can happen with kint64min.
224  return IntegerValue(gcd);
225 }
226 
227 } // namespace
228 
229 void DivideByGCD(LinearConstraint* constraint) {
230  if (constraint->coeffs.empty()) return;
231  const IntegerValue gcd = ComputeGcd(constraint->coeffs);
232  if (gcd == 1) return;
233 
234  if (constraint->lb > kMinIntegerValue) {
235  constraint->lb = CeilRatio(constraint->lb, gcd);
236  }
237  if (constraint->ub < kMaxIntegerValue) {
238  constraint->ub = FloorRatio(constraint->ub, gcd);
239  }
240  for (IntegerValue& coeff : constraint->coeffs) coeff /= gcd;
241 }
242 
244  int new_size = 0;
245  const int size = constraint->vars.size();
246  for (int i = 0; i < size; ++i) {
247  if (constraint->coeffs[i] == 0) continue;
248  constraint->vars[new_size] = constraint->vars[i];
249  constraint->coeffs[new_size] = constraint->coeffs[i];
250  ++new_size;
251  }
252  constraint->vars.resize(new_size);
253  constraint->coeffs.resize(new_size);
254 }
255 
257  const int size = constraint->vars.size();
258  for (int i = 0; i < size; ++i) {
259  const IntegerValue coeff = constraint->coeffs[i];
260  if (coeff < 0) {
261  constraint->coeffs[i] = -coeff;
262  constraint->vars[i] = NegationOf(constraint->vars[i]);
263  }
264  }
265 }
266 
268  const int size = constraint->vars.size();
269  for (int i = 0; i < size; ++i) {
270  const IntegerVariable var = constraint->vars[i];
271  if (!VariableIsPositive(var)) {
272  constraint->coeffs[i] = -constraint->coeffs[i];
273  constraint->vars[i] = NegationOf(var);
274  }
275  }
276 }
277 
279  const absl::StrongVector<IntegerVariable, double>& lp_values) const {
280  double result = ToDouble(offset);
281  for (int i = 0; i < vars.size(); ++i) {
282  result += ToDouble(coeffs[i]) * lp_values[vars[i]];
283  }
284  return result;
285 }
286 
287 std::string LinearExpression::DebugString() const {
288  std::string result;
289  for (int i = 0; i < vars.size(); ++i) {
290  absl::StrAppend(&result, i > 0 ? " " : "",
292  }
293  if (offset != 0) {
294  absl::StrAppend(&result, " + ", offset.value());
295  }
296  return result;
297 }
298 
299 // TODO(user): it would be better if LinearConstraint natively supported
300 // term and not two separated vectors. Fix?
301 //
302 // TODO(user): This is really similar to CleanTermsAndFillConstraint(), maybe
303 // we should just make the later switch negative variable to positive ones to
304 // avoid an extra linear scan on each new cuts.
306  std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
307 
308  const int size = ct->vars.size();
309  for (int i = 0; i < size; ++i) {
310  if (VariableIsPositive(ct->vars[i])) {
311  terms.push_back({ct->vars[i], ct->coeffs[i]});
312  } else {
313  terms.push_back({NegationOf(ct->vars[i]), -ct->coeffs[i]});
314  }
315  }
316  std::sort(terms.begin(), terms.end());
317 
318  ct->vars.clear();
319  ct->coeffs.clear();
320  for (const auto& term : terms) {
321  ct->vars.push_back(term.first);
322  ct->coeffs.push_back(term.second);
323  }
324 }
325 
327  absl::flat_hash_set<IntegerVariable> seen_variables;
328  const int size = ct.vars.size();
329  for (int i = 0; i < size; ++i) {
330  if (VariableIsPositive(ct.vars[i])) {
331  if (!seen_variables.insert(ct.vars[i]).second) return false;
332  } else {
333  if (!seen_variables.insert(NegationOf(ct.vars[i])).second) return false;
334  }
335  }
336  return true;
337 }
338 
340  LinearExpression canonical_expr;
341  canonical_expr.offset = expr.offset;
342  for (int i = 0; i < expr.vars.size(); ++i) {
343  if (expr.coeffs[i] < 0) {
344  canonical_expr.vars.push_back(NegationOf(expr.vars[i]));
345  canonical_expr.coeffs.push_back(-expr.coeffs[i]);
346  } else {
347  canonical_expr.vars.push_back(expr.vars[i]);
348  canonical_expr.coeffs.push_back(expr.coeffs[i]);
349  }
350  }
351  return canonical_expr;
352 }
353 
354 IntegerValue LinExprLowerBound(const LinearExpression& expr,
355  const IntegerTrail& integer_trail) {
356  IntegerValue lower_bound = expr.offset;
357  for (int i = 0; i < expr.vars.size(); ++i) {
358  DCHECK_GE(expr.coeffs[i], 0) << "The expression is not canonicalized";
359  lower_bound += expr.coeffs[i] * integer_trail.LowerBound(expr.vars[i]);
360  }
361  return lower_bound;
362 }
363 
364 IntegerValue LinExprUpperBound(const LinearExpression& expr,
365  const IntegerTrail& integer_trail) {
366  IntegerValue upper_bound = expr.offset;
367  for (int i = 0; i < expr.vars.size(); ++i) {
368  DCHECK_GE(expr.coeffs[i], 0) << "The expression is not canonicalized";
369  upper_bound += expr.coeffs[i] * integer_trail.UpperBound(expr.vars[i]);
370  }
371  return upper_bound;
372 }
373 
374 // TODO(user): Avoid duplication with PossibleIntegerOverflow() in the checker?
375 // At least make sure the code is the same.
377  const IntegerTrail& integer_trail) {
378  int64_t positive_sum(0);
379  int64_t negative_sum(0);
380  for (int i = 0; i < constraint.vars.size(); ++i) {
381  const IntegerVariable var = constraint.vars[i];
382  const IntegerValue coeff = constraint.coeffs[i];
383  const IntegerValue lb = integer_trail.LevelZeroLowerBound(var);
384  const IntegerValue ub = integer_trail.LevelZeroUpperBound(var);
385 
386  int64_t min_prod = CapProd(coeff.value(), lb.value());
387  int64_t max_prod = CapProd(coeff.value(), ub.value());
388  if (min_prod > max_prod) std::swap(min_prod, max_prod);
389 
390  positive_sum = CapAdd(positive_sum, std::max(int64_t{0}, max_prod));
391  negative_sum = CapAdd(negative_sum, std::min(int64_t{0}, min_prod));
392  }
393 
394  const int64_t limit = std::numeric_limits<int64_t>::max();
395  if (positive_sum >= limit) return false;
396  if (negative_sum <= -limit) return false;
397  if (CapSub(positive_sum, negative_sum) >= limit) return false;
398 
399  return true;
400 }
401 
403  LinearExpression result;
404  result.vars = NegationOf(expr.vars);
405  result.coeffs = expr.coeffs;
406  result.offset = -expr.offset;
407  return result;
408 }
409 
411  LinearExpression result;
412  result.offset = expr.offset;
413  for (int i = 0; i < expr.vars.size(); ++i) {
414  if (VariableIsPositive(expr.vars[i])) {
415  result.vars.push_back(expr.vars[i]);
416  result.coeffs.push_back(expr.coeffs[i]);
417  } else {
418  result.vars.push_back(NegationOf(expr.vars[i]));
419  result.coeffs.push_back(-expr.coeffs[i]);
420  }
421  }
422  return result;
423 }
424 
425 IntegerValue GetCoefficient(const IntegerVariable var,
426  const LinearExpression& expr) {
427  for (int i = 0; i < expr.vars.size(); ++i) {
428  if (expr.vars[i] == var) {
429  return expr.coeffs[i];
430  } else if (expr.vars[i] == NegationOf(var)) {
431  return -expr.coeffs[i];
432  }
433  }
434  return IntegerValue(0);
435 }
436 
437 IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
438  const LinearExpression& expr) {
440  for (int i = 0; i < expr.vars.size(); ++i) {
441  if (expr.vars[i] == var) {
442  return expr.coeffs[i];
443  }
444  }
445  return IntegerValue(0);
446 }
447 
448 } // namespace sat
449 } // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:491
int64_t CapSub(int64_t x, int64_t y)
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
int64_t min
Definition: alldiff_cst.cc:139
double ComputeL2Norm(const LinearConstraint &constraint)
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1345
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue >> *terms, LinearConstraint *constraint)
void AddLinearExpression(const LinearExpression &expr)
std::string IntegerTermDebugString(IntegerVariable var, IntegerValue coeff)
Definition: integer.h:152
LinearExpression PositiveVarExpr(const LinearExpression &expr)
void AddTerm(IntegerVariable var, IntegerValue coeff)
int64_t CapProd(int64_t x, int64_t y)
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const IntegerVariable GetLiteralView(Literal lit) const
Definition: integer.h:454
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Definition: integer.h:70
void DivideByGCD(LinearConstraint *constraint)
int64_t max
Definition: alldiff_cst.cc:140
double upper_bound
int64_t CapAdd(int64_t x, int64_t y)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
double lower_bound
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1412
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
bool IsFixed(IntegerTrail *integer_trail) const
Definition: integer.cc:62
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:138
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
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:29
void CanonicalizeConstraint(LinearConstraint *ct)
#define DCHECK(condition)
Definition: base/logging.h:885
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:82
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1349
Collection of objects used to extend the Constraint Solver library.
const IntegerVariable kNoIntegerVariable(-1)
IntVar * var
Definition: expr_array.cc:1874
IntType IntTypeAbs(IntType t)
Definition: integer.h:78
void MakeAllVariablesPositive(LinearConstraint *constraint)
IntegerValue Min(IntegerTrail *integer_trail) const
Definition: integer.cc:38
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1407
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)