OR-Tools  9.0
presolve_context.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_PRESOLVE_CONTEXT_H_
15 #define OR_TOOLS_SAT_PRESOLVE_CONTEXT_H_
16 
17 #include <cstdint>
18 #include <deque>
19 #include <vector>
20 
23 #include "ortools/sat/model.h"
26 #include "ortools/sat/util.h"
28 #include "ortools/util/bitset.h"
29 #include "ortools/util/logging.h"
32 
33 namespace operations_research {
34 namespace sat {
35 
36 // We use some special constraint index in our variable <-> constraint graph.
37 constexpr int kObjectiveConstraint = -1;
38 constexpr int kAffineRelationConstraint = -2;
39 constexpr int kAssumptionsConstraint = -3;
40 
41 class PresolveContext;
42 
43 // When storing a reference to a literal, it is important not to forget when
44 // reading it back to take its representative. Otherwise, we might introduce
45 // literal that have already been removed, which will break invariants in a
46 // bunch of places.
47 class SavedLiteral {
48  public:
50  explicit SavedLiteral(int ref) : ref_(ref) {}
51  int Get(PresolveContext* context) const;
52 
53  private:
54  int ref_ = 0;
55 };
56 
57 // Same as SavedLiteral for variable.
59  public:
61  explicit SavedVariable(int ref) : ref_(ref) {}
62  int Get(PresolveContext* context) const;
63 
64  private:
65  int ref_ = 0;
66 };
67 
68 // Wrap the CpModelProto we are presolving with extra data structure like the
69 // in-memory domain of each variables and the constraint variable graph.
71  public:
72  explicit PresolveContext(Model* model, CpModelProto* cp_model,
73  CpModelProto* mapping)
74  : working_model(cp_model),
75  mapping_model(mapping),
76  logger_(model->GetOrCreate<SolverLogger>()),
77  params_(*model->GetOrCreate<SatParameters>()),
78  time_limit_(model->GetOrCreate<TimeLimit>()),
79  random_(model->GetOrCreate<ModelRandomGenerator>()) {}
80 
81  // Helpers to adds new variables to the presolved model.
82  int NewIntVar(const Domain& domain);
83  int NewBoolVar();
84  int GetOrCreateConstantVar(int64_t cst);
85 
86  // a => b.
87  void AddImplication(int a, int b);
88 
89  // b => x in [lb, ub].
90  void AddImplyInDomain(int b, int x, const Domain& domain);
91 
92  // Helpers to query the current domain of a variable.
93  bool DomainIsEmpty(int ref) const;
94  bool IsFixed(int ref) const;
95  bool CanBeUsedAsLiteral(int ref) const;
96  bool LiteralIsTrue(int lit) const;
97  bool LiteralIsFalse(int lit) const;
98  int64_t MinOf(int ref) const;
99  int64_t MaxOf(int ref) const;
100  bool DomainContains(int ref, int64_t value) const;
101  Domain DomainOf(int ref) const;
102 
103  // Helpers to query the current domain of a linear expression.
104  // This doesn't check for integer overflow, but our linear expression
105  // should be such that this cannot happen (tested at validation).
106  int64_t MinOf(const LinearExpressionProto& expr) const;
107  int64_t MaxOf(const LinearExpressionProto& expr) const;
108 
109  // This function takes a positive variable reference.
110  bool DomainOfVarIsIncludedIn(int var, const Domain& domain) {
111  return domains[var].IsIncludedIn(domain);
112  }
113 
114  // Returns true if this ref only appear in one constraint.
115  bool VariableIsUniqueAndRemovable(int ref) const;
116 
117  // Returns true if this ref no longer appears in the model.
118  bool VariableIsNotUsedAnymore(int ref) const;
119 
120  // Functions to make sure that once we remove a variable, we no longer reuse
121  // it.
122  void MarkVariableAsRemoved(int ref);
123  bool VariableWasRemoved(int ref) const;
124 
125  // Same as VariableIsUniqueAndRemovable() except that in this case the
126  // variable also appear in the objective in addition to a single constraint.
127  bool VariableWithCostIsUniqueAndRemovable(int ref) const;
128 
129  // Returns true if an integer variable is only appearing in the rhs of
130  // constraints of the form lit => var in domain. When this is the case, then
131  // we can usually remove this variable and replace these constraints with
132  // the proper constraints on the enforcement literals.
133  bool VariableIsOnlyUsedInEncoding(int ref) const;
134 
135  // Returns false if the new domain is empty. Sets 'domain_modified' (if
136  // provided) to true iff the domain is modified otherwise does not change it.
137  ABSL_MUST_USE_RESULT bool IntersectDomainWith(
138  int ref, const Domain& domain, bool* domain_modified = nullptr);
139 
140  // Returns false if the 'lit' doesn't have the desired value in the domain.
141  ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit);
142  ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit);
143 
144  // This function always return false. It is just a way to make a little bit
145  // more sure that we abort right away when infeasibility is detected.
146  ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(
147  const std::string& message = "") {
148  // TODO(user): Report any explanation for the client in a nicer way?
149  VLOG(1) << "INFEASIBLE: '" << message << "'";
150  DCHECK(!is_unsat);
151  is_unsat = true;
152  return false;
153  }
154  bool ModelIsUnsat() const { return is_unsat; }
155 
156  // Stores a description of a rule that was just applied to have a summary of
157  // what the presolve did at the end.
158  void UpdateRuleStats(const std::string& name, int num_times = 1);
159 
160  // Updates the constraints <-> variables graph. This needs to be called each
161  // time a constraint is modified.
162  void UpdateConstraintVariableUsage(int c);
163 
164  // At the beginning of the presolve, we delay the costly creation of this
165  // "graph" until we at least ran some basic presolve. This is because during
166  // a LNS neighbhorhood, many constraints will be reduced significantly by
167  // this "simple" presolve.
169 
170  // Calls UpdateConstraintVariableUsage() on all newly created constraints.
172 
173  // Returns true if our current constraints <-> variables graph is ok.
174  // This is meant to be used in DEBUG mode only.
176 
177  // Regroups fixed variables with the same value.
178  // TODO(user): Also regroup cte and -cte?
179  void ExploitFixedDomain(int var);
180 
181  // Adds the relation (ref_x = coeff * ref_y + offset) to the repository.
182  // Once the relation is added, it doesn't need to be enforced by a constraint
183  // in the model proto, since we will propagate such relation directly and add
184  // them to the proto at the end of the presolve.
185  //
186  // Returns true if the relation was added.
187  // In some rare case, like if x = 3*z and y = 5*t are already added, we
188  // currently cannot add x = 2 * y and we will return false in these case. So
189  // when this returns false, the relation needs to be enforced by a separate
190  // constraint.
191  //
192  // If the relation was added, both variables will be marked to appear in the
193  // special kAffineRelationConstraint. This will allow to identify when a
194  // variable is no longer needed (only appear there and is not a
195  // representative).
196  bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset);
197 
198  // Adds the fact that ref_a == ref_b using StoreAffineRelation() above.
199  // This should never fail, so the relation will always be added.
200  void StoreBooleanEqualityRelation(int ref_a, int ref_b);
201 
202  // Stores/Get the relation target_ref = abs(ref); The first function returns
203  // false if it already exist and the second false if it is not present.
204  bool StoreAbsRelation(int target_ref, int ref);
205  bool GetAbsRelation(int target_ref, int* ref);
206 
207  // Returns the representative of a literal.
208  int GetLiteralRepresentative(int ref) const;
209 
210  // Returns another reference with exactly the same value.
211  int GetVariableRepresentative(int ref) const;
212 
213  // Used for statistics.
214  int NumAffineRelations() const { return affine_relations_.NumRelations(); }
215  int NumEquivRelations() const { return var_equiv_relations_.NumRelations(); }
216 
217  // This makes sure that the affine relation only uses one of the
218  // representative from the var_equiv_relations.
220 
221  // To facilitate debugging.
222  std::string RefDebugString(int ref) const;
223  std::string AffineRelationDebugString(int ref) const;
224 
225  // Makes sure the domain of ref and of its representative are in sync.
226  // Returns false on unsat.
227  bool PropagateAffineRelation(int ref);
228 
229  // Creates the internal structure for any new variables in working_model.
230  void InitializeNewDomains();
231 
232  // Clears the "rules" statistics.
233  void ClearStats();
234 
235  // Inserts the given literal to encode ref == value.
236  // If an encoding already exists, it adds the two implications between
237  // the previous encoding and the new encoding.
238  //
239  // Important: This does not update the constraint<->variable graph, so
240  // ConstraintVariableGraphIsUpToDate() will be false until
241  // UpdateNewConstraintsVariableUsage() is called.
242  void InsertVarValueEncoding(int literal, int ref, int64_t value);
243 
244  // Gets the associated literal if it is already created. Otherwise
245  // create it, add the corresponding constraints and returns it.
246  //
247  // Important: This does not update the constraint<->variable graph, so
248  // ConstraintVariableGraphIsUpToDate() will be false until
249  // UpdateNewConstraintsVariableUsage() is called.
250  int GetOrCreateVarValueEncoding(int ref, int64_t value);
251 
252  // If not already done, adds a Boolean to represent any integer variables that
253  // take only two values. Make sure all the relevant affine and encoding
254  // relations are updated.
255  //
256  // Note that this might create a new Boolean variable.
258 
259  // Returns true if a literal attached to ref == var exists.
260  // It assigns the corresponding to `literal` if non null.
261  bool HasVarValueEncoding(int ref, int64_t value, int* literal = nullptr);
262 
263  // Stores the fact that literal implies var == value.
264  // It returns true if that information is new.
265  bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value);
266 
267  // Stores the fact that literal implies var != value.
268  // It returns true if that information is new.
269  bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value);
270 
271  // Objective handling functions. We load it at the beginning so that during
272  // presolve we can work on the more efficient hash_map representation.
273  //
274  // Note that ReadObjectiveFromProto() makes sure that var_to_constraints of
275  // all the variable that appear in the objective contains -1. This is later
276  // enforced by all the functions modifying the objective.
277  //
278  // Note(user): Because we process affine relation only on
279  // CanonicalizeObjective(), it is possible that when processing a
280  // canonicalized linear constraint, we don't detect that a variable in affine
281  // relation is in the objective. For now this is fine, because when this is
282  // the case, we also have an affine linear constraint, so we can't really do
283  // anything with that variable since it appear in at least two constraints.
284  void ReadObjectiveFromProto();
285  ABSL_MUST_USE_RESULT bool CanonicalizeObjective();
286  void WriteObjectiveToProto() const;
287 
288  // Given a variable defined by the given inequality that also appear in the
289  // objective, remove it from the objective by transferring its cost to other
290  // variables in the equality.
291  //
292  // If new_vars_in_objective is not nullptr, it will be filled with "new"
293  // variables that where not in the objective before and are after
294  // substitution.
295  //
296  // Returns false, if the substitution cannot be done. This is the case if the
297  // model become UNSAT or if doing it will result in an objective that do not
298  // satisfy our overflow preconditions. Note that this can only happen if the
299  // substitued variable is not implied free (i.e. if its domain is smaller than
300  // the implied domain from the equality).
302  int var_in_equality, int64_t coeff_in_equality,
303  const ConstraintProto& equality,
304  std::vector<int>* new_vars_in_objective = nullptr);
305 
306  // Objective getters.
307  const Domain& ObjectiveDomain() const { return objective_domain_; }
308  const absl::flat_hash_map<int, int64_t>& ObjectiveMap() const {
309  return objective_map_;
310  }
312  return objective_domain_is_constraining_;
313  }
314 
315  // Advanced usage. This should be called when a variable can be removed from
316  // the problem, so we don't count it as part of an affine relation anymore.
319 
320  // Variable <-> constraint graph.
321  // The vector list is sorted and contains unique elements.
322  //
323  // Important: To properly handle the objective, var_to_constraints[objective]
324  // contains -1 so that if the objective appear in only one constraint, the
325  // constraint cannot be simplified.
326  const std::vector<int>& ConstraintToVars(int c) const {
328  return constraint_to_vars_[c];
329  }
330  const absl::flat_hash_set<int>& VarToConstraints(int var) const {
332  return var_to_constraints_[var];
333  }
334  int IntervalUsage(int c) const {
336  return interval_usage_[c];
337  }
338 
339  // Make sure we never delete an "assumption" literal by using a special
340  // constraint for that.
342  for (const int ref : working_model->assumptions()) {
343  var_to_constraints_[PositiveRef(ref)].insert(kAssumptionsConstraint);
344  }
345  }
346 
347  // The following helper adds the following constraint:
348  // result <=> (time_i <= time_j && active_i is true && active_j is true)
349  // and returns the (cached) literal result.
350  //
351  // Note that this cache should just be used temporarily and then cleared
352  // with ClearPrecedenceCache() because there is no mechanism to update the
353  // cached literals when literal equivalence are detected.
354  int GetOrCreateReifiedPrecedenceLiteral(int time_i, int time_j, int active_i,
355  int active_j);
356 
357  // Clear the precedence cache.
358  void ClearPrecedenceCache();
359 
360  SolverLogger* logger() const { return logger_; }
361  const SatParameters& params() const { return params_; }
362  TimeLimit* time_limit() { return time_limit_; }
363  ModelRandomGenerator* random() { return random_; }
364 
365  // For each variables, list the constraints that just enforce a lower bound
366  // (resp. upper bound) on that variable. If all the constraints in which a
367  // variable appear are in the same direction, then we can usually fix a
368  // variable to one of its bound (modulo its cost).
369  //
370  // TODO(user): Keeping these extra vector of hash_set seems inefficient. Come
371  // up with a better way to detect if a variable is only constrainted in one
372  // direction.
373  std::vector<absl::flat_hash_set<int>> var_to_ub_only_constraints;
374  std::vector<absl::flat_hash_set<int>> var_to_lb_only_constraints;
375 
376  CpModelProto* working_model = nullptr;
377  CpModelProto* mapping_model = nullptr;
378 
379  // Indicate if we are allowed to remove irrelevant feasible solution from the
380  // set of feasible solution. For example, if a variable is unused, can we fix
381  // it to an arbitrary value (or its mimimum objective one)? This must be true
382  // if the client wants to enumerate all solutions or wants correct tightened
383  // bounds in the response.
385 
386  // Just used to display statistics on the presolve rules that were used.
387  absl::flat_hash_map<std::string, int> stats_by_rule_name;
388 
389  // Number of "rules" applied. This should be equal to the sum of all numbers
390  // in stats_by_rule_name. This is used to decide if we should do one more pass
391  // of the presolve or not. Note that depending on the presolve transformation,
392  // a rule can correspond to a tiny change or a big change. Because of that,
393  // this isn't a perfect proxy for the efficacy of the presolve.
395 
396  // Temporary storage.
397  std::vector<int> tmp_literals;
398  std::vector<Domain> tmp_term_domains;
399  std::vector<Domain> tmp_left_domains;
400  absl::flat_hash_set<int> tmp_literal_set;
401 
402  // Each time a domain is modified this is set to true.
404 
405  // Advanced presolve. See this class comment.
407 
408  private:
409  // Helper to add an affine relation x = c.y + o to the given repository.
410  bool AddRelation(int x, int y, int64_t c, int64_t o, AffineRelation* repo);
411 
412  void AddVariableUsage(int c);
413  void UpdateLinear1Usage(const ConstraintProto& ct, int c);
414 
415  // Returns true iff the variable is not the representative of an equivalence
416  // class of size at least 2.
417  bool VariableIsNotRepresentativeOfEquivalenceClass(int var) const;
418 
419  // Process encoding_remap_queue_ and updates the encoding maps. This could
420  // lead to UNSAT being detected, in which case it will return false.
421  bool RemapEncodingMaps();
422 
423  // Makes sure we only insert encoding about the current representative.
424  //
425  // Returns false if ref cannot take the given value (it might not have been
426  // propagated yed).
427  bool CanonicalizeEncoding(int* ref, int64_t* value);
428 
429  // Inserts an half reified var value encoding (literal => var ==/!= value).
430  // It returns true if the new state is different from the old state.
431  // Not that if imply_eq is false, the literal will be stored in its negated
432  // form.
433  //
434  // Thus, if you detect literal <=> var == value, then two calls must be made:
435  // InsertHalfVarValueEncoding(literal, var, value, true);
436  // InsertHalfVarValueEncoding(NegatedRef(literal), var, value, false);
437  bool InsertHalfVarValueEncoding(int literal, int var, int64_t value,
438  bool imply_eq);
439 
440  // Insert fully reified var-value encoding.
441  void InsertVarValueEncodingInternal(int literal, int var, int64_t value,
442  bool add_constraints);
443 
444  SolverLogger* logger_;
445  const SatParameters& params_;
446  TimeLimit* time_limit_;
447  ModelRandomGenerator* random_;
448 
449  // Initially false, and set to true on the first inconsistency.
450  bool is_unsat = false;
451 
452  // The current domain of each variables.
453  std::vector<Domain> domains;
454 
455  // Internal representation of the objective. During presolve, we first load
456  // the objective in this format in order to have more efficient substitution
457  // on large problems (also because the objective is often dense). At the end
458  // we re-convert it to its proto form.
459  absl::flat_hash_map<int, int64_t> objective_map_;
460  int64_t objective_overflow_detection_;
461  std::vector<std::pair<int, int64_t>> tmp_entries_;
462  bool objective_domain_is_constraining_ = false;
463  Domain objective_domain_;
464  double objective_offset_;
465  double objective_scaling_factor_;
466 
467  // Constraints <-> Variables graph.
468  std::vector<std::vector<int>> constraint_to_vars_;
469  std::vector<absl::flat_hash_set<int>> var_to_constraints_;
470 
471  // Number of constraints of the form [lit =>] var in domain.
472  std::vector<int> constraint_to_linear1_var_;
473  std::vector<int> var_to_num_linear1_;
474 
475  // We maintain how many time each interval is used.
476  std::vector<std::vector<int>> constraint_to_intervals_;
477  std::vector<int> interval_usage_;
478 
479  // Contains abs relation (key = abs(saved_variable)).
480  absl::flat_hash_map<int, SavedVariable> abs_relations_;
481 
482  // For each constant variable appearing in the model, we maintain a reference
483  // variable with the same constant value. If two variables end up having the
484  // same fixed value, then we can detect it using this and add a new
485  // equivalence relation. See ExploitFixedDomain().
486  absl::flat_hash_map<int64_t, SavedVariable> constant_to_ref_;
487 
488  // When a "representative" gets a new representative, it should be enqueued
489  // here so that we can lazily update the *encoding_ maps below.
490  std::deque<int> encoding_remap_queue_;
491 
492  // Contains variables with some encoded value: encoding_[i][v] points
493  // to the literal attached to the value v of the variable i.
494  absl::flat_hash_map<int, absl::flat_hash_map<int64_t, SavedLiteral>>
495  encoding_;
496 
497  // Contains the currently collected half value encodings:
498  // i.e.: literal => var ==/!= value
499  // The state is accumulated (adding x => var == value then !x => var != value)
500  // will deduce that x equivalent to var == value.
501  absl::flat_hash_map<int,
502  absl::flat_hash_map<int64_t, absl::flat_hash_set<int>>>
503  eq_half_encoding_;
504  absl::flat_hash_map<int,
505  absl::flat_hash_map<int64_t, absl::flat_hash_set<int>>>
506  neq_half_encoding_;
507 
508  // This regroups all the affine relations between variables. Note that the
509  // constraints used to detect such relations will not be removed from the
510  // model at detection time (thus allowing proper domain propagation). However,
511  // if the arity of a variable becomes one, then such constraint will be
512  // removed.
513  AffineRelation affine_relations_;
514  AffineRelation var_equiv_relations_;
515 
516  std::vector<int> tmp_new_usage_;
517 
518  // Used by SetVariableAsRemoved() and VariableWasRemoved().
519  absl::flat_hash_set<int> removed_variables_;
520 
521  // Cache for the reified precedence literals created during the expansion of
522  // the reservoir constraint. This cache is only valid during the expansion
523  // phase, and is cleared afterwards.
524  absl::flat_hash_map<std::tuple<int, int, int, int>, int>
525  reified_precedences_cache_;
526 };
527 
528 } // namespace sat
529 } // namespace operations_research
530 
531 #endif // OR_TOOLS_SAT_PRESOLVE_CONTEXT_H_
#define DCHECK(condition)
Definition: base/logging.h:892
#define VLOG(verboselevel)
Definition: base/logging.h:986
We call domain any subset of Int64 = [kint64min, kint64max].
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
bool StoreAbsRelation(int target_ref, int ref)
PresolveContext(Model *model, CpModelProto *cp_model, CpModelProto *mapping)
void InsertVarValueEncoding(int literal, int ref, int64_t value)
ABSL_MUST_USE_RESULT bool IntersectDomainWith(int ref, const Domain &domain, bool *domain_modified=nullptr)
std::vector< absl::flat_hash_set< int > > var_to_lb_only_constraints
bool StoreLiteralImpliesVarNEqValue(int literal, int var, int64_t value)
bool DomainOfVarIsIncludedIn(int var, const Domain &domain)
bool VariableWithCostIsUniqueAndRemovable(int ref) const
ABSL_MUST_USE_RESULT bool SetLiteralToTrue(int lit)
bool StoreAffineRelation(int ref_x, int ref_y, int64_t coeff, int64_t offset)
std::vector< absl::flat_hash_set< int > > var_to_ub_only_constraints
bool SubstituteVariableInObjective(int var_in_equality, int64_t coeff_in_equality, const ConstraintProto &equality, std::vector< int > *new_vars_in_objective=nullptr)
const std::vector< int > & ConstraintToVars(int c) const
int GetOrCreateVarValueEncoding(int ref, int64_t value)
ABSL_MUST_USE_RESULT bool NotifyThatModelIsUnsat(const std::string &message="")
std::string AffineRelationDebugString(int ref) const
const absl::flat_hash_map< int, int64_t > & ObjectiveMap() const
absl::flat_hash_map< std::string, int > stats_by_rule_name
void StoreBooleanEqualityRelation(int ref_a, int ref_b)
bool HasVarValueEncoding(int ref, int64_t value, int *literal=nullptr)
bool DomainContains(int ref, int64_t value) const
void UpdateRuleStats(const std::string &name, int num_times=1)
ABSL_MUST_USE_RESULT bool CanonicalizeObjective()
const SatParameters & params() const
AffineRelation::Relation GetAffineRelation(int ref) const
const absl::flat_hash_set< int > & VarToConstraints(int var) const
ABSL_MUST_USE_RESULT bool SetLiteralToFalse(int lit)
int GetOrCreateReifiedPrecedenceLiteral(int time_i, int time_j, int active_i, int active_j)
absl::flat_hash_set< int > tmp_literal_set
void AddImplyInDomain(int b, int x, const Domain &domain)
bool GetAbsRelation(int target_ref, int *ref)
bool StoreLiteralImpliesVarEqValue(int literal, int var, int64_t value)
int Get(PresolveContext *context) const
int Get(PresolveContext *context) const
int64_t b
int64_t a
const std::string name
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
GurobiMPCallbackContext * context
constexpr int kAffineRelationConstraint
constexpr int kAssumptionsConstraint
constexpr int kObjectiveConstraint
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:85
std::string message
Definition: trace.cc:398