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