OR-Tools  9.3
implied_bounds.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_IMPLIED_BOUNDS_H_
15#define OR_TOOLS_SAT_IMPLIED_BOUNDS_H_
16
17#include <algorithm>
18#include <cstdint>
19#include <utility>
20#include <vector>
21
22#include "absl/container/flat_hash_map.h"
26#include "ortools/sat/integer.h"
28#include "ortools/sat/model.h"
30#include "ortools/sat/sat_parameters.pb.h"
32#include "ortools/util/bitset.h"
34
35namespace operations_research {
36namespace sat {
37
38// For each IntegerVariable, the ImpliedBound class allows to list all such
39// entries.
40//
41// This is meant to be used in the cut generation code when it make sense: if we
42// have BoolVar => X >= bound, we can always lower bound the variable X by
43// (bound - X_lb) * BoolVar + X_lb, and that can lead to stronger cuts.
45 // An integer variable in [0, 1]. When at 1, then the IntegerVariable
46 // corresponding to this entry must be greater or equal to the given lower
47 // bound.
49 IntegerValue lower_bound = IntegerValue(0);
50
51 // If false, it is when the literal_view is zero that the lower bound is
52 // valid.
53 bool is_positive = true;
54
55 // These constructors are needed for OR-Tools.
56 ImpliedBoundEntry(IntegerVariable lit, IntegerValue lb, bool positive)
57 : literal_view(lit), lower_bound(lb), is_positive(positive) {}
58
61};
62
63// Maintains all the implications of the form Literal => IntegerLiteral. We
64// collect these implication at model loading, during probing and during search.
65//
66// TODO(user): This can quickly use up too much memory. Add some limit in place.
67// In particular, each time we have literal => integer_literal we should avoid
68// storing the same integer_literal for all other_literal for which
69// other_literal => literal. For this we need to interact with the
70// BinaryImplicationGraph.
71//
72// TODO(user): This is a bit of a duplicate with the Literal <=> IntegerLiteral
73// stored in the IntegerEncoder class. However we only need one side here.
74//
75// TODO(user): Do like in the DomainDeductions class and allow to process
76// clauses (or store them) to perform more level zero deductions. Note that this
77// is again a slight duplicate with what we do there (except that we work at the
78// Domain level in that presolve class).
79//
80// TODO(user): Add an implied bound cut generator to add these simple
81// constraints to the LP when needed.
83 public:
85 : parameters_(*model->GetOrCreate<SatParameters>()),
86 sat_solver_(model->GetOrCreate<SatSolver>()),
87 integer_trail_(model->GetOrCreate<IntegerTrail>()),
88 integer_encoder_(model->GetOrCreate<IntegerEncoder>()) {}
90
91 // Adds literal => integer_literal to the repository.
92 //
93 // Not that it checks right aways if there is another bound on the same
94 // variable involving literal.Negated(), in which case we can improve the
95 // level zero lower bound of the variable.
96 void Add(Literal literal, IntegerLiteral integer_literal);
97
98 // Adds literal => var == value.
99 void AddLiteralImpliesVarEqValue(Literal literal, IntegerVariable var,
100 IntegerValue value);
101
102 // This must be called after first_decision has been enqueued and propagated.
103 // It will inspect the trail and add all new implied bounds.
104 //
105 // Preconditions: The decision level must be one (CHECKed). And the decision
106 // must be equal to first decision (we currently do not CHECK that).
107 void ProcessIntegerTrail(Literal first_decision);
108
109 // Returns all the implied bounds stored for the given variable.
110 // Note that only literal with an IntegerView are considered here.
111 const std::vector<ImpliedBoundEntry>& GetImpliedBounds(IntegerVariable var);
112
113 // Returns all the variables for which GetImpliedBounds(var) is not empty. Or
114 // at least that was not empty at some point, because we lazily remove bounds
115 // that become trivial as the search progress.
116 const std::vector<IntegerVariable>& VariablesWithImpliedBounds() const {
117 return has_implied_bounds_.PositionsSetAtLeastOnce();
118 }
119
120 // Returns all the implied values stored for a given literal.
121 const absl::flat_hash_map<IntegerVariable, IntegerValue>& GetImpliedValues(
122 Literal literal) const {
123 const auto it = literal_to_var_to_value_.find(literal.Index());
124 return it != literal_to_var_to_value_.end() ? it->second
125 : empty_var_to_value_;
126 }
127
128 // Register the fact that var = sum literal * value with sum literal == 1.
129 // Note that we call this an "element" encoding because a value can appear
130 // more than once.
131 void AddElementEncoding(IntegerVariable var,
132 const std::vector<ValueLiteralPair>& encoding,
133 int exactly_one_index);
134
135 // Returns an empty map if there is no such encoding.
136 const absl::flat_hash_map<int, std::vector<ValueLiteralPair>>&
137 GetElementEncodings(IntegerVariable var);
138
139 // Get an unsorted set of variables appearing in element encodings.
140 const std::vector<IntegerVariable>& GetElementEncodedVariables() const;
141
142 // Adds to the integer trail all the new level-zero deduction made here.
143 // This can only be called at decision level zero. Returns false iff the model
144 // is infeasible.
146
147 // When a literal does not have an integer view, we do not add any
148 // ImpliedBoundEntry. This allows to create missing entries for a literal for
149 // which a view was just created.
150 //
151 // TODO(user): Implement and call when we create new views in the linear
152 // relaxation.
154
155 private:
156 const SatParameters& parameters_;
157 SatSolver* sat_solver_;
158 IntegerTrail* integer_trail_;
159 IntegerEncoder* integer_encoder_;
160
161 // TODO(user): Remove the need for this.
162 std::vector<IntegerLiteral> tmp_integer_literals_;
163
164 // For each (Literal, IntegerVariable) the best lower bound implied by this
165 // literal. Note that there is no need to store any entries that do not
166 // improve on the level zero lower bound.
167 //
168 // TODO(user): we could lazily remove old entries to save a bit of space if
169 // many deduction where made at level zero.
170 absl::flat_hash_map<std::pair<LiteralIndex, IntegerVariable>, IntegerValue>
171 bounds_;
172
173 // Note(user): The plan is to use these during cut generation, so only the
174 // Literal with an IntegerView that can be used in the LP relaxation need to
175 // be kept here.
176 //
177 // TODO(user): Use inlined vectors.
178 std::vector<ImpliedBoundEntry> empty_implied_bounds_;
180 var_to_bounds_;
181
182 // Track the list of variables with some implied bounds.
183 SparseBitset<IntegerVariable> has_implied_bounds_;
184
185 // Stores implied values per variable.
186 absl::flat_hash_map<LiteralIndex,
187 absl::flat_hash_map<IntegerVariable, IntegerValue>>
188 literal_to_var_to_value_;
189 const absl::flat_hash_map<IntegerVariable, IntegerValue> empty_var_to_value_;
190
191 absl::flat_hash_map<IntegerVariable,
192 absl::flat_hash_map<int, std::vector<ValueLiteralPair>>>
193 var_to_index_to_element_encodings_;
194 const absl::flat_hash_map<int, std::vector<ValueLiteralPair>>
195 empty_element_encoding_;
196 std::vector<IntegerVariable> element_encoded_variables_;
197
198 // TODO(user): Ideally, this should go away if we manage to push level-zero
199 // fact at a positive level directly.
201 SparseBitset<IntegerVariable> new_level_zero_bounds_;
202
203 // Stats.
204 int64_t num_deductions_ = 0;
205 int64_t num_enqueued_in_var_to_bounds_ = 0;
206};
207
208// Looks at value encodings and detects if the product of two variables can be
209// linearized.
210//
211// In the returned encoding, note that all the literals will be unique and in
212// exactly one relation, and that the values can be duplicated. This is what we
213// call an "element" encoding.
214//
215// The expressions will also be canonical.
216bool DetectLinearEncodingOfProducts(const AffineExpression& left,
217 const AffineExpression& right, Model* model,
218 LinearConstraintBuilder* builder);
219
220// Try to linearize each term of the inner product of left and right.
221// If the linearization not possible at position i, then
222// ProductIsLinearized(energies[i]) will return false.
223void LinearizeInnerProduct(const std::vector<AffineExpression>& left,
224 const std::vector<AffineExpression>& right,
225 Model* model,
226 std::vector<LinearExpression>* energies);
227
228// Returns whether the corresponding expression is a valid linearization of
229// the product of two affine expressions.
230bool ProductIsLinearized(const LinearExpression& expr);
231
232} // namespace sat
233} // namespace operations_research
234
235#endif // OR_TOOLS_SAT_IMPLIED_BOUNDS_H_
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:819
const std::vector< ImpliedBoundEntry > & GetImpliedBounds(IntegerVariable var)
void AddLiteralImpliesVarEqValue(Literal literal, IntegerVariable var, IntegerValue value)
void NotifyNewIntegerView(Literal literal)
const absl::flat_hash_map< int, std::vector< ValueLiteralPair > > & GetElementEncodings(IntegerVariable var)
void Add(Literal literal, IntegerLiteral integer_literal)
void AddElementEncoding(IntegerVariable var, const std::vector< ValueLiteralPair > &encoding, int exactly_one_index)
const absl::flat_hash_map< IntegerVariable, IntegerValue > & GetImpliedValues(Literal literal) const
const std::vector< IntegerVariable > & VariablesWithImpliedBounds() const
const std::vector< IntegerVariable > & GetElementEncodedVariables() const
void ProcessIntegerTrail(Literal first_decision)
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
void LinearizeInnerProduct(const std::vector< AffineExpression > &left, const std::vector< AffineExpression > &right, Model *model, std::vector< LinearExpression > *energies)
const IntegerVariable kNoIntegerVariable(-1)
bool ProductIsLinearized(const LinearExpression &expr)
bool DetectLinearEncodingOfProducts(const AffineExpression &left, const AffineExpression &right, Model *model, LinearConstraintBuilder *builder)
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:89
ImpliedBoundEntry()
bool is_positive
ImpliedBoundEntry(IntegerVariable lit, IntegerValue lb, bool positive)
IntegerValue lower_bound
IntegerVariable literal_view