OR-Tools  9.2
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 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
264std::string LinearExpression::DebugString() const {
265 std::string result;
266 for (int i = 0; i < vars.size(); ++i) {
267 absl::StrAppend(&result, i > 0 ? " " : "",
269 }
270 if (offset != 0) {
271 absl::StrAppend(&result, " + ", offset.value());
272 }
273 return result;
274}
275
276// TODO(user): it would be better if LinearConstraint natively supported
277// term and not two separated vectors. Fix?
278//
279// TODO(user): This is really similar to CleanTermsAndFillConstraint(), maybe
280// we should just make the later switch negative variable to positive ones to
281// avoid an extra linear scan on each new cuts.
283 std::vector<std::pair<IntegerVariable, IntegerValue>> terms;
284
285 const int size = ct->vars.size();
286 for (int i = 0; i < size; ++i) {
287 if (VariableIsPositive(ct->vars[i])) {
288 terms.push_back({ct->vars[i], ct->coeffs[i]});
289 } else {
290 terms.push_back({NegationOf(ct->vars[i]), -ct->coeffs[i]});
291 }
292 }
293 std::sort(terms.begin(), terms.end());
294
295 ct->vars.clear();
296 ct->coeffs.clear();
297 for (const auto& term : terms) {
298 ct->vars.push_back(term.first);
299 ct->coeffs.push_back(term.second);
300 }
301}
302
304 absl::flat_hash_set<IntegerVariable> seen_variables;
305 const int size = ct.vars.size();
306 for (int i = 0; i < size; ++i) {
307 if (VariableIsPositive(ct.vars[i])) {
308 if (!seen_variables.insert(ct.vars[i]).second) return false;
309 } else {
310 if (!seen_variables.insert(NegationOf(ct.vars[i])).second) return false;
311 }
312 }
313 return true;
314}
315
317 LinearExpression canonical_expr;
318 canonical_expr.offset = expr.offset;
319 for (int i = 0; i < expr.vars.size(); ++i) {
320 if (expr.coeffs[i] < 0) {
321 canonical_expr.vars.push_back(NegationOf(expr.vars[i]));
322 canonical_expr.coeffs.push_back(-expr.coeffs[i]);
323 } else {
324 canonical_expr.vars.push_back(expr.vars[i]);
325 canonical_expr.coeffs.push_back(expr.coeffs[i]);
326 }
327 }
328 return canonical_expr;
329}
330
331IntegerValue LinExprLowerBound(const LinearExpression& expr,
332 const IntegerTrail& integer_trail) {
333 IntegerValue lower_bound = expr.offset;
334 for (int i = 0; i < expr.vars.size(); ++i) {
335 DCHECK_GE(expr.coeffs[i], 0) << "The expression is not canonicalized";
336 lower_bound += expr.coeffs[i] * integer_trail.LowerBound(expr.vars[i]);
337 }
338 return lower_bound;
339}
340
341IntegerValue LinExprUpperBound(const LinearExpression& expr,
342 const IntegerTrail& integer_trail) {
343 IntegerValue upper_bound = expr.offset;
344 for (int i = 0; i < expr.vars.size(); ++i) {
345 DCHECK_GE(expr.coeffs[i], 0) << "The expression is not canonicalized";
346 upper_bound += expr.coeffs[i] * integer_trail.UpperBound(expr.vars[i]);
347 }
348 return upper_bound;
349}
350
351// TODO(user): Avoid duplication with PossibleIntegerOverflow() in the checker?
352// At least make sure the code is the same.
354 const IntegerTrail& integer_trail) {
355 int64_t positive_sum(0);
356 int64_t negative_sum(0);
357 for (int i = 0; i < constraint.vars.size(); ++i) {
358 const IntegerVariable var = constraint.vars[i];
359 const IntegerValue coeff = constraint.coeffs[i];
360 const IntegerValue lb = integer_trail.LevelZeroLowerBound(var);
361 const IntegerValue ub = integer_trail.LevelZeroUpperBound(var);
362
363 int64_t min_prod = CapProd(coeff.value(), lb.value());
364 int64_t max_prod = CapProd(coeff.value(), ub.value());
365 if (min_prod > max_prod) std::swap(min_prod, max_prod);
366
367 positive_sum = CapAdd(positive_sum, std::max(int64_t{0}, max_prod));
368 negative_sum = CapAdd(negative_sum, std::min(int64_t{0}, min_prod));
369 }
370
371 const int64_t limit = std::numeric_limits<int64_t>::max();
372 if (positive_sum >= limit) return false;
373 if (negative_sum <= -limit) return false;
374 if (CapSub(positive_sum, negative_sum) >= limit) return false;
375
376 return true;
377}
378
380 LinearExpression result;
381 result.vars = NegationOf(expr.vars);
382 result.coeffs = expr.coeffs;
383 result.offset = -expr.offset;
384 return result;
385}
386
388 LinearExpression result;
389 result.offset = expr.offset;
390 for (int i = 0; i < expr.vars.size(); ++i) {
391 if (VariableIsPositive(expr.vars[i])) {
392 result.vars.push_back(expr.vars[i]);
393 result.coeffs.push_back(expr.coeffs[i]);
394 } else {
395 result.vars.push_back(NegationOf(expr.vars[i]));
396 result.coeffs.push_back(-expr.coeffs[i]);
397 }
398 }
399 return result;
400}
401
402IntegerValue GetCoefficient(const IntegerVariable var,
403 const LinearExpression& expr) {
404 for (int i = 0; i < expr.vars.size(); ++i) {
405 if (expr.vars[i] == var) {
406 return expr.coeffs[i];
407 } else if (expr.vars[i] == NegationOf(var)) {
408 return -expr.coeffs[i];
409 }
410 }
411 return IntegerValue(0);
412}
413
414IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
415 const LinearExpression& expr) {
417 for (int i = 0; i < expr.vars.size(); ++i) {
418 if (expr.vars[i] == var) {
419 return expr.coeffs[i];
420 }
421 }
422 return IntegerValue(0);
423}
424
425} // namespace sat
426} // 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:492
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:891
#define DCHECK(condition)
Definition: base/logging.h:886
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:263
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)
double LpValue(const absl::StrongVector< IntegerVariable, double > &lp_values) const