OR-Tools  8.2
linear_constraint_manager.h
Go to the documentation of this file.
1 // Copyright 2010-2018 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_MANAGER_H_
15 #define OR_TOOLS_SAT_LINEAR_CONSTRAINT_MANAGER_H_
16 
17 #include <cstddef>
18 #include <vector>
19 
20 #include "absl/container/flat_hash_map.h"
21 #include "absl/container/flat_hash_set.h"
25 #include "ortools/sat/model.h"
28 
29 namespace operations_research {
30 namespace sat {
31 
32 // This class holds a list of globally valid linear constraints and has some
33 // logic to decide which one should be part of the LP relaxation. We want more
34 // for a better relaxation, but for efficiency we do not want to have too much
35 // constraints while solving the LP.
36 //
37 // This class is meant to contain all the initial constraints of the LP
38 // relaxation and to get new cuts as they are generated. Thus, it can both
39 // manage cuts but also only add the initial constraints lazily if there is too
40 // many of them.
42  public:
43  struct ConstraintInfo {
45  double l2_norm = 0.0;
47  double objective_parallelism = 0.0;
49  bool is_in_lp = false;
50  size_t hash;
51  double current_score = 0.0;
52 
53  // Updated only for deletable constraints. This is incremented every time
54  // ChangeLp() is called and the constraint is active in the LP or not in the
55  // LP and violated.
56  double active_count = 0.0;
57 
58  // For now, we mark all the generated cuts as deletable and the problem
59  // constraints as undeletable.
60  // TODO(user): We can have a better heuristics. Some generated good cuts
61  // can be marked undeletable and some unused problem specified constraints
62  // can be marked deletable.
63  bool is_deletable = false;
64  };
65 
67  : sat_parameters_(*model->GetOrCreate<SatParameters>()),
68  integer_trail_(*model->GetOrCreate<IntegerTrail>()),
69  time_limit_(model->GetOrCreate<TimeLimit>()),
70  model_(model) {}
72 
73  // Add a new constraint to the manager. Note that we canonicalize constraints
74  // and merge the bounds of constraints with the same terms. We also perform
75  // basic preprocessing. If added is given, it will be set to true if this
76  // constraint was actually a new one and to false if it was dominated by an
77  // already existing one.
78  DEFINE_INT_TYPE(ConstraintIndex, int32);
79  ConstraintIndex Add(LinearConstraint ct, bool* added = nullptr);
80 
81  // Same as Add(), but logs some information about the newly added constraint.
82  // Cuts are also handled slightly differently than normal constraints.
83  //
84  // Returns true if a new cut was added and false if this cut is not
85  // efficacious or if it is a duplicate of an already existing one.
86  bool AddCut(LinearConstraint ct, std::string type_name,
88  std::string extra_info = "");
89 
90  // The objective is used as one of the criterion to score cuts.
91  // The more a cut is parallel to the objective, the better its score is.
92  void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff);
93 
94  // Heuristic to decides what LP is best solved next. The given lp_solution
95  // should usually be the optimal solution of the LP returned by GetLp() before
96  // this call, but is just used as an heuristic.
97  //
98  // The current solution state is used for detecting inactive constraints. It
99  // is also updated correctly on constraint deletion/addition so that the
100  // simplex can be fully iterative on restart by loading this modified state.
101  //
102  // Returns true iff LpConstraints() will return a different LP than before.
104  glop::BasisState* solution_state);
105 
106  // This can be called initially to add all the current constraint to the LP
107  // returned by GetLp().
108  void AddAllConstraintsToLp();
109 
110  // All the constraints managed by this class.
112  const {
113  return constraint_infos_;
114  }
115 
116  // The set of constraints indices in AllConstraints() that should be part
117  // of the next LP to solve.
118  const std::vector<ConstraintIndex>& LpConstraints() const {
119  return lp_constraints_;
120  }
121 
122  int64 num_cuts() const { return num_cuts_; }
123  int64 num_shortened_constraints() const { return num_shortened_constraints_; }
124  int64 num_coeff_strenghtening() const { return num_coeff_strenghtening_; }
125 
126  // If a debug solution has been loaded, this checks if the given constaint cut
127  // it or not. Returns true iff everything is fine and the cut does not violate
128  // the loaded solution.
129  bool DebugCheckConstraint(const LinearConstraint& cut);
130 
131  private:
132  // Heuristic that decide which constraints we should remove from the current
133  // LP. Note that such constraints can be added back later by the heuristic
134  // responsible for adding new constraints from the pool.
135  //
136  // Returns true iff one or more constraints where removed.
137  //
138  // If the solutions_state is empty, then this function does nothing and
139  // returns false (this is used for tests). Otherwise, the solutions_state is
140  // assumed to correspond to the current LP and to be of the correct size.
141  bool MaybeRemoveSomeInactiveConstraints(glop::BasisState* solution_state);
142 
143  // Apply basic inprocessing simplification rules:
144  // - remove fixed variable
145  // - reduce large coefficient (i.e. coeff strenghtenning or big-M reduction).
146  // This uses level-zero bounds.
147  // Returns true if the terms of the constraint changed.
148  bool SimplifyConstraint(LinearConstraint* ct);
149 
150  // Helper method to compute objective parallelism for a given constraint. This
151  // also lazily computes objective norm.
152  void ComputeObjectiveParallelism(const ConstraintIndex ct_index);
153 
154  // Multiplies all active counts and the increment counter by the given
155  // 'scaling_factor'. This should be called when at least one of the active
156  // counts is too high.
157  void RescaleActiveCounts(double scaling_factor);
158 
159  // Removes some deletable constraints with low active counts. For now, we
160  // don't remove any constraints which are already in LP.
161  void PermanentlyRemoveSomeConstraints();
162 
163  const SatParameters& sat_parameters_;
164  const IntegerTrail& integer_trail_;
165 
166  // Set at true by Add()/SimplifyConstraint() and at false by ChangeLp().
167  bool current_lp_is_changed_ = false;
168 
169  // Optimization to avoid calling SimplifyConstraint() when not needed.
170  int64 last_simplification_timestamp_ = 0;
171 
173 
174  // The subset of constraints currently in the lp.
175  std::vector<ConstraintIndex> lp_constraints_;
176 
177  // We keep a map from the hash of our constraint terms to their position in
178  // constraints_. This is an optimization to detect duplicate constraints. We
179  // are robust to collisions because we always relies on the ground truth
180  // contained in constraints_ and the code is still okay if we do not merge the
181  // constraints.
182  absl::flat_hash_map<size_t, ConstraintIndex> equiv_constraints_;
183 
184  int64 num_simplifications_ = 0;
185  int64 num_merged_constraints_ = 0;
186  int64 num_shortened_constraints_ = 0;
187  int64 num_splitted_constraints_ = 0;
188  int64 num_coeff_strenghtening_ = 0;
189 
190  int64 num_cuts_ = 0;
191  int64 num_add_cut_calls_ = 0;
192  std::map<std::string, int> type_to_num_cuts_;
193 
194  bool objective_is_defined_ = false;
195  bool objective_norm_computed_ = false;
196  double objective_l2_norm_ = 0.0;
197 
198  // Total deterministic time spent in this class.
199  double dtime_ = 0.0;
200 
201  // Dense representation of the objective coeffs indexed by positive variables
202  // indices. It contains 0.0 where the variables does not appear in the
203  // objective.
204  absl::StrongVector<IntegerVariable, double> dense_objective_coeffs_;
205 
206  TimeLimit* time_limit_;
207  Model* model_;
208 
209  // We want to decay the active counts of all constraints at each call and
210  // increase the active counts of active/violated constraints. However this can
211  // be too slow in practice. So instead, we keep an increment counter and
212  // update only the active/violated constraints. The counter itself is
213  // increased by a factor at each call. This has the same effect as decaying
214  // all the active counts at each call. This trick is similar to sat clause
215  // management.
216  double constraint_active_count_increase_ = 1.0;
217 
218  int32 num_deletable_constraints_ = 0;
219 };
220 
221 // Keep the top n elements from a stream of elements.
222 //
223 // TODO(user): We could use gtl::TopN when/if it gets open sourced. Note that
224 // we might be slighlty faster here since we use an indirection and don't move
225 // the Element class around as much.
226 template <typename Element>
227 class TopN {
228  public:
229  explicit TopN(int n) : n_(n) {}
230 
231  void Clear() {
232  heap_.clear();
233  elements_.clear();
234  }
235 
236  void Add(Element e, double score) {
237  if (heap_.size() < n_) {
238  const int index = elements_.size();
239  heap_.push_back({index, score});
240  elements_.push_back(std::move(e));
241  if (heap_.size() == n_) {
242  // TODO(user): We could delay that on the n + 1 push.
243  std::make_heap(heap_.begin(), heap_.end());
244  }
245  } else {
246  if (score <= heap_.front().score) return;
247  const int index_to_replace = heap_.front().index;
248  elements_[index_to_replace] = std::move(e);
249 
250  // If needed, we could be faster here with an update operation.
251  std::pop_heap(heap_.begin(), heap_.end());
252  heap_.back() = {index_to_replace, score};
253  std::push_heap(heap_.begin(), heap_.end());
254  }
255  }
256 
257  const std::vector<Element>& UnorderedElements() const { return elements_; }
258 
259  private:
260  const int n_;
261 
262  // We keep a heap of the n lowest score.
263  struct HeapElement {
264  int index; // in elements_;
265  double score;
266  const double operator<(const HeapElement& other) const {
267  return score > other.score;
268  }
269  };
270  std::vector<HeapElement> heap_;
271  std::vector<Element> elements_;
272 };
273 
274 // Before adding cuts to the global pool, it is a classical thing to only keep
275 // the top n of a given type during one generation round. This is there to help
276 // doing that.
277 //
278 // TODO(user): Avoid computing efficacity twice.
279 // TODO(user): We don't use any orthogonality consideration here.
280 // TODO(user): Detect duplicate cuts?
281 class TopNCuts {
282  public:
283  explicit TopNCuts(int n) : cuts_(n) {}
284 
285  // Add a cut to the local pool
286  void AddCut(LinearConstraint ct, const std::string& name,
288 
289  // Empty the local pool and add all its content to the manager.
290  void TransferToManager(
292  LinearConstraintManager* manager);
293 
294  private:
295  struct CutCandidate {
296  std::string name;
297  LinearConstraint cut;
298  };
299  TopN<CutCandidate> cuts_;
300 };
301 
302 } // namespace sat
303 } // namespace operations_research
304 
305 #endif // OR_TOOLS_SAT_LINEAR_CONSTRAINT_MANAGER_H_
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
bool ChangeLp(const absl::StrongVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
const absl::StrongVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
const std::vector< ConstraintIndex > & LpConstraints() const
bool AddCut(LinearConstraint ct, std::string type_name, const absl::StrongVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
void AddCut(LinearConstraint ct, const std::string &name, const absl::StrongVector< IntegerVariable, double > &lp_solution)
void TransferToManager(const absl::StrongVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
const std::vector< Element > & UnorderedElements() const
void Add(Element e, double score)
const std::string name
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1858
GRBmodel * model
int int32
int64_t int64
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
int index
Definition: pack.cc:508