Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
linear_constraint.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <cstddef>
19#include <cstdint>
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/log/check.h"
28#include "absl/strings/str_cat.h"
29#include "absl/types/span.h"
32#include "ortools/sat/integer.h"
36
37namespace operations_research {
38namespace sat {
39
40void LinearConstraintBuilder::AddTerm(IntegerVariable var, IntegerValue coeff) {
41 if (coeff == 0) return;
42 // We can either add var or NegationOf(var), and we always choose the
43 // positive one.
45 terms_.push_back({var, coeff});
46 } else {
47 terms_.push_back({NegationOf(var), -coeff});
48 }
49}
50
52 IntegerValue coeff) {
53 if (coeff == 0) return;
54 // We can either add var or NegationOf(var), and we always choose the
55 // positive one.
56 if (expr.var != kNoIntegerVariable) {
57 if (VariableIsPositive(expr.var)) {
58 terms_.push_back({expr.var, coeff * expr.coeff});
59 } else {
60 terms_.push_back({NegationOf(expr.var), -coeff * expr.coeff});
61 }
62 }
63 offset_ += coeff * expr.constant;
64}
65
67 const LinearExpression& expr) {
68 AddLinearExpression(expr, IntegerValue(1));
69}
70
72 IntegerValue coeff) {
73 for (int i = 0; i < expr.vars.size(); ++i) {
74 // We must use positive variables.
75 if (VariableIsPositive(expr.vars[i])) {
76 terms_.push_back({expr.vars[i], expr.coeffs[i] * coeff});
77 } else {
78 terms_.push_back({NegationOf(expr.vars[i]), -expr.coeffs[i] * coeff});
79 }
80 }
81 offset_ += expr.offset * coeff;
82}
83
85 absl::Span<const LiteralValueValue> product) {
86 if (product.empty()) return true;
87
88 IntegerValue product_min = kMaxIntegerValue;
89 // TODO(user): Checks the value of literals.
90 for (const LiteralValueValue& term : product) {
91 product_min = std::min(product_min, term.left_value * term.right_value);
92 }
93
94 for (const LiteralValueValue& term : product) {
95 IntegerValue coeff = term.left_value * term.right_value - product_min;
96 if (coeff == 0) continue;
97 if (!AddLiteralTerm(term.literal, coeff)) {
98 return false;
99 }
100 }
101 AddConstant(product_min);
102 return true;
103}
104
106 AffineExpression left, AffineExpression right, IntegerTrail* integer_trail,
107 bool* is_quadratic) {
108 if (integer_trail->IsFixed(left)) {
109 AddTerm(right, integer_trail->FixedValue(left));
110 } else if (integer_trail->IsFixed(right)) {
111 AddTerm(left, integer_trail->FixedValue(right));
112 } else {
113 const IntegerValue left_min = integer_trail->LowerBound(left);
114 const IntegerValue right_min = integer_trail->LowerBound(right);
115 AddTerm(left, right_min);
116 AddTerm(right, left_min);
117 // Substract the energy counted twice.
118 AddConstant(-left_min * right_min);
119 if (is_quadratic != nullptr) *is_quadratic = true;
120 }
121}
122
124 offset_ += value;
125}
126
128 Literal lit, IntegerValue coeff) {
129 DCHECK(encoder_ != nullptr);
130 IntegerVariable var = kNoIntegerVariable;
131 bool view_is_direct = true;
132 if (!encoder_->LiteralOrNegationHasView(lit, &var, &view_is_direct)) {
133 return false;
134 }
135
136 if (view_is_direct) {
137 AddTerm(var, coeff);
138 } else {
139 AddTerm(var, -coeff);
140 offset_ += coeff;
141 }
142 return true;
143}
144
148
150 IntegerValue ub) {
151 LinearConstraint result;
152 result.lb = lb > kMinIntegerValue ? lb - offset_ : lb;
153 result.ub = ub < kMaxIntegerValue ? ub - offset_ : ub;
154 CleanTermsAndFillConstraint(&terms_, &result);
155 return result;
156}
157
159 LinearExpression result;
160 CleanTermsAndFillConstraint(&terms_, &result);
161 result.offset = offset_;
162 return result;
163}
164
166 const LinearConstraint& constraint,
168 int i = 0;
169 const int size = constraint.num_terms;
170 const int shifted_size = size - 3;
171 double a0 = 0.0;
172 double a1 = 0.0;
173 double a2 = 0.0;
174 double a3 = 0.0;
175 for (; i < shifted_size; i += 4) {
176 a0 += static_cast<double>(constraint.coeffs[i].value()) *
177 values[constraint.vars[i]];
178 a1 += static_cast<double>(constraint.coeffs[i + 1].value()) *
179 values[constraint.vars[i + 1]];
180 a2 += static_cast<double>(constraint.coeffs[i + 2].value()) *
181 values[constraint.vars[i + 2]];
182 a3 += static_cast<double>(constraint.coeffs[i + 3].value()) *
183 values[constraint.vars[i + 3]];
184 }
185 double activity = a0 + a1 + a2 + a3;
186 if (i < size) {
187 activity += static_cast<double>(constraint.coeffs[i].value()) *
188 values[constraint.vars[i]];
189 if (i + 1 < size) {
190 activity += static_cast<double>(constraint.coeffs[i + 1].value()) *
191 values[constraint.vars[i + 1]];
192 if (i + 2 < size) {
193 activity += static_cast<double>(constraint.coeffs[i + 2].value()) *
194 values[constraint.vars[i + 2]];
195 }
196 }
197 }
198 return activity;
199}
200
202 double sum = 0.0;
203 for (int i = 0; i < ct.num_terms; ++i) {
204 sum += ToDouble(ct.coeffs[i]) * ToDouble(ct.coeffs[i]);
205 }
206 return std::sqrt(sum);
207}
208
210 IntegerValue result(0);
211 for (int i = 0; i < ct.num_terms; ++i) {
212 result = std::max(result, IntTypeAbs(ct.coeffs[i]));
213 }
214 return result;
215}
216
217double ScalarProduct(const LinearConstraint& ct1, const LinearConstraint& ct2) {
218 if (ct1.num_terms == 0 || ct2.num_terms == 0) return 0.0;
219 DCHECK(std::is_sorted(ct1.vars.get(), ct1.vars.get() + ct1.num_terms));
220 DCHECK(std::is_sorted(ct2.vars.get(), ct2.vars.get() + ct2.num_terms));
221 double scalar_product = 0.0;
222 int index_1 = 0;
223 int index_2 = 0;
224 IntegerVariable var1 = ct1.vars[index_1];
225 IntegerVariable var2 = ct2.vars[index_2];
226 while (true) {
227 if (var1 == var2) {
228 scalar_product += static_cast<double>(ct1.coeffs[index_1].value()) *
229 static_cast<double>(ct2.coeffs[index_2].value());
230 if (++index_1 == ct1.num_terms) break;
231 if (++index_2 == ct2.num_terms) break;
232 var1 = ct1.vars[index_1];
233 var2 = ct2.vars[index_2];
234 } else if (var1 > var2) {
235 if (++index_2 == ct2.num_terms) break;
236 var2 = ct2.vars[index_2];
237 } else {
238 if (++index_1 == ct1.num_terms) break;
239 var1 = ct1.vars[index_1];
240 }
241 }
242 return scalar_product;
243}
244
245namespace {
246
247// TODO(user): Template for any integer type and expose this?
248IntegerValue ComputeGcd(absl::Span<const IntegerValue> values) {
249 if (values.empty()) return IntegerValue(1);
250 int64_t gcd = 0;
251 for (const IntegerValue value : values) {
252 gcd = MathUtil::GCD64(gcd, std::abs(value.value()));
253 if (gcd == 1) break;
254 }
255 if (gcd < 0) return IntegerValue(1); // Can happen with kint64min.
256 return IntegerValue(gcd);
257}
258
259} // namespace
260
261void DivideByGCD(LinearConstraint* constraint) {
262 if (constraint->num_terms == 0) return;
263 const IntegerValue gcd = ComputeGcd(
264 {constraint->coeffs.get(), static_cast<size_t>(constraint->num_terms)});
265 if (gcd == 1) return;
266
267 if (constraint->lb > kMinIntegerValue) {
268 constraint->lb = CeilRatio(constraint->lb, gcd);
269 }
270 if (constraint->ub < kMaxIntegerValue) {
271 constraint->ub = FloorRatio(constraint->ub, gcd);
272 }
273 for (int i = 0; i < constraint->num_terms; ++i) {
274 constraint->coeffs[i] /= gcd;
275 }
276}
277
279 int new_size = 0;
280 const int size = constraint->num_terms;
281 for (int i = 0; i < size; ++i) {
282 if (constraint->coeffs[i] == 0) continue;
283 constraint->vars[new_size] = constraint->vars[i];
284 constraint->coeffs[new_size] = constraint->coeffs[i];
285 ++new_size;
286 }
287 constraint->resize(new_size);
288}
289
291 const int size = constraint->num_terms;
292 for (int i = 0; i < size; ++i) {
293 const IntegerValue coeff = constraint->coeffs[i];
294 if (coeff < 0) {
295 constraint->coeffs[i] = -coeff;
296 constraint->vars[i] = NegationOf(constraint->vars[i]);
297 }
298 }
299}
300
302 const int size = constraint->num_terms;
303 for (int i = 0; i < size; ++i) {
304 const IntegerVariable var = constraint->vars[i];
305 if (!VariableIsPositive(var)) {
306 constraint->coeffs[i] = -constraint->coeffs[i];
307 constraint->vars[i] = NegationOf(var);
308 }
309 }
310}
311
314 double result = ToDouble(offset);
315 for (int i = 0; i < vars.size(); ++i) {
316 result += ToDouble(coeffs[i]) * lp_values[vars[i]];
317 }
318 return result;
319}
320
321IntegerValue LinearExpression::LevelZeroMin(IntegerTrail* integer_trail) const {
322 IntegerValue result = offset;
323 for (int i = 0; i < vars.size(); ++i) {
324 DCHECK_GE(coeffs[i], 0);
325 result += coeffs[i] * integer_trail->LevelZeroLowerBound(vars[i]);
326 }
327 return result;
328}
329
330IntegerValue LinearExpression::Min(const IntegerTrail& integer_trail) const {
331 IntegerValue result = offset;
332 for (int i = 0; i < vars.size(); ++i) {
333 if (coeffs[i] > 0) {
334 result += coeffs[i] * integer_trail.LowerBound(vars[i]);
335 } else {
336 result += coeffs[i] * integer_trail.UpperBound(vars[i]);
337 }
338 }
339 return result;
340}
341
342IntegerValue LinearExpression::Max(const IntegerTrail& integer_trail) const {
343 IntegerValue result = offset;
344 for (int i = 0; i < vars.size(); ++i) {
345 if (coeffs[i] > 0) {
346 result += coeffs[i] * integer_trail.UpperBound(vars[i]);
347 } else {
348 result += coeffs[i] * integer_trail.LowerBound(vars[i]);
349 }
350 }
351 return result;
352}
353
354std::string LinearExpression::DebugString() const {
355 if (vars.empty()) return absl::StrCat(offset.value());
356 std::string result;
357 for (int i = 0; i < vars.size(); ++i) {
358 absl::StrAppend(&result, i > 0 ? " " : "",
360 }
361 if (offset != 0) {
362 absl::StrAppend(&result, " + ", offset.value());
363 }
364 return result;
365}
366
368 absl::flat_hash_set<IntegerVariable> seen_variables;
369 const int size = ct.num_terms;
370 for (int i = 0; i < size; ++i) {
371 if (VariableIsPositive(ct.vars[i])) {
372 if (!seen_variables.insert(ct.vars[i]).second) return false;
373 } else {
374 if (!seen_variables.insert(NegationOf(ct.vars[i])).second) return false;
375 }
376 }
377 return true;
378}
379
381 LinearExpression canonical_expr;
382 canonical_expr.offset = expr.offset;
383 for (int i = 0; i < expr.vars.size(); ++i) {
384 if (expr.coeffs[i] < 0) {
385 canonical_expr.vars.push_back(NegationOf(expr.vars[i]));
386 canonical_expr.coeffs.push_back(-expr.coeffs[i]);
387 } else {
388 canonical_expr.vars.push_back(expr.vars[i]);
389 canonical_expr.coeffs.push_back(expr.coeffs[i]);
390 }
391 }
392 return canonical_expr;
393}
394
395// TODO(user): Avoid duplication with PossibleIntegerOverflow() in the checker?
396// At least make sure the code is the same.
398 const IntegerTrail& integer_trail) {
399 int64_t positive_sum(0);
400 int64_t negative_sum(0);
401 for (int i = 0; i < constraint.num_terms; ++i) {
402 const IntegerVariable var = constraint.vars[i];
403 const IntegerValue coeff = constraint.coeffs[i];
404 const IntegerValue lb = integer_trail.LevelZeroLowerBound(var);
405 const IntegerValue ub = integer_trail.LevelZeroUpperBound(var);
406
407 int64_t min_prod = CapProd(coeff.value(), lb.value());
408 int64_t max_prod = CapProd(coeff.value(), ub.value());
409 if (min_prod > max_prod) std::swap(min_prod, max_prod);
410
411 positive_sum = CapAdd(positive_sum, std::max(int64_t{0}, max_prod));
412 negative_sum = CapAdd(negative_sum, std::min(int64_t{0}, min_prod));
413 }
414
415 const int64_t limit = std::numeric_limits<int64_t>::max();
416 if (positive_sum >= limit) return false;
417 if (negative_sum <= -limit) return false;
418 if (CapSub(positive_sum, negative_sum) >= limit) return false;
419
420 return true;
421}
422
424 LinearExpression result;
425 result.vars = NegationOf(expr.vars);
426 result.coeffs = expr.coeffs;
427 result.offset = -expr.offset;
428 return result;
429}
430
432 LinearExpression result;
433 result.offset = expr.offset;
434 for (int i = 0; i < expr.vars.size(); ++i) {
435 if (VariableIsPositive(expr.vars[i])) {
436 result.vars.push_back(expr.vars[i]);
437 result.coeffs.push_back(expr.coeffs[i]);
438 } else {
439 result.vars.push_back(NegationOf(expr.vars[i]));
440 result.coeffs.push_back(-expr.coeffs[i]);
441 }
442 }
443 return result;
444}
445
446IntegerValue GetCoefficient(const IntegerVariable var,
447 const LinearExpression& expr) {
448 for (int i = 0; i < expr.vars.size(); ++i) {
449 if (expr.vars[i] == var) {
450 return expr.coeffs[i];
451 } else if (expr.vars[i] == NegationOf(var)) {
452 return -expr.coeffs[i];
453 }
454 }
455 return IntegerValue(0);
456}
457
458IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
459 const LinearExpression& expr) {
460 CHECK(VariableIsPositive(var));
461 for (int i = 0; i < expr.vars.size(); ++i) {
462 if (expr.vars[i] == var) {
463 return expr.coeffs[i];
464 }
465 }
466 return IntegerValue(0);
467}
468
469bool PossibleOverflow(const IntegerTrail& integer_trail,
470 const LinearConstraint& constraint) {
471 IntegerValue min_activity(0);
472 IntegerValue max_activity(0);
473 const int size = constraint.num_terms;
474 for (int i = 0; i < size; ++i) {
475 const IntegerVariable var = constraint.vars[i];
476 const IntegerValue coeff = constraint.coeffs[i];
477 CHECK_NE(coeff, 0);
478 const IntegerValue lb = integer_trail.LevelZeroLowerBound(var);
479 const IntegerValue ub = integer_trail.LevelZeroUpperBound(var);
480 if (coeff > 0) {
481 if (!AddProductTo(lb, coeff, &min_activity)) return true;
482 if (!AddProductTo(ub, coeff, &max_activity)) return true;
483 } else {
484 if (!AddProductTo(ub, coeff, &min_activity)) return true;
485 if (!AddProductTo(lb, coeff, &max_activity)) return true;
486 }
487 }
488 return AtMinOrMaxInt64(CapSub(max_activity.value(), min_activity.value()));
489}
490
491} // namespace sat
492} // namespace operations_research
IntegerValue size
static int64_t GCD64(int64_t x, int64_t y)
Definition mathutil.h:108
ABSL_MUST_USE_RESULT bool LiteralOrNegationHasView(Literal lit, IntegerVariable *view=nullptr, bool *view_is_direct=nullptr) const
Definition integer.cc:582
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
Definition integer.h:1717
IntegerValue UpperBound(IntegerVariable i) const
Definition integer.h:1721
IntegerValue FixedValue(IntegerVariable i) const
Checks that the variable is fixed and returns its value.
Definition integer.h:1729
bool IsFixed(IntegerVariable i) const
Checks if the variable is fixed.
Definition integer.h:1725
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition integer.h:1817
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
Definition integer.h:1812
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail, bool *is_quadratic=nullptr)
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff=IntegerValue(1))
void AddTerm(IntegerVariable var, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddConstant(IntegerValue value)
Adds the corresponding term to the current linear expression.
LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub)
ABSL_MUST_USE_RESULT bool AddDecomposedProduct(absl::Span< const LiteralValueValue > product)
const Constraint * ct
int64_t value
IntVar * var
int lit
double ComputeActivity(const LinearConstraint &constraint, const util_intops::StrongVector< IntegerVariable, double > &values)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition integer.h:94
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
Computes result += a * b, and return false iff there is an overflow.
Definition integer.h:169
void DivideByGCD(LinearConstraint *constraint)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
Definition integer.h:81
double ComputeL2Norm(const LinearConstraint &ct)
Returns sqrt(sum square(coeff)).
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition integer.h:85
void RemoveZeroTerms(LinearConstraint *constraint)
Removes the entries with a coefficient of zero.
IntegerValue ComputeInfinityNorm(const LinearConstraint &ct)
Returns the maximum absolute value of the coefficients.
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
Definition integer.cc:51
std::string IntegerTermDebugString(IntegerVariable var, IntegerValue coeff)
Definition integer.h:203
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
bool PossibleOverflow(const IntegerTrail &integer_trail, const LinearConstraint &constraint)
void CleanTermsAndFillConstraint(std::vector< std::pair< IntegerVariable, IntegerValue > > *terms, LinearExpression *output)
void MakeAllCoefficientsPositive(LinearConstraint *constraint)
Makes all coefficients positive by transforming a variable to its negation.
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
LinearExpression PositiveVarExpr(const LinearExpression &expr)
Returns the same expression with positive variables.
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
void MakeAllVariablesPositive(LinearConstraint *constraint)
Makes all variables "positive" by transforming a variable to its negation.
IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var, const LinearExpression &expr)
bool ValidateLinearConstraintForOverflow(const LinearConstraint &constraint, const IntegerTrail &integer_trail)
bool NoDuplicateVariable(const LinearConstraint &ct)
Returns false if duplicate variables are found in ct.
bool VariableIsPositive(IntegerVariable i)
Definition integer.h:189
double ToDouble(IntegerValue value)
Definition integer.h:73
double ScalarProduct(const LinearConstraint &ct1, const LinearConstraint &ct2)
In SWIG mode, we don't want anything besides these top-level includes.
bool AtMinOrMaxInt64(int64_t x)
Checks if x is equal to the min or the max value of an int64_t.
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)
std::unique_ptr< IntegerValue[]> coeffs
std::unique_ptr< IntegerVariable[]> vars
IntegerValue LevelZeroMin(IntegerTrail *integer_trail) const
IntegerValue Min(const IntegerTrail &integer_trail) const
double LpValue(const util_intops::StrongVector< IntegerVariable, double > &lp_values) const
Return[s] the evaluation of the linear expression.
IntegerValue Max(const IntegerTrail &integer_trail) const