OR-Tools  9.0
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  // We can either add var or NegationOf(var), and we always choose the
27  // positive one.
28  if (VariableIsPositive(var)) {
29  terms_.push_back({var, coeff});
30  } else {
31  terms_.push_back({NegationOf(var), -coeff});
32  }
33 }
34 
36  IntegerValue coeff) {
37  // We can either add var or NegationOf(var), and we always choose the
38  // positive one.
39  if (expr.var != kNoIntegerVariable) {
40  if (VariableIsPositive(expr.var)) {
41  terms_.push_back({expr.var, coeff * expr.coeff});
42  } else {
43  terms_.push_back({NegationOf(expr.var), -coeff * expr.coeff});
44  }
45  }
46  if (lb_ > kMinIntegerValue) lb_ -= coeff * expr.constant;
47  if (ub_ < kMaxIntegerValue) ub_ -= coeff * expr.constant;
48 }
49 
51  if (lb_ > kMinIntegerValue) lb_ -= value;
52  if (ub_ < kMaxIntegerValue) ub_ -= value;
53 }
54 
55 ABSL_MUST_USE_RESULT bool LinearConstraintBuilder::AddLiteralTerm(
56  Literal lit, IntegerValue coeff) {
57  bool has_direct_view = encoder_.GetLiteralView(lit) != kNoIntegerVariable;
58  bool has_opposite_view =
59  encoder_.GetLiteralView(lit.Negated()) != kNoIntegerVariable;
60 
61  // If a literal has both views, we want to always keep the same
62  // representative: the smallest IntegerVariable. Note that AddTerm() will
63  // also make sure to use the associated positive variable.
64  if (has_direct_view && has_opposite_view) {
65  if (encoder_.GetLiteralView(lit) <=
66  encoder_.GetLiteralView(lit.Negated())) {
67  has_opposite_view = false;
68  } else {
69  has_direct_view = false;
70  }
71  }
72  if (has_direct_view) {
73  AddTerm(encoder_.GetLiteralView(lit), coeff);
74  return true;
75  }
76  if (has_opposite_view) {
77  AddTerm(encoder_.GetLiteralView(lit.Negated()), -coeff);
78  if (lb_ > kMinIntegerValue) lb_ -= coeff;
79  if (ub_ < kMaxIntegerValue) ub_ -= coeff;
80  return true;
81  }
82  return false;
83 }
84 
86  std::vector<std::pair<IntegerVariable, IntegerValue>>* terms,
87  LinearConstraint* constraint) {
88  constraint->vars.clear();
89  constraint->coeffs.clear();
90 
91  // Sort and add coeff of duplicate variables. Note that a variable and
92  // its negation will appear one after another in the natural order.
93  std::sort(terms->begin(), terms->end());
94  IntegerVariable previous_var = kNoIntegerVariable;
95  IntegerValue current_coeff(0);
96  for (const std::pair<IntegerVariable, IntegerValue> entry : *terms) {
97  if (previous_var == entry.first) {
98  current_coeff += entry.second;
99  } else if (previous_var == NegationOf(entry.first)) {
100  current_coeff -= entry.second;
101  } else {
102  if (current_coeff != 0) {
103  constraint->vars.push_back(previous_var);
104  constraint->coeffs.push_back(current_coeff);
105  }
106  previous_var = entry.first;
107  current_coeff = entry.second;
108  }
109  }
110  if (current_coeff != 0) {
111  constraint->vars.push_back(previous_var);
112  constraint->coeffs.push_back(current_coeff);
113  }
114 }
115 
117  LinearConstraint result;
118  result.lb = lb_;
119  result.ub = ub_;
120  CleanTermsAndFillConstraint(&terms_, &result);
121  return result;
122 }
123 
125  const LinearConstraint& constraint,
127  double activity = 0;
128  for (int i = 0; i < constraint.vars.size(); ++i) {
129  const IntegerVariable var = constraint.vars[i];
130  const IntegerValue coeff = constraint.coeffs[i];
131  activity += coeff.value() * values[var];
132  }
133  return activity;
134 }
135 
136 double ComputeL2Norm(const LinearConstraint& constraint) {
137  double sum = 0.0;
138  for (const IntegerValue coeff : constraint.coeffs) {
139  sum += ToDouble(coeff) * ToDouble(coeff);
140  }
141  return std::sqrt(sum);
142 }
143 
144 IntegerValue ComputeInfinityNorm(const LinearConstraint& constraint) {
145  IntegerValue result(0);
146  for (const IntegerValue coeff : constraint.coeffs) {
147  result = std::max(result, IntTypeAbs(coeff));
148  }
149  return result;
150 }
151 
152 double ScalarProduct(const LinearConstraint& constraint1,
153  const LinearConstraint& constraint2) {
154  DCHECK(std::is_sorted(constraint1.vars.begin(), constraint1.vars.end()));
155  DCHECK(std::is_sorted(constraint2.vars.begin(), constraint2.vars.end()));
156  double scalar_product = 0.0;
157  int index_1 = 0;
158  int index_2 = 0;
159  while (index_1 < constraint1.vars.size() &&
160  index_2 < constraint2.vars.size()) {
161  if (constraint1.vars[index_1] == constraint2.vars[index_2]) {
162  scalar_product += ToDouble(constraint1.coeffs[index_1]) *
163  ToDouble(constraint2.coeffs[index_2]);
164  index_1++;
165  index_2++;
166  } else if (constraint1.vars[index_1] > constraint2.vars[index_2]) {
167  index_2++;
168  } else {
169  index_1++;
170  }
171  }
172  return scalar_product;
173 }
174 
175 namespace {
176 
177 // TODO(user): Template for any integer type and expose this?
178 IntegerValue ComputeGcd(const std::vector<IntegerValue>& values) {
179  if (values.empty()) return IntegerValue(1);
180  int64_t gcd = 0;
181  for (const IntegerValue value : values) {
182  gcd = MathUtil::GCD64(gcd, std::abs(value.value()));
183  if (gcd == 1) break;
184  }
185  if (gcd < 0) return IntegerValue(1); // Can happen with kint64min.
186  return IntegerValue(gcd);
187 }
188 
189 } // namespace
190 
191 void DivideByGCD(LinearConstraint* constraint) {
192  if (constraint->coeffs.empty()) return;
193  const IntegerValue gcd = ComputeGcd(constraint->coeffs);
194  if (gcd == 1) return;
195 
196  if (constraint->lb > kMinIntegerValue) {
197  constraint->lb = CeilRatio(constraint->lb, gcd);
198  }
199  if (constraint->ub < kMaxIntegerValue) {
200  constraint->ub = FloorRatio(constraint->ub, gcd);
201  }
202  for (IntegerValue& coeff : constraint->coeffs) coeff /= gcd;
203 }
204 
206  int new_size = 0;
207  const int size = constraint->vars.size();
208  for (int i = 0; i < size; ++i) {
209  if (constraint->coeffs[i] == 0) continue;
210  constraint->vars[new_size] = constraint->vars[i];
211  constraint->coeffs[new_size] = constraint->coeffs[i];
212  ++new_size;
213  }
214  constraint->vars.resize(new_size);
215  constraint->coeffs.resize(new_size);
216 }
217 
219  const int size = constraint->vars.size();
220  for (int i = 0; i < size; ++i) {
221  const IntegerValue coeff = constraint->coeffs[i];
222  if (coeff < 0) {
223  constraint->coeffs[i] = -coeff;
224  constraint->vars[i] = NegationOf(constraint->vars[i]);
225  }
226  }
227 }
228 
230  const int size = constraint->vars.size();
231  for (int i = 0; i < size; ++i) {
232  const IntegerVariable var = constraint->vars[i];
233  if (!VariableIsPositive(var)) {
234  constraint->coeffs[i] = -constraint->coeffs[i];
235  constraint->vars[i] = NegationOf(var);
236  }
237  }
238 }
239 
240 // TODO(user): it would be better if LinearConstraint natively supported
241 // term and not two separated vectors. Fix?
242 //
243 // TODO(user): This is really similar to CleanTermsAndFillConstraint(), maybe
244 // we should just make the later switch negative variable to positive ones to
245 // avoid an extra linear scan on each new cuts.
247  std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
248 
249  const int size = ct->vars.size();
250  for (int i = 0; i < size; ++i) {
251  if (VariableIsPositive(ct->vars[i])) {
252  terms.push_back({ct->vars[i], ct->coeffs[i]});
253  } else {
254  terms.push_back({NegationOf(ct->vars[i]), -ct->coeffs[i]});
255  }
256  }
257  std::sort(terms.begin(), terms.end());
258 
259  ct->vars.clear();
260  ct->coeffs.clear();
261  for (const auto& term : terms) {
262  ct->vars.push_back(term.first);
263  ct->coeffs.push_back(term.second);
264  }
265 }
266 
268  absl::flat_hash_set<IntegerVariable> seen_variables;
269  const int size = ct.vars.size();
270  for (int i = 0; i < size; ++i) {
271  if (VariableIsPositive(ct.vars[i])) {
272  if (!seen_variables.insert(ct.vars[i]).second) return false;
273  } else {
274  if (!seen_variables.insert(NegationOf(ct.vars[i])).second) return false;
275  }
276  }
277  return true;
278 }
279 
281  LinearExpression canonical_expr;
282  canonical_expr.offset = expr.offset;
283  for (int i = 0; i < expr.vars.size(); ++i) {
284  if (expr.coeffs[i] < 0) {
285  canonical_expr.vars.push_back(NegationOf(expr.vars[i]));
286  canonical_expr.coeffs.push_back(-expr.coeffs[i]);
287  } else {
288  canonical_expr.vars.push_back(expr.vars[i]);
289  canonical_expr.coeffs.push_back(expr.coeffs[i]);
290  }
291  }
292  return canonical_expr;
293 }
294 
295 IntegerValue LinExprLowerBound(const LinearExpression& expr,
296  const IntegerTrail& integer_trail) {
297  IntegerValue lower_bound = expr.offset;
298  for (int i = 0; i < expr.vars.size(); ++i) {
299  DCHECK_GE(expr.coeffs[i], 0) << "The expression is not canonicalized";
300  lower_bound += expr.coeffs[i] * integer_trail.LowerBound(expr.vars[i]);
301  }
302  return lower_bound;
303 }
304 
305 IntegerValue LinExprUpperBound(const LinearExpression& expr,
306  const IntegerTrail& integer_trail) {
307  IntegerValue upper_bound = expr.offset;
308  for (int i = 0; i < expr.vars.size(); ++i) {
309  DCHECK_GE(expr.coeffs[i], 0) << "The expression is not canonicalized";
310  upper_bound += expr.coeffs[i] * integer_trail.UpperBound(expr.vars[i]);
311  }
312  return upper_bound;
313 }
314 
316  LinearExpression result;
317  result.vars = NegationOf(expr.vars);
318  result.coeffs = expr.coeffs;
319  result.offset = -expr.offset;
320  return result;
321 }
322 
324  LinearExpression result;
325  result.offset = expr.offset;
326  for (int i = 0; i < expr.vars.size(); ++i) {
327  if (VariableIsPositive(expr.vars[i])) {
328  result.vars.push_back(expr.vars[i]);
329  result.coeffs.push_back(expr.coeffs[i]);
330  } else {
331  result.vars.push_back(NegationOf(expr.vars[i]));
332  result.coeffs.push_back(-expr.coeffs[i]);
333  }
334  }
335  return result;
336 }
337 
338 IntegerValue GetCoefficient(const IntegerVariable var,
339  const LinearExpression& expr) {
340  for (int i = 0; i < expr.vars.size(); ++i) {
341  if (expr.vars[i] == var) {
342  return expr.coeffs[i];
343  } else if (expr.vars[i] == NegationOf(var)) {
344  return -expr.coeffs[i];
345  }
346  }
347  return IntegerValue(0);
348 }
349 
350 IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
351  const LinearExpression& expr) {
353  for (int i = 0; i < expr.vars.size(); ++i) {
354  if (expr.vars[i] == var) {
355  return expr.coeffs[i];
356  }
357  }
358  return IntegerValue(0);
359 }
360 
361 } // namespace sat
362 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
#define CHECK(condition)
Definition: base/logging.h:498
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK(condition)
Definition: base/logging.h:892
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
const IntegerVariable GetLiteralView(Literal lit) const
Definition: integer.h:425
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1309
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1305
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddTerm(IntegerVariable var, IntegerValue coeff)
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
IntType IntTypeAbs(IntType t)
Definition: integer.h:78
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:82
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
void RemoveZeroTerms(LinearConstraint *constraint)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
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:29
IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var, const LinearExpression &expr)
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue >> *terms, LinearConstraint *constraint)
IntegerValue LinExprUpperBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:135
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Definition: integer.h:70
Collection of objects used to extend the Constraint Solver library.