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