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