OR-Tools  9.3
sat/linear_constraint.h
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
14#ifndef OR_TOOLS_SAT_LINEAR_CONSTRAINT_H_
15#define OR_TOOLS_SAT_LINEAR_CONSTRAINT_H_
16
17#include <algorithm>
18#include <ostream>
19#include <string>
20#include <utility>
21#include <vector>
22
23#include "absl/base/attributes.h"
24#include "absl/strings/str_cat.h"
26#include "ortools/sat/integer.h"
27#include "ortools/sat/model.h"
30
31namespace operations_research {
32namespace sat {
33
34// One linear constraint on a set of Integer variables.
35// Important: there should be no duplicate variables.
36//
37// We also assume that we never have integer overflow when evaluating such
38// constraint at the ROOT node. This should be enforced by the checker for user
39// given constraints, and we must enforce it ourselves for the newly created
40// constraint. See ValidateLinearConstraintForOverflow().
42 IntegerValue lb;
43 IntegerValue ub;
44 std::vector<IntegerVariable> vars;
45 std::vector<IntegerValue> coeffs;
46
48 LinearConstraint(IntegerValue _lb, IntegerValue _ub) : lb(_lb), ub(_ub) {}
49
50 void AddTerm(IntegerVariable var, IntegerValue coeff) {
51 vars.push_back(var);
52 coeffs.push_back(coeff);
53 }
54
55 void Clear() {
56 lb = ub = IntegerValue(0);
57 ClearTerms();
58 }
59
60 void ClearTerms() {
61 vars.clear();
62 coeffs.clear();
63 }
64
65 std::string DebugString() const {
66 std::string result;
67 if (lb.value() > kMinIntegerValue) {
68 absl::StrAppend(&result, lb.value(), " <= ");
69 }
70 for (int i = 0; i < vars.size(); ++i) {
71 absl::StrAppend(&result, i > 0 ? " " : "",
73 }
74 if (ub.value() < kMaxIntegerValue) {
75 absl::StrAppend(&result, " <= ", ub.value());
76 }
77 return result;
78 }
79
80 bool operator==(const LinearConstraint other) const {
81 if (this->lb != other.lb) return false;
82 if (this->ub != other.ub) return false;
83 if (this->vars != other.vars) return false;
84 if (this->coeffs != other.coeffs) return false;
85 return true;
86 }
87};
88
89inline std::ostream& operator<<(std::ostream& os, const LinearConstraint& ct) {
90 os << ct.DebugString();
91 return os;
92}
93
94// Helper struct to model linear expression for lin_min/lin_max constraints. The
95// canonical expression should only contain positive coefficients.
97 std::vector<IntegerVariable> vars;
98 std::vector<IntegerValue> coeffs;
99 IntegerValue offset = IntegerValue(0);
100
101 // Return[s] the evaluation of the linear expression.
102 double LpValue(
103 const absl::StrongVector<IntegerVariable, double>& lp_values) const;
104 IntegerValue LevelZeroMin(IntegerTrail* integer_trail) const;
105 IntegerValue Min(IntegerTrail* integer_trail) const;
106
107 std::string DebugString() const;
108};
109
110// Returns the same expression in the canonical form (all positive
111// coefficients).
113
114// Returns lower bound of linear expression using variable bounds of the
115// variables in expression. Assumes Canonical expression (all positive
116// coefficients).
117IntegerValue LinExprLowerBound(const LinearExpression& expr,
118 const IntegerTrail& integer_trail);
119
120// Returns upper bound of linear expression using variable bounds of the
121// variables in expression. Assumes Canonical expression (all positive
122// coefficients).
123IntegerValue LinExprUpperBound(const LinearExpression& expr,
124 const IntegerTrail& integer_trail);
125
126// Makes sure that any of our future computation on this constraint will not
127// cause overflow. We use the level zero bounds and use the same definition as
128// in PossibleIntegerOverflow() in the cp_model.proto checker.
129//
130// Namely, the sum of positive terms, the sum of negative terms and their
131// difference shouldn't overflow. Note that we don't validate the rhs, but if
132// the bounds are properly relaxed, then this shouldn't cause any issues.
133//
134// Note(user): We should avoid doing this test too often as it can be slow. At
135// least do not do it more than once on each constraint.
137 const IntegerTrail& integer_trail);
138
139// Preserves canonicality.
141
142// Returns the same expression with positive variables.
144
145// Returns the coefficient of the variable in the expression. Works in linear
146// time.
147// Note: GetCoefficient(NegationOf(var, expr)) == -GetCoefficient(var, expr).
148IntegerValue GetCoefficient(const IntegerVariable var,
149 const LinearExpression& expr);
150IntegerValue GetCoefficientOfPositiveVar(const IntegerVariable var,
151 const LinearExpression& expr);
152
153// Allow to build a LinearConstraint while making sure there is no duplicate
154// variables. Note that we do not simplify literal/variable that are currently
155// fixed here.
156//
157// All the functions manipulate a linear expression with an offset. The final
158// constraint bounds will include this offset.
159//
160// TODO(user): Rename to LinearExpressionBuilder?
162 public:
163 // We support "sticky" kMinIntegerValue for lb and kMaxIntegerValue for ub
164 // for one-sided constraints.
165 //
166 // Assumes that the 'model' has IntegerEncoder. The bounds can either be
167 // specified at construction or during the Build() call.
169 : encoder_(*model->Get<IntegerEncoder>()), lb_(0), ub_(0) {}
170 LinearConstraintBuilder(const Model* model, IntegerValue lb, IntegerValue ub)
171 : encoder_(*model->Get<IntegerEncoder>()), lb_(lb), ub_(ub) {}
172
173 // Adds the corresponding term to the current linear expression.
174 void AddConstant(IntegerValue value);
175 void AddTerm(IntegerVariable var, IntegerValue coeff);
176 void AddTerm(AffineExpression expr, IntegerValue coeff);
177 void AddLinearExpression(const LinearExpression& expr);
178 void AddLinearExpression(const LinearExpression& expr, IntegerValue coeff);
179
180 // Add literal * coeff to the constaint. Returns false and do nothing if the
181 // given literal didn't have an integer view.
182 ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff);
183
184 // Add an under linearization of the product of two affine expressions.
185 // If at least one of them is fixed, then we add the exact product (which is
186 // linear). Otherwise, we use McCormick relaxation:
187 // left * right = (left_min + delta_left) * (right_min + delta_right) =
188 // left_min * right_min + delta_left * right_min +
189 // delta_right * left_min + delta_left * delta_right
190 // which is >= (by ignoring the quatratic term)
191 // right_min * left + left_min * right - right_min * left_min
192 //
193 // TODO(user): We could use (max - delta) instead of (min + delta) for each
194 // expression instead. This would depend on the LP value of the left and
195 // right.
197 IntegerTrail* integer_trail);
198
199 // Clears all added terms and constants. Keeps the original bounds.
200 void Clear() {
201 offset_ = IntegerValue(0);
202 terms_.clear();
203 }
204
205 // Builds and returns the corresponding constraint in a canonical form.
206 // All the IntegerVariable will be positive and appear in increasing index
207 // order.
208 //
209 // The bounds can be changed here or taken at construction.
210 //
211 // TODO(user): this doesn't invalidate the builder object, but if one wants
212 // to do a lot of dynamic editing to the constraint, then then underlying
213 // algorithm needs to be optimized for that.
215 LinearConstraint BuildConstraint(IntegerValue lb, IntegerValue ub);
216
217 // Returns the linear expression part of the constraint only, without the
218 // bounds.
220
221 private:
222 const IntegerEncoder& encoder_;
223 const IntegerValue lb_;
224 const IntegerValue ub_;
225
226 IntegerValue offset_ = IntegerValue(0);
227
228 // Initially we push all AddTerm() here, and during Build() we merge terms
229 // on the same variable.
230 std::vector<std::pair<IntegerVariable, IntegerValue>> terms_;
231};
232
233// Returns the activity of the given constraint. That is the current value of
234// the linear terms.
235double ComputeActivity(
236 const LinearConstraint& constraint,
238
239// Returns sqrt(sum square(coeff)).
240double ComputeL2Norm(const LinearConstraint& constraint);
241
242// Returns the maximum absolute value of the coefficients.
243IntegerValue ComputeInfinityNorm(const LinearConstraint& constraint);
244
245// Returns the scalar product of given constraint coefficients. This method
246// assumes that the constraint variables are in sorted order.
247double ScalarProduct(const LinearConstraint& constraint1,
248 const LinearConstraint& constraint2);
249
250// Computes the GCD of the constraint coefficient, and divide them by it. This
251// also tighten the constraint bounds assumming all the variables are integer.
252void DivideByGCD(LinearConstraint* constraint);
253
254// Removes the entries with a coefficient of zero.
255void RemoveZeroTerms(LinearConstraint* constraint);
256
257// Makes all coefficients positive by transforming a variable to its negation.
258void MakeAllCoefficientsPositive(LinearConstraint* constraint);
259
260// Makes all variables "positive" by transforming a variable to its negation.
261void MakeAllVariablesPositive(LinearConstraint* constraint);
262
263// Sorts the terms and makes all IntegerVariable positive. This assumes that a
264// variable or its negation only appear once.
265//
266// Note that currently this allocates some temporary memory.
267void CanonicalizeConstraint(LinearConstraint* ct);
268
269// Returns false if duplicate variables are found in ct.
270bool NoDuplicateVariable(const LinearConstraint& ct);
271
272// Sorts and merges duplicate IntegerVariable in the given "terms".
273// Fills the given LinearConstraint or LinearExpression with the result.
274//
275// TODO(user): This actually only sort the terms, we don't clean them.
276template <class ClassWithVarsAndCoeffs>
278 std::vector<std::pair<IntegerVariable, IntegerValue>>* terms,
279 ClassWithVarsAndCoeffs* output) {
280 output->vars.clear();
281 output->coeffs.clear();
282
283 // Sort and add coeff of duplicate variables. Note that a variable and
284 // its negation will appear one after another in the natural order.
285 std::sort(terms->begin(), terms->end());
286 IntegerVariable previous_var = kNoIntegerVariable;
287 IntegerValue current_coeff(0);
288 for (const std::pair<IntegerVariable, IntegerValue>& entry : *terms) {
289 if (previous_var == entry.first) {
290 current_coeff += entry.second;
291 } else if (previous_var == NegationOf(entry.first)) {
292 current_coeff -= entry.second;
293 } else {
294 if (current_coeff != 0) {
295 output->vars.push_back(previous_var);
296 output->coeffs.push_back(current_coeff);
297 }
298 previous_var = entry.first;
299 current_coeff = entry.second;
300 }
301 }
302 if (current_coeff != 0) {
303 output->vars.push_back(previous_var);
304 output->coeffs.push_back(current_coeff);
305 }
306}
307
308} // namespace sat
309} // namespace operations_research
310
311#endif // OR_TOOLS_SAT_LINEAR_CONSTRAINT_H_
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)
LinearConstraintBuilder(const Model *model, IntegerValue lb, IntegerValue ub)
void AddTerm(IntegerVariable var, IntegerValue coeff)
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
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)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc: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)
void DivideByGCD(LinearConstraint *constraint)
double ComputeActivity(const LinearConstraint &constraint, const absl::StrongVector< IntegerVariable, double > &values)
Collection of objects used to extend the Constraint Solver library.
bool operator==(const LinearConstraint other) const
LinearConstraint(IntegerValue _lb, IntegerValue _ub)
void AddTerm(IntegerVariable var, IntegerValue coeff)
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