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