OR-Tools  9.2
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 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
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
151double 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
159IntegerValue 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
167double 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
190namespace {
191
192// TODO(user): Template for any integer type and expose this?
193IntegerValue 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
206void 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
264IntegerValue 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
273IntegerValue 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
281std::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
348IntegerValue 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
358IntegerValue 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
419IntegerValue 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
431IntegerValue 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
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 CHECK_GE(val1, val2)
Definition: base/logging.h:706
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:894
#define DCHECK(condition)
Definition: base/logging.h:889
static int64_t GCD64(int64_t x, int64_t y)
Definition: mathutil.h:107
const IntegerVariable GetLiteralView(Literal lit) const
Definition: integer.h:493
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1443
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1439
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1524
IntegerValue FixedValue(IntegerVariable i) const
Definition: integer.h:1447
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1519
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1435
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:92
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:79
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:83
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::string IntegerTermDebugString(IntegerVariable var, IntegerValue coeff)
Definition: integer.h:153
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 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:30
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:139
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
double ToDouble(IntegerValue value)
Definition: integer.h:71
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