OR-Tools  9.6
linear_constraint.cc
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 
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <cstdint>
19 #include <cstdlib>
20 #include <limits>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include "absl/base/attributes.h"
26 #include "absl/container/flat_hash_set.h"
27 #include "absl/strings/str_cat.h"
28 #include "ortools/base/logging.h"
29 #include "ortools/base/mathutil.h"
31 #include "ortools/sat/integer.h"
32 #include "ortools/sat/sat_base.h"
35 
36 namespace operations_research {
37 namespace sat {
38 
39 void LinearConstraintBuilder::AddTerm(IntegerVariable var, IntegerValue coeff) {
40  if (coeff == 0) return;
41  // We can either add var or NegationOf(var), and we always choose the
42  // positive one.
43  if (VariableIsPositive(var)) {
44  terms_.push_back({var, coeff});
45  } else {
46  terms_.push_back({NegationOf(var), -coeff});
47  }
48 }
49 
51  IntegerValue coeff) {
52  if (coeff == 0) return;
53  // We can either add var or NegationOf(var), and we always choose the
54  // positive one.
55  if (expr.var != kNoIntegerVariable) {
56  if (VariableIsPositive(expr.var)) {
57  terms_.push_back({expr.var, coeff * expr.coeff});
58  } else {
59  terms_.push_back({NegationOf(expr.var), -coeff * expr.coeff});
60  }
61  }
62  offset_ += coeff * expr.constant;
63 }
64 
66  const LinearExpression& expr) {
67  AddLinearExpression(expr, IntegerValue(1));
68 }
69 
71  IntegerValue coeff) {
72  for (int i = 0; i < expr.vars.size(); ++i) {
73  // We must use positive variables.
74  if (VariableIsPositive(expr.vars[i])) {
75  terms_.push_back({expr.vars[i], expr.coeffs[i] * coeff});
76  } else {
77  terms_.push_back({NegationOf(expr.vars[i]), -expr.coeffs[i] * coeff});
78  }
79  }
80  offset_ += expr.offset * coeff;
81 }
82 
84  const std::vector<LiteralValueValue>& product) {
85  if (product.empty()) return true;
86 
87  IntegerValue product_min = kMaxIntegerValue;
88  // TODO(user): Checks the value of literals.
89  for (const LiteralValueValue& term : product) {
90  product_min = std::min(product_min, term.left_value * term.right_value);
91  }
92 
93  for (const LiteralValueValue& term : product) {
94  IntegerValue coeff = term.left_value * term.right_value - product_min;
95  if (coeff == 0) continue;
96  if (!AddLiteralTerm(term.literal, coeff)) {
97  return false;
98  }
99  }
100  AddConstant(product_min);
101  return true;
102 }
103 
105  AffineExpression left, AffineExpression right, IntegerTrail* integer_trail,
106  bool* is_quadratic) {
107  if (integer_trail->IsFixed(left)) {
108  AddTerm(right, integer_trail->FixedValue(left));
109  } else if (integer_trail->IsFixed(right)) {
110  AddTerm(left, integer_trail->FixedValue(right));
111  } else {
112  const IntegerValue left_min = integer_trail->LowerBound(left);
113  const IntegerValue right_min = integer_trail->LowerBound(right);
114  AddTerm(left, right_min);
115  AddTerm(right, left_min);
116  // Substract the energy counted twice.
117  AddConstant(-left_min * right_min);
118  if (is_quadratic != nullptr) *is_quadratic = true;
119  }
120 }
121 
123  offset_ += value;
124 }
125 
126 ABSL_MUST_USE_RESULT bool LinearConstraintBuilder::AddLiteralTerm(
127  Literal lit, IntegerValue coeff) {
128  DCHECK(encoder_ != nullptr);
129  IntegerVariable var = kNoIntegerVariable;
130  bool view_is_direct = true;
131  if (!encoder_->LiteralOrNegationHasView(lit, &var, &view_is_direct)) {
132  return false;
133  }
134 
135  if (view_is_direct) {
136  AddTerm(var, coeff);
137  } else {
138  AddTerm(var, -coeff);
139  offset_ += coeff;
140  }
141  return true;
142 }
143 
145  return BuildConstraint(lb_, ub_);
146 }
147 
149  IntegerValue ub) {
150  LinearConstraint result;
151  result.lb = lb > kMinIntegerValue ? lb - offset_ : lb;
152  result.ub = ub < kMaxIntegerValue ? ub - offset_ : ub;
153  CleanTermsAndFillConstraint(&terms_, &result);
154  return result;
155 }
156 
158  LinearExpression result;
159  CleanTermsAndFillConstraint(&terms_, &result);
160  result.offset = offset_;
161  return result;
162 }
163 
165  const LinearConstraint& constraint,
167  int i = 0;
168  const int size = static_cast<int>(constraint.vars.size());
169  const int shifted_size = size - 3;
170  double a0 = 0.0;
171  double a1 = 0.0;
172  double a2 = 0.0;
173  double a3 = 0.0;
174  for (; i < shifted_size; i += 4) {
175  a0 += static_cast<double>(constraint.coeffs[i].value()) *
176  values[constraint.vars[i]];
177  a1 += static_cast<double>(constraint.coeffs[i + 1].value()) *
178  values[constraint.vars[i + 1]];
179  a2 += static_cast<double>(constraint.coeffs[i + 2].value()) *
180  values[constraint.vars[i + 2]];
181  a3 += static_cast<double>(constraint.coeffs[i + 3].value()) *
182  values[constraint.vars[i + 3]];
183  }
184  double activity = a0 + a1 + a2 + a3;
185  if (i < size) {
186  activity += static_cast<double>(constraint.coeffs[i].value()) *
187  values[constraint.vars[i]];
188  if (i + 1 < size) {
189  activity += static_cast<double>(constraint.coeffs[i + 1].value()) *
190  values[constraint.vars[i + 1]];
191  if (i + 2 < size) {
192  activity += static_cast<double>(constraint.coeffs[i + 2].value()) *
193  values[constraint.vars[i + 2]];
194  }
195  }
196  }
197  return activity;
198 }
199 
200 double ComputeL2Norm(const LinearConstraint& constraint) {
201  double sum = 0.0;
202  for (const IntegerValue coeff : constraint.coeffs) {
203  sum += ToDouble(coeff) * ToDouble(coeff);
204  }
205  return std::sqrt(sum);
206 }
207 
208 IntegerValue ComputeInfinityNorm(const LinearConstraint& constraint) {
209  IntegerValue result(0);
210  for (const IntegerValue coeff : constraint.coeffs) {
211  result = std::max(result, IntTypeAbs(coeff));
212  }
213  return result;
214 }
215 
216 double ScalarProduct(const LinearConstraint& constraint1,
217  const LinearConstraint& constraint2) {
218  DCHECK(std::is_sorted(constraint1.vars.begin(), constraint1.vars.end()));
219  DCHECK(std::is_sorted(constraint2.vars.begin(), constraint2.vars.end()));
220  double scalar_product = 0.0;
221  int index_1 = 0;
222  int index_2 = 0;
223  while (index_1 < constraint1.vars.size() &&
224  index_2 < constraint2.vars.size()) {
225  if (constraint1.vars[index_1] == constraint2.vars[index_2]) {
226  scalar_product += ToDouble(constraint1.coeffs[index_1]) *
227  ToDouble(constraint2.coeffs[index_2]);
228  index_1++;
229  index_2++;
230  } else if (constraint1.vars[index_1] > constraint2.vars[index_2]) {
231  index_2++;
232  } else {
233  index_1++;
234  }
235  }
236  return scalar_product;
237 }
238 
239 namespace {
240 
241 // TODO(user): Template for any integer type and expose this?
242 IntegerValue ComputeGcd(const std::vector<IntegerValue>& values) {
243  if (values.empty()) return IntegerValue(1);
244  int64_t gcd = 0;
245  for (const IntegerValue value : values) {
246  gcd = MathUtil::GCD64(gcd, std::abs(value.value()));
247  if (gcd == 1) break;
248  }
249  if (gcd < 0) return IntegerValue(1); // Can happen with kint64min.
250  return IntegerValue(gcd);
251 }
252 
253 } // namespace
254 
255 void DivideByGCD(LinearConstraint* constraint) {
256  if (constraint->coeffs.empty()) return;
257  const IntegerValue gcd = ComputeGcd(constraint->coeffs);
258  if (gcd == 1) return;
259 
260  if (constraint->lb > kMinIntegerValue) {
261  constraint->lb = CeilRatio(constraint->lb, gcd);
262  }
263  if (constraint->ub < kMaxIntegerValue) {
264  constraint->ub = FloorRatio(constraint->ub, gcd);
265  }
266  for (IntegerValue& coeff : constraint->coeffs) coeff /= gcd;
267 }
268 
270  int new_size = 0;
271  const int size = constraint->vars.size();
272  for (int i = 0; i < size; ++i) {
273  if (constraint->coeffs[i] == 0) continue;
274  constraint->vars[new_size] = constraint->vars[i];
275  constraint->coeffs[new_size] = constraint->coeffs[i];
276  ++new_size;
277  }
278  constraint->vars.resize(new_size);
279  constraint->coeffs.resize(new_size);
280 }
281 
283  const int size = constraint->vars.size();
284  for (int i = 0; i < size; ++i) {
285  const IntegerValue coeff = constraint->coeffs[i];
286  if (coeff < 0) {
287  constraint->coeffs[i] = -coeff;
288  constraint->vars[i] = NegationOf(constraint->vars[i]);
289  }
290  }
291 }
292 
294  const int size = constraint->vars.size();
295  for (int i = 0; i < size; ++i) {
296  const IntegerVariable var = constraint->vars[i];
297  if (!VariableIsPositive(var)) {
298  constraint->coeffs[i] = -constraint->coeffs[i];
299  constraint->vars[i] = NegationOf(var);
300  }
301  }
302 }
303 
305  const absl::StrongVector<IntegerVariable, double>& lp_values) const {
306  double result = ToDouble(offset);
307  for (int i = 0; i < vars.size(); ++i) {
308  result += ToDouble(coeffs[i]) * lp_values[vars[i]];
309  }
310  return result;
311 }
312 
313 IntegerValue LinearExpression::LevelZeroMin(IntegerTrail* integer_trail) const {
314  IntegerValue result = offset;
315  for (int i = 0; i < vars.size(); ++i) {
316  DCHECK_GE(coeffs[i], 0);
317  result += coeffs[i] * integer_trail->LevelZeroLowerBound(vars[i]);
318  }
319  return result;
320 }
321 
322 IntegerValue LinearExpression::Min(const IntegerTrail& integer_trail) const {
323  IntegerValue result = offset;
324  for (int i = 0; i < vars.size(); ++i) {
325  if (coeffs[i] > 0) {
326  result += coeffs[i] * integer_trail.LowerBound(vars[i]);
327  } else {
328  result += coeffs[i] * integer_trail.UpperBound(vars[i]);
329  }
330  }
331  return result;
332 }
333 
334 IntegerValue LinearExpression::Max(const IntegerTrail& integer_trail) const {
335  IntegerValue result = offset;
336  for (int i = 0; i < vars.size(); ++i) {
337  if (coeffs[i] > 0) {
338  result += coeffs[i] * integer_trail.UpperBound(vars[i]);
339  } else {
340  result += coeffs[i] * integer_trail.LowerBound(vars[i]);
341  }
342  }
343  return result;
344 }
345 
346 std::string LinearExpression::DebugString() const {
347  if (vars.empty()) return absl::StrCat(offset.value());
348  std::string result;
349  for (int i = 0; i < vars.size(); ++i) {
350  absl::StrAppend(&result, i > 0 ? " " : "",
352  }
353  if (offset != 0) {
354  absl::StrAppend(&result, " + ", offset.value());
355  }
356  return result;
357 }
358 
359 // TODO(user): it would be better if LinearConstraint natively supported
360 // term and not two separated vectors. Fix?
361 //
362 // TODO(user): This is really similar to CleanTermsAndFillConstraint(), maybe
363 // we should just make the later switch negative variable to positive ones to
364 // avoid an extra linear scan on each new cuts.
366  std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
367 
368  const int size = ct->vars.size();
369  for (int i = 0; i < size; ++i) {
370  if (VariableIsPositive(ct->vars[i])) {
371  terms.push_back({ct->vars[i], ct->coeffs[i]});
372  } else {
373  terms.push_back({NegationOf(ct->vars[i]), -ct->coeffs[i]});
374  }
375  }
376  std::sort(terms.begin(), terms.end());
377 
378  ct->vars.clear();
379  ct->coeffs.clear();
380  for (const auto& term : terms) {
381  ct->vars.push_back(term.first);
382  ct->coeffs.push_back(term.second);
383  }
384 }
385 
387  absl::flat_hash_set<IntegerVariable> seen_variables;
388  const int size = ct.vars.size();
389  for (int i = 0; i < size; ++i) {
390  if (VariableIsPositive(ct.vars[i])) {
391  if (!seen_variables.insert(ct.vars[i]).second) return false;
392  } else {
393  if (!seen_variables.insert(NegationOf(ct.vars[i])).second) return false;
394  }
395  }
396  return true;
397 }
398 
400  LinearExpression canonical_expr;
401  canonical_expr.offset = expr.offset;
402  for (int i = 0; i < expr.vars.size(); ++i) {
403  if (expr.coeffs[i] < 0) {
404  canonical_expr.vars.push_back(NegationOf(expr.vars[i]));
405  canonical_expr.coeffs.push_back(-expr.coeffs[i]);
406  } else {
407  canonical_expr.vars.push_back(expr.vars[i]);
408  canonical_expr.coeffs.push_back(expr.coeffs[i]);
409  }
410  }
411  return canonical_expr;
412 }
413 
414 // TODO(user): Avoid duplication with PossibleIntegerOverflow() in the checker?
415 // At least make sure the code is the same.
417  const IntegerTrail& integer_trail) {
418  int64_t positive_sum(0);
419  int64_t negative_sum(0);
420  for (int i = 0; i < constraint.vars.size(); ++i) {
421  const IntegerVariable var = constraint.vars[i];
422  const IntegerValue coeff = constraint.coeffs[i];
423  const IntegerValue lb = integer_trail.LevelZeroLowerBound(var);
424  const IntegerValue ub = integer_trail.LevelZeroUpperBound(var);
425 
426  int64_t min_prod = CapProd(coeff.value(), lb.value());
427  int64_t max_prod = CapProd(coeff.value(), ub.value());
428  if (min_prod > max_prod) std::swap(min_prod, max_prod);
429 
430  positive_sum = CapAdd(positive_sum, std::max(int64_t{0}, max_prod));
431  negative_sum = CapAdd(negative_sum, std::min(int64_t{0}, min_prod));
432  }
433 
434  const int64_t limit = std::numeric_limits<int64_t>::max();
435  if (positive_sum >= limit) return false;
436  if (negative_sum <= -limit) return false;
437  if (CapSub(positive_sum, negative_sum) >= limit) return false;
438 
439  return true;
440 }
441 
443  LinearExpression result;
444  result.vars = NegationOf(expr.vars);
445  result.coeffs = expr.coeffs;
446  result.offset = -expr.offset;
447  return result;
448 }
449 
451  LinearExpression result;
452  result.offset = expr.offset;
453  for (int i = 0; i < expr.vars.size(); ++i) {
454  if (VariableIsPositive(expr.vars[i])) {
455  result.vars.push_back(expr.vars[i]);
456  result.coeffs.push_back(expr.coeffs[i]);
457  } else {
458  result.vars.push_back(NegationOf(expr.vars[i]));
459  result.coeffs.push_back(-expr.coeffs[i]);
460  }
461  }
462  return result;
463 }
464 
465 IntegerValue GetCoefficient(const IntegerVariable var,
466  const LinearExpression& expr) {
467  for (int i = 0; i < expr.vars.size(); ++i) {
468  if (expr.vars[i] == var) {
469  return expr.coeffs[i];
470  } else if (expr.vars[i] == NegationOf(var)) {
471  return -expr.coeffs[i];
472  }
473  }
474  return IntegerValue(0);
475 }
476 
477 IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
478  const LinearExpression& expr) {
479  CHECK(VariableIsPositive(var));
480  for (int i = 0; i < expr.vars.size(); ++i) {
481  if (expr.vars[i] == var) {
482  return expr.coeffs[i];
483  }
484  }
485  return IntegerValue(0);
486 }
487 
488 } // namespace sat
489 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
ABSL_MUST_USE_RESULT bool LiteralOrNegationHasView(Literal lit, IntegerVariable *view=nullptr, bool *view_is_direct=nullptr) const
Definition: integer.cc:559
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1565
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1561
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1646
IntegerValue FixedValue(IntegerVariable i) const
Definition: integer.h:1569
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1641
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1557
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 AddTerm(IntegerVariable var, IntegerValue coeff)
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:269
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:98
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
Definition: integer.h:85
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:89
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)
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:145
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Definition: integer.h:77
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
if(!yyg->yy_init)
Definition: parser.yy.cc:965
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