OR-Tools  9.6
probing.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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_PROBING_H_
15 #define OR_TOOLS_SAT_PROBING_H_
16 
17 #include <functional>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/strings/str_cat.h"
23 #include "absl/types/span.h"
24 #include "ortools/sat/clause.h"
26 #include "ortools/sat/integer.h"
27 #include "ortools/sat/model.h"
28 #include "ortools/sat/sat_base.h"
29 #include "ortools/sat/sat_solver.h"
30 #include "ortools/sat/util.h"
31 #include "ortools/util/bitset.h"
32 #include "ortools/util/logging.h"
34 
35 namespace operations_research {
36 namespace sat {
37 
38 class Prober {
39  public:
40  explicit Prober(Model* model);
41 
42  // Fixes Booleans variables to true/false and see what is propagated. This
43  // can:
44  //
45  // - Fix some Boolean variables (if we reach a conflict while probing).
46  //
47  // - Infer new direct implications. We add them directly to the
48  // BinaryImplicationGraph and they can later be used to detect equivalent
49  // literals, expand at most ones clique, etc...
50  //
51  // - Tighten the bounds of integer variables. If we probe the two possible
52  // values of a Boolean (b=0 and b=1), we get for each integer variables two
53  // propagated domain D_0 and D_1. The level zero domain can then be
54  // intersected with D_0 U D_1. This can restrict the lower/upper bounds of a
55  // variable, but it can also create holes in the domain! This will detect
56  // common cases like an integer variable in [0, 10] that actually only take
57  // two values [0] or [10] depending on one Boolean.
58  //
59  // Returns false if the problem was proved INFEASIBLE during probing.
60  //
61  // TODO(user): For now we process the Boolean in their natural order, this is
62  // not the most efficient.
63  //
64  // TODO(user): This might generate a lot of new direct implications. We might
65  // not want to add them directly to the BinaryImplicationGraph and could
66  // instead use them directly to detect equivalent literal like in
67  // ProbeAndFindEquivalentLiteral(). The situation is not clear.
68  //
69  // TODO(user): More generally, we might want to register any literal => bound
70  // in the IntegerEncoder. This would allow to remember them and use them in
71  // other part of the solver (cuts, lifting, ...).
72  //
73  // TODO(user): Rename to include Integer in the name and distinguish better
74  // from FailedLiteralProbing() below.
75  bool ProbeBooleanVariables(double deterministic_time_limit);
76 
77  // Same as above method except it probes only on the variables given in
78  // 'bool_vars'.
79  bool ProbeBooleanVariables(double deterministic_time_limit,
80  absl::Span<const BooleanVariable> bool_vars);
81 
82  bool ProbeOneVariable(BooleanVariable b);
83 
84  // Statistics.
85  // They are reset each time ProbleBooleanVariables() is called.
86  // Note however that we do not reset them on a call to ProbeOneVariable().
87  int num_new_literals_fixed() const { return num_new_literals_fixed_; }
88  int num_new_binary_clauses() const { return num_new_binary_; }
89 
90  // Register a callback that will be called on each "propagation".
91  // One can inspect the VariablesAssignment to see what are the inferred
92  // literals.
93  void SetPropagationCallback(std::function<void(Literal decision)> f) {
94  callback_ = f;
95  }
96 
97  private:
98  bool ProbeOneVariableInternal(BooleanVariable b);
99 
100  // Model owned classes.
101  const Trail& trail_;
102  const VariablesAssignment& assignment_;
103  IntegerTrail* integer_trail_;
104  ImpliedBounds* implied_bounds_;
105  ProductDetector* product_detector_;
106  SatSolver* sat_solver_;
107  TimeLimit* time_limit_;
108  BinaryImplicationGraph* implication_graph_;
109 
110  // To detect literal x that must be true because b => x and not(b) => x.
111  // When probing b, we add all propagated literal to propagated, and when
112  // probing not(b) we check if any are already there.
113  SparseBitset<LiteralIndex> propagated_;
114 
115  // Modifications found during probing.
116  std::vector<Literal> to_fix_at_true_;
117  std::vector<IntegerLiteral> new_integer_bounds_;
118  std::vector<std::pair<Literal, Literal>> new_binary_clauses_;
119 
120  // Probing statistics.
121  int num_new_holes_ = 0;
122  int num_new_binary_ = 0;
123  int num_new_integer_bounds_ = 0;
124  int num_new_literals_fixed_ = 0;
125 
126  std::function<void(Literal decision)> callback_ = nullptr;
127 
128  // Logger.
129  SolverLogger* logger_;
130 };
131 
132 // Try to randomly tweak the search and stop at the first conflict each time.
133 // This can sometimes find feasible solution, but more importantly, it is a form
134 // of probing that can sometimes find small and interesting conflicts or fix
135 // variables. This seems to work well on the SAT14/app/rook-* problems and
136 // do fix more variables if run before probing.
137 //
138 // If a feasible SAT solution is found (i.e. all Boolean assigned), then this
139 // abort and leave the solver with the full solution assigned.
140 //
141 // Returns false iff the problem is UNSAT.
142 bool LookForTrivialSatSolution(double deterministic_time_limit, Model* model);
143 
144 // Options for the FailedLiteralProbing() code below.
145 //
146 // A good reference for the algorithms involved here is the paper "Revisiting
147 // Hyper Binary Resolution" Marijn J. H. Heule, Matti Jarvisalo, Armin Biere,
148 // http://www.cs.utexas.edu/~marijn/cpaior2013.pdf
150  // The probing will consume all this deterministic time or stop if nothing
151  // else can be deduced and everything has been probed until fix-point. The
152  // fix point depend on the extract_binay_clauses option:
153  // - If false, we will just stop when no more failed literal can be found.
154  // - If true, we will do more work and stop when all failed literal have been
155  // found and all hyper binary resolution have been performed.
156  //
157  // TODO(user): We can also provide a middle ground and probe all failed
158  // literal but do not extract all binary clauses.
159  //
160  // Note that the fix-point is unique, modulo the equivalent literal detection
161  // we do. And if we add binary clauses, modulo the transitive reduction of the
162  // binary implication graph.
163  //
164  // To be fast, we only use the binary clauses in the binary implication graph
165  // for the equivalence detection. So the power of the equivalence detection
166  // changes if the extract_binay_clauses option is true or not.
167  //
168  // TODO(user): The fix point is not yet reached since we don't currently
169  // simplify non-binary clauses with these equivalence, but we will.
170  double deterministic_limit = 1.0;
171 
172  // This is also called hyper binary resolution. Basically, we make sure that
173  // the binary implication graph is augmented with all the implication of the
174  // form a => b that can be derived by fixing 'a' at level zero and doing a
175  // propagation using all constraints. Note that we only add clauses that
176  // cannot be derived by the current implication graph.
177  //
178  // With these extra clause the power of the equivalence literal detection
179  // using only the binary implication graph with increase. Note that it is
180  // possible to do exactly the same thing without adding these binary clause
181  // first. This is what is done by yet another probing algorithm (currently in
182  // simplification.cc).
183  //
184  // TODO(user): Note that adding binary clause before/during the SAT presolve
185  // is currently not always a good idea. This is because we don't simplify the
186  // other clause as much as we could. Also, there can be up to a quadratic
187  // number of clauses added this way, which might slow down things a lot. But
188  // then because of the deterministic limit, we usually cannot add too much
189  // clauses, even for huge problems, since we will reach the limit before that.
191 
192  // Use a version of the "Tree look" algorithm as explained in the paper above.
193  // This is usually faster and more efficient. Note that when extracting binary
194  // clauses it might currently produce more "redundant" one in the sense that a
195  // transitive reduction of the binary implication graph after all hyper binary
196  // resolution have been performed may need to do more work.
197  bool use_tree_look = true;
198 
199  // There is two sligthly different implementation of the tree-look algo.
200  //
201  // TODO(user): Decide which one is better, currently the difference seems
202  // small but the queue seems slightly faster.
203  bool use_queue = true;
204 
205  // If we detect as we probe that a new binary clause subsumes one of the
206  // non-binary clause, we will replace the long clause by the binary one. This
207  // is orthogonal to the extract_binary_clauses parameters which will add all
208  // binary clauses but not neceassirly check for subsumption.
210 
211  // We assume this is also true if --v 1 is activated.
212  bool log_info = false;
213 
214  std::string ToString() const {
215  return absl::StrCat("deterministic_limit: ", deterministic_limit,
216  " extract_binary_clauses: ", extract_binary_clauses,
217  " use_tree_look: ", use_tree_look,
218  " use_queue: ", use_queue);
219  }
220 };
221 
222 // Similar to ProbeBooleanVariables() but different :-)
223 //
224 // First, this do not consider integer variable. It doesn't do any disjunctive
225 // reasoning (i.e. changing the domain of an integer variable by intersecting
226 // it with the union of what happen when x is fixed and not(x) is fixed).
227 //
228 // However this should be more efficient and just work better for pure Boolean
229 // problems. On integer problems, we might also want to run this one first,
230 // and then do just one quick pass of ProbeBooleanVariables().
231 //
232 // Note that this by itself just do one "round", look at the code in the
233 // Inprocessing class that call this interleaved with other reductions until a
234 // fix point is reached.
235 //
236 // This can fix a lot of literals via failed literal detection, that is when
237 // we detect that x => not(x) via propagation after taking x as a decision. It
238 // also use the strongly connected component algorithm to detect equivalent
239 // literals.
240 //
241 // It will add any detected binary clause (via hyper binary resolution) to
242 // the implication graph. See the option comments for more details.
243 bool FailedLiteralProbingRound(ProbingOptions options, Model* model);
244 
245 } // namespace sat
246 } // namespace operations_research
247 
248 #endif // OR_TOOLS_SAT_PROBING_H_
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
void SetPropagationCallback(std::function< void(Literal decision)> f)
Definition: probing.h:93
int num_new_binary_clauses() const
Definition: probing.h:88
bool ProbeOneVariable(BooleanVariable b)
Definition: probing.cc:197
bool ProbeBooleanVariables(double deterministic_time_limit)
Definition: probing.cc:54
int num_new_literals_fixed() const
Definition: probing.h:87
int64_t b
GRBmodel * model
bool LookForTrivialSatSolution(double deterministic_time_limit, Model *model)
Definition: probing.cc:297
bool FailedLiteralProbingRound(ProbingOptions options, Model *model)
Definition: probing.cc:375
Collection of objects used to extend the Constraint Solver library.