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