OR-Tools  9.0
sat_inprocessing.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 // This file contains the entry point for our presolve/inprocessing code.
15 //
16 // TODO(user): for now it is mainly presolve, but the idea is to call these
17 // function during the search so they should be as incremental as possible. That
18 // is avoid doing work that is not useful because nothing changed or exploring
19 // parts that were not done during the last round.
20 
21 #ifndef OR_TOOLS_SAT_SAT_INPROCESSING_H_
22 #define OR_TOOLS_SAT_SAT_INPROCESSING_H_
23 
24 #include <cstdint>
25 
27 #include "ortools/sat/clause.h"
28 #include "ortools/sat/model.h"
29 #include "ortools/sat/sat_base.h"
31 #include "ortools/sat/sat_solver.h"
32 #include "ortools/sat/util.h"
35 
36 namespace operations_research {
37 namespace sat {
38 
39 // The order is important and each clauses has a "special" literal that is
40 // put first.
41 //
42 // TODO(user): Use a flat memory structure instead.
44  // Utility function that push back clause but also make sure the given literal
45  // from clause appear first.
47  absl::Span<const Literal> clause);
48 
49  std::deque<std::vector<Literal>> clauses;
50 };
51 
52 class StampingSimplifier;
55 
57  // The time budget to spend.
58  double deterministic_time_limit = 30.0;
59 
60  // We assume this is also true if --v 1 is activated and we actually log
61  // even more in --v 1.
62  bool log_info = false;
63 
64  // See ProbingOptions in probing.h
66 
67  // Whether we perform a transitive reduction of the binary implication graph
68  // after equivalent literal detection and before each probing pass.
69  //
70  // TODO(user): Doing that before the current SAT presolve also change the
71  // possible reduction. This shouldn't matter if we use the binary implication
72  // graph and its reachability instead of just binary clause though.
74 };
75 
76 // We need to keep some information from one call to the next, so we use a
77 // class. Note that as this becomes big, we can probably split it.
78 //
79 // TODO(user): Some algorithms here use the normal SAT propagation engine.
80 // However we might want to temporarily disable activities/phase saving and so
81 // on if we want to run them as in-processing steps so that they
82 // do not "pollute" the normal SAT search.
83 //
84 // TODO(user): For the propagation, this depends on the SatSolver class, which
85 // mean we cannot really use it without some refactoring as an in-processing
86 // from the SatSolver::Solve() function. So we do need a special
87 // InprocessingSolve() that lives outside SatSolver. Alternatively, we can
88 // extract the propagation main loop and conflict analysis from SatSolver.
89 class Inprocessing {
90  public:
92  : assignment_(model->GetOrCreate<Trail>()->Assignment()),
93  implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
94  clause_manager_(model->GetOrCreate<LiteralWatchers>()),
95  trail_(model->GetOrCreate<Trail>()),
96  decision_policy_(model->GetOrCreate<SatDecisionPolicy>()),
97  time_limit_(model->GetOrCreate<TimeLimit>()),
98  sat_solver_(model->GetOrCreate<SatSolver>()),
99  stamping_simplifier_(model->GetOrCreate<StampingSimplifier>()),
100  blocked_clause_simplifier_(
101  model->GetOrCreate<BlockedClauseSimplifier>()),
102  bounded_variable_elimination_(
103  model->GetOrCreate<BoundedVariableElimination>()),
104  model_(model) {}
105 
106  // Does some simplifications until a fix point is reached or the given
107  // deterministic time is passed.
108  bool PresolveLoop(SatPresolveOptions options);
109 
110  // When the option use_sat_inprocessing is true, then this is called at each
111  // restart. It is up to the heuristics here to decide what inprocessing we
112  // do or if we wait for the next call before doing anything.
113  bool InprocessingRound();
114 
115  // Simple wrapper that makes sure all the clauses are attached before a
116  // propagation is performed.
117  bool LevelZeroPropagate();
118 
119  // Detects equivalences in the implication graph and propagates any failed
120  // literal found during the process.
121  bool DetectEquivalencesAndStamp(bool use_transitive_reduction, bool log_info);
122 
123  // Removes fixed variables and exploit equivalence relations to cleanup the
124  // clauses. Returns false if UNSAT.
125  bool RemoveFixedAndEquivalentVariables(bool log_info);
126 
127  // Returns true if there is new fixed variables or new equivalence relations
128  // since RemoveFixedAndEquivalentVariables() was last called.
129  bool MoreFixedVariableToClean() const;
130  bool MoreRedundantVariableToClean() const;
131 
132  // Processes all clauses and see if there is any subsumption/strenghtening
133  // reductions that can be performed. Returns false if UNSAT.
134  bool SubsumeAndStrenghtenRound(bool log_info);
135 
136  private:
137  const VariablesAssignment& assignment_;
138  BinaryImplicationGraph* implication_graph_;
139  LiteralWatchers* clause_manager_;
140  Trail* trail_;
141  SatDecisionPolicy* decision_policy_;
142  TimeLimit* time_limit_;
143  SatSolver* sat_solver_;
144  StampingSimplifier* stamping_simplifier_;
145  BlockedClauseSimplifier* blocked_clause_simplifier_;
146  BoundedVariableElimination* bounded_variable_elimination_;
147 
148  double total_dtime_ = 0.0;
149 
150  // TODO(user): This is only used for calling probing. We should probably
151  // create a Probing class to wraps its data. This will also be needed to not
152  // always probe the same variables in each round if the deterministic time
153  // limit is low.
154  Model* model_;
155 
156  // Last since clause database was cleaned up.
157  int64_t last_num_redundant_literals_ = 0;
158  int64_t last_num_fixed_variables_ = 0;
159 };
160 
161 // Implements "stamping" as described in "Efficient CNF Simplification based on
162 // Binary Implication Graphs", Marijn Heule, Matti Jarvisalo and Armin Biere.
163 //
164 // This sample the implications graph with a spanning tree, and then simplify
165 // all clauses (subsumption / strengthening) using the implications encoded in
166 // this tree. So this allows to consider chain of implications instead of just
167 // direct ones, but depending on the problem, only a small fraction of the
168 // implication graph will be captured by the tree.
169 //
170 // Note that we randomize the spanning tree at each call. This can benefit by
171 // having the implication graph be transitively reduced before.
173  public:
175  : assignment_(model->GetOrCreate<Trail>()->Assignment()),
176  implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
177  clause_manager_(model->GetOrCreate<LiteralWatchers>()),
178  random_(model->GetOrCreate<ModelRandomGenerator>()),
179  time_limit_(model->GetOrCreate<TimeLimit>()) {}
180 
181  // This is "fast" (linear scan + sort of all clauses) so we always complete
182  // the full round.
183  //
184  // TODO(user): To save one scan over all the clauses, we could do the fixed
185  // and equivalence variable cleaning here too.
186  bool DoOneRound(bool log_info);
187 
188  // When we compute stamps, we might detect fixed variable (via failed literal
189  // probing in the implication graph). So it might make sense to do that until
190  // we have dealt with all fixed literals before calling DoOneRound().
191  bool ComputeStampsForNextRound(bool log_info);
192 
193  // Visible for testing.
195 
196  // Using a DFS visiting order, we can answer reachability query in O(1) on a
197  // tree, this is well known. ComputeStamps() also detect failed literal in
198  // the tree and fix them. It can return false on UNSAT.
199  bool ComputeStamps();
201  return first_stamps_[a.Index()] < first_stamps_[b.Index()] &&
202  last_stamps_[b.Index()] < last_stamps_[a.Index()];
203  }
204 
205  bool ProcessClauses();
206 
207  private:
208  const VariablesAssignment& assignment_;
209  BinaryImplicationGraph* implication_graph_;
210  LiteralWatchers* clause_manager_;
211  ModelRandomGenerator* random_;
212  TimeLimit* time_limit_;
213 
214  // For ComputeStampsForNextRound().
215  bool stamps_are_already_computed_ = false;
216 
217  // Reset at each round.
218  double dtime_ = 0.0;
219  int64_t num_subsumed_clauses_ = 0;
220  int64_t num_removed_literals_ = 0;
221  int64_t num_fixed_ = 0;
222 
223  // Encode a spanning tree of the implication graph.
225 
226  // Adjacency list representation of the parents_ tree.
229  std::vector<LiteralIndex> children_;
230 
231  // Temporary data for the DFS.
233  std::vector<LiteralIndex> dfs_stack_;
234 
235  // First/Last visited index in a DFS of the tree above.
238 };
239 
240 // A clause c is "blocked" by a literal l if all clauses containing the
241 // negation of l resolve to trivial clause with c. Blocked clause can be
242 // simply removed from the problem. At postsolve, if a blocked clause is not
243 // satisfied, then l can simply be set to true without breaking any of the
244 // clause containing not(l).
245 //
246 // See the paper "Blocked Clause Elimination", Matti Jarvisalo, Armin Biere,
247 // and Marijn Heule.
248 //
249 // TODO(user): This requires that l only appear in clauses and not in the
250 // integer part of CP-SAT.
252  public:
254  : assignment_(model->GetOrCreate<Trail>()->Assignment()),
255  implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
256  clause_manager_(model->GetOrCreate<LiteralWatchers>()),
257  postsolve_(model->GetOrCreate<PostsolveClauses>()),
258  time_limit_(model->GetOrCreate<TimeLimit>()) {}
259 
260  void DoOneRound(bool log_info);
261 
262  private:
263  void InitializeForNewRound();
264  void ProcessLiteral(Literal current_literal);
265  bool ClauseIsBlocked(Literal current_literal,
266  absl::Span<const Literal> clause);
267 
268  const VariablesAssignment& assignment_;
269  BinaryImplicationGraph* implication_graph_;
270  LiteralWatchers* clause_manager_;
271  PostsolveClauses* postsolve_;
272  TimeLimit* time_limit_;
273 
274  double dtime_ = 0.0;
275  int32_t num_blocked_clauses_ = 0;
276  int64_t num_inspected_literals_ = 0;
277 
278  // Temporary vector to mark literal of a clause.
280 
281  // List of literal to process.
282  // TODO(user): use priority queue?
284  std::deque<Literal> queue_;
285 
286  // We compute the occurrence graph just once at the beginning of each round
287  // and we do not shrink it as we remove blocked clauses.
288  DEFINE_INT_TYPE(ClauseIndex, int32_t);
291  literal_to_clauses_;
292 };
293 
295  public:
297  : parameters_(*model->GetOrCreate<SatParameters>()),
298  assignment_(model->GetOrCreate<Trail>()->Assignment()),
299  implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
300  clause_manager_(model->GetOrCreate<LiteralWatchers>()),
301  postsolve_(model->GetOrCreate<PostsolveClauses>()),
302  trail_(model->GetOrCreate<Trail>()),
303  time_limit_(model->GetOrCreate<TimeLimit>()) {}
304 
305  bool DoOneRound(bool log_info);
306 
307  private:
308  int NumClausesContaining(Literal l);
309  void UpdatePriorityQueue(BooleanVariable var);
310  bool CrossProduct(BooleanVariable var);
311  void DeleteClause(SatClause* sat_clause);
312  void DeleteAllClausesContaining(Literal literal);
313  void AddClause(absl::Span<const Literal> clause);
314  bool RemoveLiteralFromClause(Literal lit, SatClause* sat_clause);
315  bool Propagate();
316 
317  // The actual clause elimination algo. We have two versions, one just compute
318  // the "score" of what will be the final state. The other perform the
319  // resolution, remove old clauses and add the new ones.
320  //
321  // Returns false on UNSAT.
322  template <bool score_only, bool with_binary_only>
323  bool ResolveAllClauseContaining(Literal lit);
324 
325  const SatParameters& parameters_;
326  const VariablesAssignment& assignment_;
327  BinaryImplicationGraph* implication_graph_;
328  LiteralWatchers* clause_manager_;
329  PostsolveClauses* postsolve_;
330  Trail* trail_;
331  TimeLimit* time_limit_;
332 
333  int propagation_index_;
334 
335  double dtime_ = 0.0;
336  int64_t num_inspected_literals_ = 0;
337  int64_t num_simplifications_ = 0;
338  int64_t num_blocked_clauses_ = 0;
339  int64_t num_eliminated_variables_ = 0;
340  int64_t num_literals_diff_ = 0;
341  int64_t num_clauses_diff_ = 0;
342 
343  int64_t new_score_;
344  int64_t score_threshold_;
345 
346  // Temporary vector to mark literal of a clause and compute its resolvant.
348  std::vector<Literal> resolvant_;
349 
350  // Priority queue of variable to process.
351  // We will process highest priority first.
352  struct VariableWithPriority {
353  BooleanVariable var;
354  int32_t priority;
355 
356  // Interface for the IntegerPriorityQueue.
357  int Index() const { return var.value(); }
358  bool operator<(const VariableWithPriority& o) const {
359  return priority < o.priority;
360  }
361  };
362  IntegerPriorityQueue<VariableWithPriority> queue_;
363 
364  // We update the queue_ in batch.
365  absl::StrongVector<BooleanVariable, bool> in_need_to_be_updated_;
366  std::vector<BooleanVariable> need_to_be_updated_;
367 
368  // We compute the occurrence graph just once at the beginning of each round.
369  // We maintains the sizes at all time and lazily shrink the graph with deleted
370  // clauses.
371  DEFINE_INT_TYPE(ClauseIndex, int32_t);
374  literal_to_clauses_;
375  absl::StrongVector<LiteralIndex, int> literal_to_num_clauses_;
376 };
377 
378 } // namespace sat
379 } // namespace operations_research
380 
381 #endif // OR_TOOLS_SAT_SAT_INPROCESSING_H_
An Assignment is a variable -> domains mapping, used to report solutions to the user.
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 PresolveLoop(SatPresolveOptions options)
bool RemoveFixedAndEquivalentVariables(bool log_info)
bool DetectEquivalencesAndStamp(bool use_transitive_reduction, bool log_info)
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
bool ImplicationIsInTree(Literal a, Literal b) const
int64_t b
int64_t a
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:85
std::deque< std::vector< Literal > > clauses
void AddClauseWithSpecialLiteral(Literal literal, absl::Span< const Literal > clause)