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