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
20#include "ortools/sat/integer.h"
21
22namespace operations_research {
23namespace sat {
24
25void 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.
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
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
174double 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
182IntegerValue 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
190double 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
213namespace {
214
215// TODO(user): Template for any integer type and expose this?
216IntegerValue 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
229void 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
287std::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
354IntegerValue 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
364IntegerValue 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
425IntegerValue 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
437IntegerValue 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
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:491
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:890
#define DCHECK(condition)
Definition: base/logging.h:885
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
const IntegerVariable GetLiteralView(Literal lit) const
Definition: integer.h:454
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1349
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1412
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1407
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1345
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail)
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
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue > > *terms, LinearConstraint *constraint)
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)
std::string IntegerTermDebugString(IntegerVariable var, IntegerValue coeff)
Definition: integer.h:152
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)
IntegerValue LinExprUpperBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:138
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.
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)
IntegerValue Min(IntegerTrail *integer_trail) const
Definition: integer.cc:38
bool IsFixed(IntegerTrail *integer_trail) const
Definition: integer.cc:62
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const