cadical 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +26 -0
- data/README.md +101 -0
- data/ext/cadical/cadical/LICENSE +28 -0
- data/ext/cadical/cadical/README.md +65 -0
- data/ext/cadical/cadical/VERSION +1 -0
- data/ext/cadical/cadical/src/analyze.cpp +1360 -0
- data/ext/cadical/cadical/src/arena.cpp +30 -0
- data/ext/cadical/cadical/src/arena.hpp +105 -0
- data/ext/cadical/cadical/src/assume.cpp +616 -0
- data/ext/cadical/cadical/src/averages.cpp +34 -0
- data/ext/cadical/cadical/src/averages.hpp +37 -0
- data/ext/cadical/cadical/src/backbone.cpp +631 -0
- data/ext/cadical/cadical/src/backtrack.cpp +177 -0
- data/ext/cadical/cadical/src/backward.cpp +231 -0
- data/ext/cadical/cadical/src/bins.cpp +22 -0
- data/ext/cadical/cadical/src/bins.hpp +22 -0
- data/ext/cadical/cadical/src/block.cpp +824 -0
- data/ext/cadical/cadical/src/block.hpp +37 -0
- data/ext/cadical/cadical/src/cadical.cpp +1026 -0
- data/ext/cadical/cadical/src/cadical.hpp +1419 -0
- data/ext/cadical/cadical/src/ccadical.cpp +208 -0
- data/ext/cadical/cadical/src/ccadical.h +74 -0
- data/ext/cadical/cadical/src/checker.cpp +649 -0
- data/ext/cadical/cadical/src/checker.hpp +173 -0
- data/ext/cadical/cadical/src/clause.cpp +689 -0
- data/ext/cadical/cadical/src/clause.hpp +190 -0
- data/ext/cadical/cadical/src/collect.cpp +545 -0
- data/ext/cadical/cadical/src/compact.cpp +550 -0
- data/ext/cadical/cadical/src/condition.cpp +940 -0
- data/ext/cadical/cadical/src/config.cpp +101 -0
- data/ext/cadical/cadical/src/config.hpp +20 -0
- data/ext/cadical/cadical/src/congruence.cpp +7880 -0
- data/ext/cadical/cadical/src/congruence.hpp +757 -0
- data/ext/cadical/cadical/src/constrain.cpp +64 -0
- data/ext/cadical/cadical/src/contract.cpp +27 -0
- data/ext/cadical/cadical/src/contract.hpp +139 -0
- data/ext/cadical/cadical/src/cover.cpp +704 -0
- data/ext/cadical/cadical/src/cover.hpp +34 -0
- data/ext/cadical/cadical/src/decide.cpp +347 -0
- data/ext/cadical/cadical/src/decompose.cpp +738 -0
- data/ext/cadical/cadical/src/decompose.hpp +23 -0
- data/ext/cadical/cadical/src/deduplicate.cpp +275 -0
- data/ext/cadical/cadical/src/definition.cpp +283 -0
- data/ext/cadical/cadical/src/delay.hpp +38 -0
- data/ext/cadical/cadical/src/drattracer.cpp +153 -0
- data/ext/cadical/cadical/src/drattracer.hpp +55 -0
- data/ext/cadical/cadical/src/elim.cpp +1171 -0
- data/ext/cadical/cadical/src/elim.hpp +53 -0
- data/ext/cadical/cadical/src/elimfast.cpp +570 -0
- data/ext/cadical/cadical/src/ema.cpp +95 -0
- data/ext/cadical/cadical/src/ema.hpp +68 -0
- data/ext/cadical/cadical/src/extend.cpp +289 -0
- data/ext/cadical/cadical/src/external.cpp +1034 -0
- data/ext/cadical/cadical/src/external.hpp +487 -0
- data/ext/cadical/cadical/src/external_propagate.cpp +1316 -0
- data/ext/cadical/cadical/src/factor.cpp +1011 -0
- data/ext/cadical/cadical/src/factor.hpp +54 -0
- data/ext/cadical/cadical/src/file.cpp +506 -0
- data/ext/cadical/cadical/src/file.hpp +210 -0
- data/ext/cadical/cadical/src/flags.cpp +135 -0
- data/ext/cadical/cadical/src/flags.hpp +93 -0
- data/ext/cadical/cadical/src/flip.cpp +269 -0
- data/ext/cadical/cadical/src/format.cpp +89 -0
- data/ext/cadical/cadical/src/format.hpp +36 -0
- data/ext/cadical/cadical/src/frattracer.cpp +277 -0
- data/ext/cadical/cadical/src/frattracer.hpp +67 -0
- data/ext/cadical/cadical/src/gates.cpp +766 -0
- data/ext/cadical/cadical/src/heap.hpp +212 -0
- data/ext/cadical/cadical/src/idruptracer.cpp +566 -0
- data/ext/cadical/cadical/src/idruptracer.hpp +112 -0
- data/ext/cadical/cadical/src/instantiate.cpp +365 -0
- data/ext/cadical/cadical/src/instantiate.hpp +45 -0
- data/ext/cadical/cadical/src/internal.cpp +1265 -0
- data/ext/cadical/cadical/src/internal.hpp +1941 -0
- data/ext/cadical/cadical/src/inttypes.hpp +34 -0
- data/ext/cadical/cadical/src/ipasir.cpp +47 -0
- data/ext/cadical/cadical/src/ipasir.h +37 -0
- data/ext/cadical/cadical/src/kitten.c +2599 -0
- data/ext/cadical/cadical/src/kitten.h +97 -0
- data/ext/cadical/cadical/src/level.hpp +33 -0
- data/ext/cadical/cadical/src/lidruptracer.cpp +657 -0
- data/ext/cadical/cadical/src/lidruptracer.hpp +122 -0
- data/ext/cadical/cadical/src/limit.cpp +146 -0
- data/ext/cadical/cadical/src/limit.hpp +168 -0
- data/ext/cadical/cadical/src/logging.cpp +214 -0
- data/ext/cadical/cadical/src/logging.hpp +98 -0
- data/ext/cadical/cadical/src/lookahead.cpp +520 -0
- data/ext/cadical/cadical/src/lratchecker.cpp +833 -0
- data/ext/cadical/cadical/src/lratchecker.hpp +168 -0
- data/ext/cadical/cadical/src/lrattracer.cpp +200 -0
- data/ext/cadical/cadical/src/lrattracer.hpp +61 -0
- data/ext/cadical/cadical/src/lucky.cpp +506 -0
- data/ext/cadical/cadical/src/message.cpp +218 -0
- data/ext/cadical/cadical/src/message.hpp +65 -0
- data/ext/cadical/cadical/src/minimize.cpp +224 -0
- data/ext/cadical/cadical/src/mobical.cpp +5588 -0
- data/ext/cadical/cadical/src/occs.cpp +52 -0
- data/ext/cadical/cadical/src/occs.hpp +36 -0
- data/ext/cadical/cadical/src/options.cpp +359 -0
- data/ext/cadical/cadical/src/options.hpp +442 -0
- data/ext/cadical/cadical/src/parse.cpp +471 -0
- data/ext/cadical/cadical/src/parse.hpp +75 -0
- data/ext/cadical/cadical/src/phases.cpp +56 -0
- data/ext/cadical/cadical/src/phases.hpp +19 -0
- data/ext/cadical/cadical/src/probe.cpp +990 -0
- data/ext/cadical/cadical/src/profile.cpp +107 -0
- data/ext/cadical/cadical/src/profile.hpp +283 -0
- data/ext/cadical/cadical/src/proof.cpp +691 -0
- data/ext/cadical/cadical/src/proof.hpp +125 -0
- data/ext/cadical/cadical/src/propagate.cpp +584 -0
- data/ext/cadical/cadical/src/queue.cpp +90 -0
- data/ext/cadical/cadical/src/queue.hpp +74 -0
- data/ext/cadical/cadical/src/radix.hpp +180 -0
- data/ext/cadical/cadical/src/random.cpp +207 -0
- data/ext/cadical/cadical/src/random.h +44 -0
- data/ext/cadical/cadical/src/random.hpp +98 -0
- data/ext/cadical/cadical/src/range.hpp +125 -0
- data/ext/cadical/cadical/src/reap.cpp +127 -0
- data/ext/cadical/cadical/src/reap.hpp +28 -0
- data/ext/cadical/cadical/src/reduce.cpp +278 -0
- data/ext/cadical/cadical/src/reluctant.hpp +82 -0
- data/ext/cadical/cadical/src/rephase.cpp +402 -0
- data/ext/cadical/cadical/src/report.cpp +313 -0
- data/ext/cadical/cadical/src/resources.cpp +160 -0
- data/ext/cadical/cadical/src/resources.hpp +16 -0
- data/ext/cadical/cadical/src/restart.cpp +178 -0
- data/ext/cadical/cadical/src/restore.cpp +267 -0
- data/ext/cadical/cadical/src/score.cpp +51 -0
- data/ext/cadical/cadical/src/score.hpp +16 -0
- data/ext/cadical/cadical/src/shrink.cpp +507 -0
- data/ext/cadical/cadical/src/signal.cpp +136 -0
- data/ext/cadical/cadical/src/signal.hpp +33 -0
- data/ext/cadical/cadical/src/solution.cpp +50 -0
- data/ext/cadical/cadical/src/solver.cpp +1871 -0
- data/ext/cadical/cadical/src/stable.cpp +31 -0
- data/ext/cadical/cadical/src/stack.h +110 -0
- data/ext/cadical/cadical/src/stats.cpp +920 -0
- data/ext/cadical/cadical/src/stats.hpp +418 -0
- data/ext/cadical/cadical/src/subsume.cpp +645 -0
- data/ext/cadical/cadical/src/sweep.cpp +1960 -0
- data/ext/cadical/cadical/src/sweep.hpp +61 -0
- data/ext/cadical/cadical/src/terminal.cpp +38 -0
- data/ext/cadical/cadical/src/terminal.hpp +96 -0
- data/ext/cadical/cadical/src/ternary.cpp +450 -0
- data/ext/cadical/cadical/src/testing.hpp +24 -0
- data/ext/cadical/cadical/src/tier.cpp +189 -0
- data/ext/cadical/cadical/src/tracer.hpp +186 -0
- data/ext/cadical/cadical/src/transred.cpp +253 -0
- data/ext/cadical/cadical/src/unstable.cpp +29 -0
- data/ext/cadical/cadical/src/util.cpp +129 -0
- data/ext/cadical/cadical/src/util.hpp +181 -0
- data/ext/cadical/cadical/src/var.cpp +39 -0
- data/ext/cadical/cadical/src/var.hpp +22 -0
- data/ext/cadical/cadical/src/veripbtracer.cpp +427 -0
- data/ext/cadical/cadical/src/veripbtracer.hpp +106 -0
- data/ext/cadical/cadical/src/version.cpp +107 -0
- data/ext/cadical/cadical/src/version.hpp +13 -0
- data/ext/cadical/cadical/src/vivify.cpp +1893 -0
- data/ext/cadical/cadical/src/vivify.hpp +50 -0
- data/ext/cadical/cadical/src/walk.cpp +1088 -0
- data/ext/cadical/cadical/src/walk.hpp +91 -0
- data/ext/cadical/cadical/src/walk_full_occs.cpp +966 -0
- data/ext/cadical/cadical/src/warmup.cpp +403 -0
- data/ext/cadical/cadical/src/watch.cpp +126 -0
- data/ext/cadical/cadical/src/watch.hpp +77 -0
- data/ext/cadical/cadical_ext.c +778 -0
- data/ext/cadical/extconf.rb +26 -0
- data/lib/cadical/version.rb +3 -0
- data/lib/cadical.rb +12 -0
- metadata +218 -0
|
@@ -0,0 +1,1171 @@
|
|
|
1
|
+
#include "internal.hpp"
|
|
2
|
+
|
|
3
|
+
namespace CaDiCaL {
|
|
4
|
+
|
|
5
|
+
/*------------------------------------------------------------------------*/
|
|
6
|
+
|
|
7
|
+
// Implements a variant of bounded variable elimination as originally
|
|
8
|
+
// described in our SAT'05 paper introducing 'SATeLite'. This is an
|
|
9
|
+
// inprocessing version, i.e., it is interleaved with search and triggers
|
|
10
|
+
// subsumption and strengthening, blocked and covered clause elimination
|
|
11
|
+
// during elimination rounds. It focuses only those variables which
|
|
12
|
+
// occurred in removed irredundant clauses since the last time an
|
|
13
|
+
// elimination round was run. By bounding the maximum resolvent size we can
|
|
14
|
+
// run each elimination round until completion. See the code of 'elim' for
|
|
15
|
+
// how elimination rounds are interleaved with blocked clause elimination
|
|
16
|
+
// and subsumption (which in turn also calls vivification and transitive
|
|
17
|
+
// reduction of the binary implication graph).
|
|
18
|
+
|
|
19
|
+
/*------------------------------------------------------------------------*/
|
|
20
|
+
|
|
21
|
+
inline double Internal::compute_elim_score (unsigned lit) {
|
|
22
|
+
assert (1 <= lit), assert (lit <= (unsigned) max_var);
|
|
23
|
+
const double pos = noccs (lit);
|
|
24
|
+
const double neg = noccs (-lit);
|
|
25
|
+
if (!pos)
|
|
26
|
+
return -neg;
|
|
27
|
+
if (!neg)
|
|
28
|
+
return -pos;
|
|
29
|
+
double sum = 0, prod = 0;
|
|
30
|
+
if (opts.elimsum)
|
|
31
|
+
sum = opts.elimsum * (pos + neg);
|
|
32
|
+
if (opts.elimprod)
|
|
33
|
+
prod = opts.elimprod * (pos * neg);
|
|
34
|
+
return prod + sum;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*------------------------------------------------------------------------*/
|
|
38
|
+
|
|
39
|
+
bool elim_more::operator() (unsigned a, unsigned b) {
|
|
40
|
+
const auto s = internal->compute_elim_score (a);
|
|
41
|
+
const auto t = internal->compute_elim_score (b);
|
|
42
|
+
if (s > t)
|
|
43
|
+
return true;
|
|
44
|
+
if (s < t)
|
|
45
|
+
return false;
|
|
46
|
+
return a > b;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/*------------------------------------------------------------------------*/
|
|
50
|
+
|
|
51
|
+
// Note that the new fast subsumption algorithm implemented in 'subsume'
|
|
52
|
+
// does not distinguish between irredundant and redundant clauses and is
|
|
53
|
+
// also run during search to strengthen and remove 'sticky' redundant
|
|
54
|
+
// clauses but also irredundant ones. So beside learned units during search
|
|
55
|
+
// or as consequence of other preprocessors, these subsumption rounds during
|
|
56
|
+
// search can remove (irredundant) clauses (and literals), which in turn
|
|
57
|
+
// might make new bounded variable elimination possible. This is tested
|
|
58
|
+
// in the 'bool ineliminating ()' guard.
|
|
59
|
+
|
|
60
|
+
bool Internal::ineliminating () {
|
|
61
|
+
|
|
62
|
+
if (!opts.elim)
|
|
63
|
+
return false;
|
|
64
|
+
if (!preprocessing && !opts.inprocessing)
|
|
65
|
+
return false;
|
|
66
|
+
if (preprocessing)
|
|
67
|
+
assert (lim.preprocessing);
|
|
68
|
+
|
|
69
|
+
// Respect (increasing) conflict limit.
|
|
70
|
+
//
|
|
71
|
+
if (lim.elim >= stats.conflicts)
|
|
72
|
+
return false;
|
|
73
|
+
|
|
74
|
+
// Wait until there are new units or new removed variables
|
|
75
|
+
// (in removed or shrunken irredundant clauses and thus marked).
|
|
76
|
+
//
|
|
77
|
+
if (last.elim.fixed < stats.all.fixed)
|
|
78
|
+
return true;
|
|
79
|
+
if (last.elim.marked < stats.mark.elim)
|
|
80
|
+
return true;
|
|
81
|
+
|
|
82
|
+
// VERBOSE (3, "elim not scheduled due to fixpoint");
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/*------------------------------------------------------------------------*/
|
|
87
|
+
|
|
88
|
+
// Update the global elimination schedule after adding or removing a clause.
|
|
89
|
+
|
|
90
|
+
void Internal::elim_update_added_clause (Eliminator &eliminator,
|
|
91
|
+
Clause *c) {
|
|
92
|
+
assert (!c->redundant);
|
|
93
|
+
ElimSchedule &schedule = eliminator.schedule;
|
|
94
|
+
for (const auto &lit : *c) {
|
|
95
|
+
if (!active (lit))
|
|
96
|
+
continue;
|
|
97
|
+
occs (lit).push_back (c);
|
|
98
|
+
if (frozen (lit))
|
|
99
|
+
continue;
|
|
100
|
+
noccs (lit)++;
|
|
101
|
+
const int idx = abs (lit);
|
|
102
|
+
if (schedule.contains (idx))
|
|
103
|
+
schedule.update (idx);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
void Internal::elim_update_removed_lit (Eliminator &eliminator, int lit) {
|
|
108
|
+
if (!active (lit))
|
|
109
|
+
return;
|
|
110
|
+
if (frozen (lit))
|
|
111
|
+
return;
|
|
112
|
+
int64_t &score = noccs (lit);
|
|
113
|
+
assert (score > 0);
|
|
114
|
+
score--;
|
|
115
|
+
const int idx = abs (lit);
|
|
116
|
+
ElimSchedule &schedule = eliminator.schedule;
|
|
117
|
+
if (schedule.contains (idx))
|
|
118
|
+
schedule.update (idx);
|
|
119
|
+
else {
|
|
120
|
+
LOG ("rescheduling %d for elimination after removing clause", idx);
|
|
121
|
+
schedule.push_back (idx);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
void Internal::elim_update_removed_clause (Eliminator &eliminator,
|
|
126
|
+
Clause *c, int except) {
|
|
127
|
+
assert (!c->redundant);
|
|
128
|
+
for (const auto &lit : *c) {
|
|
129
|
+
if (lit == except)
|
|
130
|
+
continue;
|
|
131
|
+
assert (lit != -except);
|
|
132
|
+
elim_update_removed_lit (eliminator, lit);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/*------------------------------------------------------------------------*/
|
|
137
|
+
|
|
138
|
+
// Since we do not have watches we have to do our own unit propagation
|
|
139
|
+
// during elimination as soon we find a unit clause. This finds new units
|
|
140
|
+
// and also marks clauses satisfied by those units as garbage immediately.
|
|
141
|
+
|
|
142
|
+
void Internal::elim_propagate (Eliminator &eliminator, int root) {
|
|
143
|
+
assert (val (root) > 0);
|
|
144
|
+
vector<int> work;
|
|
145
|
+
size_t i = 0;
|
|
146
|
+
work.push_back (root);
|
|
147
|
+
while (i < work.size ()) {
|
|
148
|
+
int lit = work[i++];
|
|
149
|
+
LOG ("elimination propagation of %d", lit);
|
|
150
|
+
assert (val (lit) > 0);
|
|
151
|
+
const Occs &ns = occs (-lit);
|
|
152
|
+
for (const auto &c : ns) {
|
|
153
|
+
if (c->garbage)
|
|
154
|
+
continue;
|
|
155
|
+
int unit = 0, satisfied = 0;
|
|
156
|
+
for (const auto &other : *c) {
|
|
157
|
+
const signed char tmp = val (other);
|
|
158
|
+
if (tmp < 0)
|
|
159
|
+
continue;
|
|
160
|
+
if (tmp > 0) {
|
|
161
|
+
satisfied = other;
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
if (unit)
|
|
165
|
+
unit = INT_MIN;
|
|
166
|
+
else
|
|
167
|
+
unit = other;
|
|
168
|
+
}
|
|
169
|
+
if (satisfied) {
|
|
170
|
+
LOG (c, "elimination propagation of %d finds %d satisfied", lit,
|
|
171
|
+
satisfied);
|
|
172
|
+
elim_update_removed_clause (eliminator, c, satisfied);
|
|
173
|
+
mark_garbage (c);
|
|
174
|
+
} else if (!unit) {
|
|
175
|
+
LOG ("empty clause during elimination propagation of %d", lit);
|
|
176
|
+
// need to set conflict = c for lrat
|
|
177
|
+
conflict = c;
|
|
178
|
+
learn_empty_clause ();
|
|
179
|
+
conflict = 0;
|
|
180
|
+
break;
|
|
181
|
+
} else if (unit != INT_MIN) {
|
|
182
|
+
LOG ("new unit %d during elimination propagation of %d", unit, lit);
|
|
183
|
+
build_chain_for_units (unit, c, 0);
|
|
184
|
+
assign_unit (unit);
|
|
185
|
+
work.push_back (unit);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (unsat)
|
|
189
|
+
break;
|
|
190
|
+
const Occs &ps = occs (lit);
|
|
191
|
+
for (const auto &c : ps) {
|
|
192
|
+
if (c->garbage)
|
|
193
|
+
continue;
|
|
194
|
+
LOG (c, "elimination propagation of %d produces satisfied", lit);
|
|
195
|
+
elim_update_removed_clause (eliminator, c, lit);
|
|
196
|
+
mark_garbage (c);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/*------------------------------------------------------------------------*/
|
|
202
|
+
|
|
203
|
+
// On-the-fly self-subsuming resolution during variable elimination is due
|
|
204
|
+
// to HyoJung Han, Fabio Somenzi, SAT'09. Basically while resolving two
|
|
205
|
+
// clauses we test the resolvent to be smaller than one of the antecedents.
|
|
206
|
+
// If this is the case the pivot can be removed from the antecedent
|
|
207
|
+
// on-the-fly and the resolution can be skipped during elimination.
|
|
208
|
+
|
|
209
|
+
void Internal::elim_on_the_fly_self_subsumption (Eliminator &eliminator,
|
|
210
|
+
Clause *c, int pivot) {
|
|
211
|
+
LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot);
|
|
212
|
+
stats.elimotfstr++;
|
|
213
|
+
stats.strengthened++;
|
|
214
|
+
assert (clause.empty ());
|
|
215
|
+
for (const auto &lit : *c) {
|
|
216
|
+
if (lit == pivot)
|
|
217
|
+
continue;
|
|
218
|
+
const signed char tmp = val (lit);
|
|
219
|
+
assert (tmp <= 0);
|
|
220
|
+
if (tmp < 0)
|
|
221
|
+
continue;
|
|
222
|
+
clause.push_back (lit);
|
|
223
|
+
}
|
|
224
|
+
Clause *r = new_resolved_irredundant_clause ();
|
|
225
|
+
elim_update_added_clause (eliminator, r);
|
|
226
|
+
clause.clear ();
|
|
227
|
+
lrat_chain.clear ();
|
|
228
|
+
elim_update_removed_clause (eliminator, c, pivot);
|
|
229
|
+
mark_garbage (c);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/*------------------------------------------------------------------------*/
|
|
233
|
+
|
|
234
|
+
// Resolve two clauses on the pivot literal 'pivot', which is assumed to
|
|
235
|
+
// occur in opposite phases in 'c' and 'd'. The actual resolvent is stored
|
|
236
|
+
// in the temporary global 'clause' if it is not redundant. It is
|
|
237
|
+
// considered redundant if one of the clauses is already marked as garbage
|
|
238
|
+
// it is root level satisfied, the resolvent is empty, a unit, or produces a
|
|
239
|
+
// self-subsuming resolution, which results in the pivot to be removed from
|
|
240
|
+
// at least one of the antecedents.
|
|
241
|
+
|
|
242
|
+
// Note that current root level assignments are taken into account, i.e., by
|
|
243
|
+
// removing root level falsified literals. The function returns true if the
|
|
244
|
+
// resolvent is not redundant and for instance has to be taken into account
|
|
245
|
+
// during bounded variable elimination.
|
|
246
|
+
|
|
247
|
+
// Detected units are immediately assigned and in case the last argument is
|
|
248
|
+
// true also propagated eagerly in a elimination specific propagation
|
|
249
|
+
// routine, which not only finds units but also updates the schedule.
|
|
250
|
+
|
|
251
|
+
// When this function is called during computation of the number of
|
|
252
|
+
// non-trivial (or non-satisfied) resolvents we can eagerly propagate units.
|
|
253
|
+
// But during actually adding the resolvents this results in problems as we
|
|
254
|
+
// found one rare test case '../test/trace/reg0056.trace' (out of billions),
|
|
255
|
+
// where the pivot itself was assigned during such a propagation while
|
|
256
|
+
// adding resolvents and lead to pushing a clause to the reconstruction
|
|
257
|
+
// stack that later flipped the value of the pivot (while all other literals
|
|
258
|
+
// in that clause were unit implied too). Not pushing the pivot clauses to
|
|
259
|
+
// the reconstruction stack produced a wrong model too. Our fix is to only
|
|
260
|
+
// eagerly propagate during computation of the number of resolvents and
|
|
261
|
+
// otherwise delay propagation until the end of elimination (which is less
|
|
262
|
+
// precise regarding scheduling but very rarely happens).
|
|
263
|
+
|
|
264
|
+
bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c,
|
|
265
|
+
int pivot, Clause *d,
|
|
266
|
+
const bool propagate_eagerly) {
|
|
267
|
+
|
|
268
|
+
assert (!c->redundant);
|
|
269
|
+
assert (!d->redundant);
|
|
270
|
+
|
|
271
|
+
stats.elimres++;
|
|
272
|
+
|
|
273
|
+
if (c->garbage || d->garbage)
|
|
274
|
+
return false;
|
|
275
|
+
if (c->size > d->size) {
|
|
276
|
+
pivot = -pivot;
|
|
277
|
+
swap (c, d);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
assert (!level);
|
|
281
|
+
assert (clause.empty ());
|
|
282
|
+
|
|
283
|
+
int satisfied = 0; // Contains this satisfying literal.
|
|
284
|
+
int tautological = 0; // Clashing literal if tautological.
|
|
285
|
+
|
|
286
|
+
int s = 0; // Actual literals from 'c'.
|
|
287
|
+
int t = 0; // Actual literals from 'd'.
|
|
288
|
+
|
|
289
|
+
// First determine whether the first antecedent is satisfied, add its
|
|
290
|
+
// literals to 'clause' and mark them (except for 'pivot').
|
|
291
|
+
//
|
|
292
|
+
for (const auto &lit : *c) {
|
|
293
|
+
if (lit == pivot) {
|
|
294
|
+
s++;
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
assert (lit != -pivot);
|
|
298
|
+
const signed char tmp = val (lit);
|
|
299
|
+
if (tmp > 0) {
|
|
300
|
+
satisfied = lit;
|
|
301
|
+
break;
|
|
302
|
+
} else if (tmp < 0) {
|
|
303
|
+
if (!lrat)
|
|
304
|
+
continue;
|
|
305
|
+
Flags &f = flags (lit);
|
|
306
|
+
if (f.seen)
|
|
307
|
+
continue;
|
|
308
|
+
analyzed.push_back (lit);
|
|
309
|
+
f.seen = true;
|
|
310
|
+
int64_t id = unit_id (-lit);
|
|
311
|
+
lrat_chain.push_back (id);
|
|
312
|
+
continue;
|
|
313
|
+
} else
|
|
314
|
+
mark (lit), clause.push_back (lit), s++;
|
|
315
|
+
}
|
|
316
|
+
if (satisfied) {
|
|
317
|
+
LOG (c, "satisfied by %d antecedent", satisfied);
|
|
318
|
+
elim_update_removed_clause (eliminator, c, satisfied);
|
|
319
|
+
mark_garbage (c);
|
|
320
|
+
clause.clear ();
|
|
321
|
+
lrat_chain.clear ();
|
|
322
|
+
clear_analyzed_literals ();
|
|
323
|
+
unmark (c);
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Then determine whether the second antecedent is satisfied, add its
|
|
328
|
+
// literal to 'clause' and check whether a clashing literal is found, such
|
|
329
|
+
// that the resolvent would be tautological.
|
|
330
|
+
//
|
|
331
|
+
for (const auto &lit : *d) {
|
|
332
|
+
if (lit == -pivot) {
|
|
333
|
+
t++;
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
assert (lit != pivot);
|
|
337
|
+
signed char tmp = val (lit);
|
|
338
|
+
if (tmp > 0) {
|
|
339
|
+
satisfied = lit;
|
|
340
|
+
break;
|
|
341
|
+
} else if (tmp < 0) {
|
|
342
|
+
if (!lrat)
|
|
343
|
+
continue;
|
|
344
|
+
Flags &f = flags (lit);
|
|
345
|
+
if (f.seen)
|
|
346
|
+
continue;
|
|
347
|
+
analyzed.push_back (lit);
|
|
348
|
+
f.seen = true;
|
|
349
|
+
int64_t id = unit_id (-lit);
|
|
350
|
+
lrat_chain.push_back (id);
|
|
351
|
+
continue;
|
|
352
|
+
} else if ((tmp = marked (lit)) < 0) {
|
|
353
|
+
tautological = lit;
|
|
354
|
+
break;
|
|
355
|
+
} else if (!tmp)
|
|
356
|
+
clause.push_back (lit), t++;
|
|
357
|
+
else
|
|
358
|
+
assert (tmp > 0), t++;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
clear_analyzed_literals ();
|
|
362
|
+
unmark (c);
|
|
363
|
+
const int64_t size = clause.size ();
|
|
364
|
+
|
|
365
|
+
if (lrat) {
|
|
366
|
+
lrat_chain.push_back (d->id);
|
|
367
|
+
lrat_chain.push_back (c->id);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (satisfied) {
|
|
371
|
+
LOG (d, "satisfied by %d antecedent", satisfied);
|
|
372
|
+
elim_update_removed_clause (eliminator, d, satisfied);
|
|
373
|
+
mark_garbage (d);
|
|
374
|
+
clause.clear ();
|
|
375
|
+
lrat_chain.clear ();
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
LOG (c, "first antecedent");
|
|
380
|
+
LOG (d, "second antecedent");
|
|
381
|
+
|
|
382
|
+
if (tautological) {
|
|
383
|
+
clause.clear ();
|
|
384
|
+
LOG ("resolvent tautological on %d", tautological);
|
|
385
|
+
lrat_chain.clear ();
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (!size) {
|
|
390
|
+
clause.clear ();
|
|
391
|
+
LOG ("empty resolvent");
|
|
392
|
+
learn_empty_clause (); // already clears lrat_chain.
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (size == 1) {
|
|
397
|
+
int unit = clause[0];
|
|
398
|
+
LOG ("unit resolvent %d", unit);
|
|
399
|
+
clause.clear ();
|
|
400
|
+
assign_unit (unit); // already clears lrat_chain.
|
|
401
|
+
if (propagate_eagerly)
|
|
402
|
+
elim_propagate (eliminator, unit);
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
LOG (clause, "resolvent");
|
|
407
|
+
assert (!lrat || !lrat_chain.empty ());
|
|
408
|
+
|
|
409
|
+
// Double self-subsuming resolution. The clauses 'c' and 'd' are
|
|
410
|
+
// identical except for the pivot which occurs in different phase. The
|
|
411
|
+
// resolvent subsumes both antecedents.
|
|
412
|
+
|
|
413
|
+
if (s > size && t > size) {
|
|
414
|
+
assert (s == size + 1);
|
|
415
|
+
assert (t == size + 1);
|
|
416
|
+
clause.clear ();
|
|
417
|
+
// LRAT is c + d (+ eventual units)
|
|
418
|
+
elim_on_the_fly_self_subsumption (eliminator, c, pivot);
|
|
419
|
+
LOG (d, "double pivot %d on-the-fly self-subsuming resolution", -pivot);
|
|
420
|
+
stats.elimotfsub++;
|
|
421
|
+
stats.subsumed++;
|
|
422
|
+
elim_update_removed_clause (eliminator, d, -pivot);
|
|
423
|
+
mark_garbage (d);
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Single self-subsuming resolution: The pivot can be removed from 'c',
|
|
428
|
+
// which is implemented by adding a clause which is the same as 'c' but
|
|
429
|
+
// with 'pivot' removed and then marking 'c' as garbage.
|
|
430
|
+
|
|
431
|
+
if (s > size) {
|
|
432
|
+
assert (s == size + 1);
|
|
433
|
+
clause.clear ();
|
|
434
|
+
// LRAT is c + d (+ eventual units)
|
|
435
|
+
elim_on_the_fly_self_subsumption (eliminator, c, pivot);
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Same single self-subsuming resolution situation, but only for 'd'.
|
|
440
|
+
|
|
441
|
+
if (t > size) {
|
|
442
|
+
assert (t == size + 1);
|
|
443
|
+
clause.clear ();
|
|
444
|
+
// LRAT is c + d (+ eventual units) -> same.
|
|
445
|
+
elim_on_the_fly_self_subsumption (eliminator, d, -pivot);
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
if (propagate_eagerly)
|
|
449
|
+
lrat_chain.clear ();
|
|
450
|
+
return true;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/*------------------------------------------------------------------------*/
|
|
454
|
+
|
|
455
|
+
// Check whether the number of non-tautological resolvents on 'pivot' is
|
|
456
|
+
// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This
|
|
457
|
+
// is the main criteria of bounded variable elimination. As a side effect
|
|
458
|
+
// it flushes garbage clauses with that variable, sorts its occurrence lists
|
|
459
|
+
// (smallest clauses first) and also negates pivot if it has more positive
|
|
460
|
+
// than negative occurrences.
|
|
461
|
+
|
|
462
|
+
bool Internal::elim_resolvents_are_bounded (Eliminator &eliminator,
|
|
463
|
+
int pivot) {
|
|
464
|
+
const bool substitute = !eliminator.gates.empty ();
|
|
465
|
+
const bool resolve_gates = eliminator.definition_unit;
|
|
466
|
+
if (substitute)
|
|
467
|
+
LOG ("trying to substitute %d", pivot);
|
|
468
|
+
|
|
469
|
+
stats.elimtried++;
|
|
470
|
+
|
|
471
|
+
assert (!unsat);
|
|
472
|
+
assert (active (pivot));
|
|
473
|
+
|
|
474
|
+
const Occs &ps = occs (pivot);
|
|
475
|
+
const Occs &ns = occs (-pivot);
|
|
476
|
+
const int64_t pos = ps.size ();
|
|
477
|
+
const int64_t neg = ns.size ();
|
|
478
|
+
if (!pos || !neg)
|
|
479
|
+
return lim.elimbound >= 0;
|
|
480
|
+
const int64_t bound = pos + neg + lim.elimbound;
|
|
481
|
+
|
|
482
|
+
LOG ("checking number resolvents on %d bounded by "
|
|
483
|
+
"%" PRId64 " = %" PRId64 " + %" PRId64 " + %" PRId64,
|
|
484
|
+
pivot, bound, pos, neg, lim.elimbound);
|
|
485
|
+
|
|
486
|
+
// Try all resolutions between a positive occurrence (outer loop) of
|
|
487
|
+
// 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the
|
|
488
|
+
// bound on non-tautological resolvents is not hit and the size of the
|
|
489
|
+
// generated resolvents does not exceed the resolvent clause size limit.
|
|
490
|
+
|
|
491
|
+
int64_t resolvents = 0; // Non-tautological resolvents.
|
|
492
|
+
|
|
493
|
+
for (const auto &c : ps) {
|
|
494
|
+
assert (!c->redundant);
|
|
495
|
+
if (c->garbage)
|
|
496
|
+
continue;
|
|
497
|
+
for (const auto &d : ns) {
|
|
498
|
+
assert (!d->redundant);
|
|
499
|
+
if (d->garbage)
|
|
500
|
+
continue;
|
|
501
|
+
if (!resolve_gates && substitute && c->gate == d->gate)
|
|
502
|
+
continue;
|
|
503
|
+
stats.elimrestried++;
|
|
504
|
+
if (resolve_clauses (eliminator, c, pivot, d, true)) {
|
|
505
|
+
resolvents++;
|
|
506
|
+
int size = clause.size ();
|
|
507
|
+
clause.clear ();
|
|
508
|
+
LOG ("now at least %" PRId64
|
|
509
|
+
" non-tautological resolvents on pivot %d",
|
|
510
|
+
resolvents, pivot);
|
|
511
|
+
if (size > opts.elimclslim) {
|
|
512
|
+
LOG ("resolvent size %d too big after %" PRId64
|
|
513
|
+
" resolvents on %d",
|
|
514
|
+
size, resolvents, pivot);
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
if (resolvents > bound) {
|
|
518
|
+
LOG ("too many non-tautological resolvents on %d", pivot);
|
|
519
|
+
return false;
|
|
520
|
+
}
|
|
521
|
+
} else if (unsat)
|
|
522
|
+
return false;
|
|
523
|
+
else if (val (pivot))
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents",
|
|
529
|
+
resolvents, bound);
|
|
530
|
+
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/*------------------------------------------------------------------------*/
|
|
535
|
+
// Add all resolvents on 'pivot' and connect them.
|
|
536
|
+
|
|
537
|
+
inline void Internal::elim_add_resolvents (Eliminator &eliminator,
|
|
538
|
+
int pivot) {
|
|
539
|
+
|
|
540
|
+
const bool substitute = !eliminator.gates.empty ();
|
|
541
|
+
const bool resolve_gates = eliminator.definition_unit;
|
|
542
|
+
if (substitute) {
|
|
543
|
+
LOG ("substituting pivot %d by resolving with %zd gate clauses", pivot,
|
|
544
|
+
eliminator.gates.size ());
|
|
545
|
+
stats.elimsubst++;
|
|
546
|
+
}
|
|
547
|
+
switch (eliminator.gatetype) {
|
|
548
|
+
case EQUI:
|
|
549
|
+
stats.eliminated_equi++;
|
|
550
|
+
break;
|
|
551
|
+
case AND:
|
|
552
|
+
stats.eliminated_and++;
|
|
553
|
+
break;
|
|
554
|
+
case ITE:
|
|
555
|
+
stats.eliminated_ite++;
|
|
556
|
+
break;
|
|
557
|
+
case XOR:
|
|
558
|
+
stats.eliminated_xor++;
|
|
559
|
+
break;
|
|
560
|
+
case DEF:
|
|
561
|
+
stats.eliminated_def++;
|
|
562
|
+
break;
|
|
563
|
+
case NO:
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
LOG ("adding all resolvents on %d", pivot);
|
|
568
|
+
|
|
569
|
+
assert (!val (pivot));
|
|
570
|
+
assert (!flags (pivot).eliminated ());
|
|
571
|
+
|
|
572
|
+
const Occs &ps = occs (pivot);
|
|
573
|
+
const Occs &ns = occs (-pivot);
|
|
574
|
+
#ifdef LOGGING
|
|
575
|
+
int64_t resolvents = 0;
|
|
576
|
+
#endif
|
|
577
|
+
for (auto &c : ps) {
|
|
578
|
+
if (unsat)
|
|
579
|
+
break;
|
|
580
|
+
if (c->garbage)
|
|
581
|
+
continue;
|
|
582
|
+
for (auto &d : ns) {
|
|
583
|
+
if (unsat)
|
|
584
|
+
break;
|
|
585
|
+
if (d->garbage)
|
|
586
|
+
continue;
|
|
587
|
+
if (!resolve_gates && substitute && c->gate == d->gate)
|
|
588
|
+
continue;
|
|
589
|
+
if (!resolve_clauses (eliminator, c, pivot, d, false))
|
|
590
|
+
continue;
|
|
591
|
+
assert (!lrat || !lrat_chain.empty ());
|
|
592
|
+
Clause *r = new_resolved_irredundant_clause ();
|
|
593
|
+
elim_update_added_clause (eliminator, r);
|
|
594
|
+
eliminator.enqueue (r);
|
|
595
|
+
lrat_chain.clear ();
|
|
596
|
+
clause.clear ();
|
|
597
|
+
#ifdef LOGGING
|
|
598
|
+
resolvents++;
|
|
599
|
+
#endif
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/*------------------------------------------------------------------------*/
|
|
607
|
+
|
|
608
|
+
// Remove clauses with 'pivot' and '-pivot' by marking them as garbage and
|
|
609
|
+
// push them on the extension stack.
|
|
610
|
+
|
|
611
|
+
void Internal::mark_eliminated_clauses_as_garbage (
|
|
612
|
+
Eliminator &eliminator, int pivot, bool &deleted_binary_clause) {
|
|
613
|
+
assert (!unsat);
|
|
614
|
+
|
|
615
|
+
LOG ("marking irredundant clauses with %d as garbage", pivot);
|
|
616
|
+
|
|
617
|
+
const int64_t substitute = eliminator.gates.size ();
|
|
618
|
+
if (substitute)
|
|
619
|
+
LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute);
|
|
620
|
+
#ifndef NDEBUG
|
|
621
|
+
int64_t pushed = 0;
|
|
622
|
+
#endif
|
|
623
|
+
Occs &ps = occs (pivot);
|
|
624
|
+
for (const auto &c : ps) {
|
|
625
|
+
if (c->garbage)
|
|
626
|
+
continue;
|
|
627
|
+
assert (!c->redundant);
|
|
628
|
+
if (!substitute || c->gate) {
|
|
629
|
+
if (proof)
|
|
630
|
+
proof->weaken_minus (c);
|
|
631
|
+
if (c->size == 2)
|
|
632
|
+
deleted_binary_clause = true;
|
|
633
|
+
external->push_clause_on_extension_stack (c, pivot);
|
|
634
|
+
#ifndef NDEBUG
|
|
635
|
+
pushed++;
|
|
636
|
+
#endif
|
|
637
|
+
}
|
|
638
|
+
mark_garbage (c);
|
|
639
|
+
elim_update_removed_clause (eliminator, c, pivot);
|
|
640
|
+
}
|
|
641
|
+
erase_occs (ps);
|
|
642
|
+
|
|
643
|
+
LOG ("marking irredundant clauses with %d as garbage", -pivot);
|
|
644
|
+
|
|
645
|
+
Occs &ns = occs (-pivot);
|
|
646
|
+
for (const auto &d : ns) {
|
|
647
|
+
if (d->garbage)
|
|
648
|
+
continue;
|
|
649
|
+
assert (!d->redundant);
|
|
650
|
+
if (!substitute || d->gate) {
|
|
651
|
+
if (proof)
|
|
652
|
+
proof->weaken_minus (d);
|
|
653
|
+
if (d->size == 2)
|
|
654
|
+
deleted_binary_clause = true;
|
|
655
|
+
external->push_clause_on_extension_stack (d, -pivot);
|
|
656
|
+
#ifndef NDEBUG
|
|
657
|
+
pushed++;
|
|
658
|
+
#endif
|
|
659
|
+
}
|
|
660
|
+
mark_garbage (d);
|
|
661
|
+
elim_update_removed_clause (eliminator, d, -pivot);
|
|
662
|
+
}
|
|
663
|
+
erase_occs (ns);
|
|
664
|
+
|
|
665
|
+
if (substitute)
|
|
666
|
+
assert (pushed <= substitute);
|
|
667
|
+
|
|
668
|
+
// Unfortunately, we can not use the trick by Niklas Soerensson anymore,
|
|
669
|
+
// which avoids saving all clauses on the extension stack. This would
|
|
670
|
+
// break our new incremental 'restore' logic.
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/*------------------------------------------------------------------------*/
|
|
674
|
+
|
|
675
|
+
// Try to eliminate 'pivot' by bounded variable elimination.
|
|
676
|
+
void Internal::try_to_eliminate_variable (Eliminator &eliminator, int pivot,
|
|
677
|
+
bool &deleted_binary_clause) {
|
|
678
|
+
|
|
679
|
+
if (!active (pivot))
|
|
680
|
+
return;
|
|
681
|
+
assert (!frozen (pivot));
|
|
682
|
+
|
|
683
|
+
// First flush garbage clauses.
|
|
684
|
+
//
|
|
685
|
+
int64_t pos = flush_occs (pivot);
|
|
686
|
+
int64_t neg = flush_occs (-pivot);
|
|
687
|
+
|
|
688
|
+
if (pos > neg) {
|
|
689
|
+
pivot = -pivot;
|
|
690
|
+
swap (pos, neg);
|
|
691
|
+
}
|
|
692
|
+
LOG ("pivot %d occurs positively %" PRId64
|
|
693
|
+
" times and negatively %" PRId64 " times",
|
|
694
|
+
pivot, pos, neg);
|
|
695
|
+
assert (!eliminator.schedule.contains (abs (pivot)));
|
|
696
|
+
assert (pos <= neg);
|
|
697
|
+
|
|
698
|
+
if (pos && neg > opts.elimocclim) {
|
|
699
|
+
LOG ("too many occurrences thus not eliminated %d", pivot);
|
|
700
|
+
assert (!eliminator.schedule.contains (abs (pivot)));
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
LOG ("trying to eliminate %d", pivot);
|
|
705
|
+
assert (!flags (pivot).eliminated ());
|
|
706
|
+
|
|
707
|
+
// Sort occurrence lists, such that shorter clauses come first.
|
|
708
|
+
Occs &ps = occs (pivot);
|
|
709
|
+
stable_sort (ps.begin (), ps.end (), clause_smaller_size ());
|
|
710
|
+
Occs &ns = occs (-pivot);
|
|
711
|
+
stable_sort (ns.begin (), ns.end (), clause_smaller_size ());
|
|
712
|
+
|
|
713
|
+
if (pos)
|
|
714
|
+
find_gate_clauses (eliminator, pivot);
|
|
715
|
+
|
|
716
|
+
if (!unsat && !val (pivot)) {
|
|
717
|
+
if (elim_resolvents_are_bounded (eliminator, pivot)) {
|
|
718
|
+
LOG ("number of resolvents on %d are bounded", pivot);
|
|
719
|
+
elim_add_resolvents (eliminator, pivot);
|
|
720
|
+
if (!unsat)
|
|
721
|
+
mark_eliminated_clauses_as_garbage (eliminator, pivot,
|
|
722
|
+
deleted_binary_clause);
|
|
723
|
+
if (active (pivot))
|
|
724
|
+
mark_eliminated (pivot);
|
|
725
|
+
} else {
|
|
726
|
+
LOG ("too many resolvents on %d so not eliminated", pivot);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
unmark_gate_clauses (eliminator);
|
|
731
|
+
elim_backward_clauses (eliminator);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/*------------------------------------------------------------------------*/
|
|
735
|
+
|
|
736
|
+
void Internal::
|
|
737
|
+
mark_redundant_clauses_with_eliminated_variables_as_garbage () {
|
|
738
|
+
for (const auto &c : clauses) {
|
|
739
|
+
if (c->garbage || !c->redundant)
|
|
740
|
+
continue;
|
|
741
|
+
bool clean = true;
|
|
742
|
+
for (const auto &lit : *c) {
|
|
743
|
+
Flags &f = flags (lit);
|
|
744
|
+
if (f.eliminated ()) {
|
|
745
|
+
clean = false;
|
|
746
|
+
break;
|
|
747
|
+
}
|
|
748
|
+
if (f.pure ()) {
|
|
749
|
+
clean = false;
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
if (!clean)
|
|
754
|
+
mark_garbage (c);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/*------------------------------------------------------------------------*/
|
|
759
|
+
|
|
760
|
+
// This function performs one round of bounded variable elimination and
|
|
761
|
+
// returns the number of eliminated variables. The additional result
|
|
762
|
+
// 'completed' is true if this elimination round ran to completion (all
|
|
763
|
+
// variables have been tried). Otherwise it was asynchronously terminated
|
|
764
|
+
// or the resolution limit was hit.
|
|
765
|
+
|
|
766
|
+
int Internal::elim_round (bool &completed, bool &deleted_binary_clause) {
|
|
767
|
+
|
|
768
|
+
assert (opts.elim);
|
|
769
|
+
assert (!unsat);
|
|
770
|
+
|
|
771
|
+
START_SIMPLIFIER (elim, ELIM);
|
|
772
|
+
stats.elimrounds++;
|
|
773
|
+
|
|
774
|
+
int64_t marked_before = last.elim.marked;
|
|
775
|
+
last.elim.marked = stats.mark.elim;
|
|
776
|
+
assert (!level);
|
|
777
|
+
|
|
778
|
+
int64_t resolution_limit;
|
|
779
|
+
|
|
780
|
+
if (opts.elimlimited) {
|
|
781
|
+
int64_t delta = stats.propagations.search;
|
|
782
|
+
delta *= 1e-3 * opts.elimeffort;
|
|
783
|
+
if (delta < opts.elimmineff)
|
|
784
|
+
delta = opts.elimmineff;
|
|
785
|
+
if (delta > opts.elimmaxeff)
|
|
786
|
+
delta = opts.elimmaxeff;
|
|
787
|
+
delta = max (delta, (int64_t) 2l * active ());
|
|
788
|
+
|
|
789
|
+
PHASE ("elim-round", stats.elimrounds,
|
|
790
|
+
"limit of %" PRId64 " resolutions", delta);
|
|
791
|
+
|
|
792
|
+
resolution_limit = stats.elimres + delta;
|
|
793
|
+
} else {
|
|
794
|
+
PHASE ("elim-round", stats.elimrounds, "resolutions unlimited");
|
|
795
|
+
resolution_limit = LONG_MAX;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
init_noccs ();
|
|
799
|
+
|
|
800
|
+
// First compute the number of occurrences of each literal and at the same
|
|
801
|
+
// time mark satisfied clauses and update 'elim' flags of variables in
|
|
802
|
+
// clauses with root level assigned literals (both false and true).
|
|
803
|
+
//
|
|
804
|
+
for (const auto &c : clauses) {
|
|
805
|
+
if (c->garbage || c->redundant)
|
|
806
|
+
continue;
|
|
807
|
+
bool satisfied = false, falsified = false;
|
|
808
|
+
for (const auto &lit : *c) {
|
|
809
|
+
const signed char tmp = val (lit);
|
|
810
|
+
if (tmp > 0)
|
|
811
|
+
satisfied = true;
|
|
812
|
+
else if (tmp < 0)
|
|
813
|
+
falsified = true;
|
|
814
|
+
else
|
|
815
|
+
assert (active (lit));
|
|
816
|
+
}
|
|
817
|
+
if (satisfied)
|
|
818
|
+
mark_garbage (c); // forces more precise counts
|
|
819
|
+
else {
|
|
820
|
+
for (const auto &lit : *c) {
|
|
821
|
+
if (!active (lit))
|
|
822
|
+
continue;
|
|
823
|
+
if (falsified)
|
|
824
|
+
mark_elim (lit); // simulate unit propagation
|
|
825
|
+
noccs (lit)++;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
init_occs ();
|
|
831
|
+
|
|
832
|
+
Eliminator eliminator (this);
|
|
833
|
+
ElimSchedule &schedule = eliminator.schedule;
|
|
834
|
+
assert (schedule.empty ());
|
|
835
|
+
|
|
836
|
+
// Now find elimination candidates which occurred in clauses removed since
|
|
837
|
+
// the last time we ran bounded variable elimination, which in turned
|
|
838
|
+
// triggered their 'elim' bit to be set.
|
|
839
|
+
//
|
|
840
|
+
for (auto idx : vars) {
|
|
841
|
+
if (!active (idx))
|
|
842
|
+
continue;
|
|
843
|
+
if (frozen (idx))
|
|
844
|
+
continue;
|
|
845
|
+
if (!flags (idx).elim)
|
|
846
|
+
continue;
|
|
847
|
+
LOG ("scheduling %d for elimination initially", idx);
|
|
848
|
+
schedule.push_back (idx);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
schedule.shrink ();
|
|
852
|
+
|
|
853
|
+
#ifndef QUIET
|
|
854
|
+
int64_t scheduled = schedule.size ();
|
|
855
|
+
#endif
|
|
856
|
+
|
|
857
|
+
PHASE ("elim-round", stats.elimrounds,
|
|
858
|
+
"scheduled %" PRId64 " variables %.0f%% for elimination",
|
|
859
|
+
scheduled, percent (scheduled, active ()));
|
|
860
|
+
|
|
861
|
+
// Connect irredundant clauses.
|
|
862
|
+
//
|
|
863
|
+
for (const auto &c : clauses)
|
|
864
|
+
if (!c->garbage && !c->redundant)
|
|
865
|
+
for (const auto &lit : *c)
|
|
866
|
+
if (active (lit))
|
|
867
|
+
occs (lit).push_back (c);
|
|
868
|
+
|
|
869
|
+
#ifndef QUIET
|
|
870
|
+
const int64_t old_resolutions = stats.elimres;
|
|
871
|
+
#endif
|
|
872
|
+
const int old_eliminated = stats.all.eliminated;
|
|
873
|
+
const int old_fixed = stats.all.fixed;
|
|
874
|
+
|
|
875
|
+
// Limit on garbage literals during variable elimination. If the limit is
|
|
876
|
+
// hit a garbage collection is performed.
|
|
877
|
+
//
|
|
878
|
+
const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20);
|
|
879
|
+
|
|
880
|
+
// Main loops tries to eliminate variables according to the schedule. The
|
|
881
|
+
// schedule is updated dynamically and variables are potentially
|
|
882
|
+
// rescheduled to be tried again if they occur in a removed clause.
|
|
883
|
+
//
|
|
884
|
+
#ifndef QUIET
|
|
885
|
+
int64_t tried = 0;
|
|
886
|
+
#endif
|
|
887
|
+
while (!unsat && !terminated_asynchronously () &&
|
|
888
|
+
stats.elimres <= resolution_limit && !schedule.empty ()) {
|
|
889
|
+
int idx = schedule.front ();
|
|
890
|
+
schedule.pop_front ();
|
|
891
|
+
flags (idx).elim = false;
|
|
892
|
+
try_to_eliminate_variable (eliminator, idx, deleted_binary_clause);
|
|
893
|
+
#ifndef QUIET
|
|
894
|
+
tried++;
|
|
895
|
+
#endif
|
|
896
|
+
if (stats.garbage.literals <= garbage_limit)
|
|
897
|
+
continue;
|
|
898
|
+
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
|
|
899
|
+
garbage_collection ();
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// If the schedule is empty all variables have been tried (even
|
|
903
|
+
// rescheduled ones). Otherwise asynchronous termination happened or we
|
|
904
|
+
// ran into the resolution limit (or derived unsatisfiability).
|
|
905
|
+
//
|
|
906
|
+
completed = !schedule.size ();
|
|
907
|
+
|
|
908
|
+
if (!completed)
|
|
909
|
+
last.elim.marked = marked_before;
|
|
910
|
+
|
|
911
|
+
PHASE ("elim-round", stats.elimrounds,
|
|
912
|
+
"tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)",
|
|
913
|
+
tried, percent (tried, scheduled), schedule.size ());
|
|
914
|
+
|
|
915
|
+
schedule.erase ();
|
|
916
|
+
|
|
917
|
+
// Collect potential literal clause instantiation pairs, which needs full
|
|
918
|
+
// occurrence lists and thus we have it here before resetting them.
|
|
919
|
+
//
|
|
920
|
+
Instantiator instantiator;
|
|
921
|
+
if (!unsat && !terminated_asynchronously () && opts.instantiate)
|
|
922
|
+
collect_instantiation_candidates (instantiator);
|
|
923
|
+
|
|
924
|
+
reset_occs ();
|
|
925
|
+
reset_noccs ();
|
|
926
|
+
|
|
927
|
+
// Mark all redundant clauses with eliminated variables as garbage.
|
|
928
|
+
//
|
|
929
|
+
if (!unsat)
|
|
930
|
+
mark_redundant_clauses_with_eliminated_variables_as_garbage ();
|
|
931
|
+
|
|
932
|
+
int eliminated = stats.all.eliminated - old_eliminated;
|
|
933
|
+
#ifndef QUIET
|
|
934
|
+
int64_t resolutions = stats.elimres - old_resolutions;
|
|
935
|
+
PHASE ("elim-round", stats.elimrounds,
|
|
936
|
+
"eliminated %d variables %.0f%% in %" PRId64 " resolutions",
|
|
937
|
+
eliminated, percent (eliminated, scheduled), resolutions);
|
|
938
|
+
#endif
|
|
939
|
+
|
|
940
|
+
last.elim.subsumephases = stats.subsumephases;
|
|
941
|
+
const int units = stats.all.fixed - old_fixed;
|
|
942
|
+
report ('e', !opts.reportall && !(eliminated + units));
|
|
943
|
+
STOP_SIMPLIFIER (elim, ELIM);
|
|
944
|
+
|
|
945
|
+
if (!unsat && !terminated_asynchronously () &&
|
|
946
|
+
instantiator) // Do we have candidate pairs?
|
|
947
|
+
instantiate (instantiator);
|
|
948
|
+
|
|
949
|
+
return eliminated; // non-zero if successful
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/*------------------------------------------------------------------------*/
|
|
953
|
+
|
|
954
|
+
// Increase elimination bound (additional clauses allowed during variable
|
|
955
|
+
// elimination), which is triggered if elimination with last bound completed
|
|
956
|
+
// (including no new subsumptions). This was pioneered by GlueMiniSAT in
|
|
957
|
+
// the SAT Race 2015 and then picked up Chanseok Oh in his COMinisatPS
|
|
958
|
+
// solver, which in turn is used in the Maple series of SAT solvers.
|
|
959
|
+
// The bound is no increased if the maximum bound is reached.
|
|
960
|
+
|
|
961
|
+
void Internal::increase_elimination_bound () {
|
|
962
|
+
|
|
963
|
+
if (lim.elimbound >= opts.elimboundmax)
|
|
964
|
+
return;
|
|
965
|
+
|
|
966
|
+
if (lim.elimbound < 0)
|
|
967
|
+
lim.elimbound = 0;
|
|
968
|
+
else if (!lim.elimbound)
|
|
969
|
+
lim.elimbound = 1;
|
|
970
|
+
else
|
|
971
|
+
lim.elimbound *= 2;
|
|
972
|
+
|
|
973
|
+
if (lim.elimbound > opts.elimboundmax)
|
|
974
|
+
lim.elimbound = opts.elimboundmax;
|
|
975
|
+
|
|
976
|
+
PHASE ("elim-phase", stats.elimphases,
|
|
977
|
+
"new elimination bound %" PRId64 "", lim.elimbound);
|
|
978
|
+
|
|
979
|
+
// Now reschedule all active variables for elimination again.
|
|
980
|
+
//
|
|
981
|
+
#ifdef LOGGING
|
|
982
|
+
int count = 0;
|
|
983
|
+
#endif
|
|
984
|
+
for (auto idx : vars) {
|
|
985
|
+
if (!active (idx))
|
|
986
|
+
continue;
|
|
987
|
+
if (flags (idx).elim)
|
|
988
|
+
continue;
|
|
989
|
+
mark_elim (idx);
|
|
990
|
+
#ifdef LOGGING
|
|
991
|
+
count++;
|
|
992
|
+
#endif
|
|
993
|
+
}
|
|
994
|
+
LOG ("marked %d variables as elimination candidates", count);
|
|
995
|
+
|
|
996
|
+
report ('^');
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
void Internal::init_citten () {
|
|
1000
|
+
if (!opts.elimdef)
|
|
1001
|
+
return;
|
|
1002
|
+
assert (!citten);
|
|
1003
|
+
citten = kitten_init ();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
void Internal::reset_citten () {
|
|
1007
|
+
if (citten) {
|
|
1008
|
+
kitten_release (citten);
|
|
1009
|
+
citten = 0;
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/*------------------------------------------------------------------------*/
|
|
1014
|
+
|
|
1015
|
+
void Internal::elim (bool update_limits) {
|
|
1016
|
+
|
|
1017
|
+
if (unsat)
|
|
1018
|
+
return;
|
|
1019
|
+
if (level)
|
|
1020
|
+
backtrack ();
|
|
1021
|
+
if (!propagate ()) {
|
|
1022
|
+
learn_empty_clause ();
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
stats.elimphases++;
|
|
1027
|
+
PHASE ("elim-phase", stats.elimphases,
|
|
1028
|
+
"starting at most %d elimination rounds", opts.elimrounds);
|
|
1029
|
+
|
|
1030
|
+
if (external_prop) {
|
|
1031
|
+
assert (!level);
|
|
1032
|
+
private_steps = true;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
#ifndef QUIET
|
|
1036
|
+
int old_active_variables = active ();
|
|
1037
|
+
int old_eliminated = stats.all.eliminated;
|
|
1038
|
+
#endif
|
|
1039
|
+
|
|
1040
|
+
// Make sure there was a complete subsumption phase since last
|
|
1041
|
+
// elimination
|
|
1042
|
+
//
|
|
1043
|
+
if (last.elim.subsumephases == stats.subsumephases)
|
|
1044
|
+
subsume ();
|
|
1045
|
+
|
|
1046
|
+
reset_watches (); // saves lots of memory
|
|
1047
|
+
|
|
1048
|
+
init_citten ();
|
|
1049
|
+
|
|
1050
|
+
// Alternate one round of bounded variable elimination ('elim_round') and
|
|
1051
|
+
// subsumption ('subsume_round'), blocked ('block') and covered clause
|
|
1052
|
+
// elimination ('cover') until nothing changes, or the round limit is hit.
|
|
1053
|
+
// The loop also aborts early if no variable could be eliminated, the
|
|
1054
|
+
// empty clause is resolved, it is asynchronously terminated or a
|
|
1055
|
+
// resolution limit is hit.
|
|
1056
|
+
|
|
1057
|
+
// This variable determines whether the whole loop of this bounded
|
|
1058
|
+
// variable elimination phase ('elim') ran until completion. This
|
|
1059
|
+
// potentially triggers an incremental increase of the elimination bound.
|
|
1060
|
+
//
|
|
1061
|
+
bool phase_complete = false, deleted_binary_clause = false;
|
|
1062
|
+
|
|
1063
|
+
int round = 1;
|
|
1064
|
+
#ifndef QUIET
|
|
1065
|
+
int eliminated = 0;
|
|
1066
|
+
#endif
|
|
1067
|
+
|
|
1068
|
+
bool round_complete = false;
|
|
1069
|
+
while (!unsat && !phase_complete && !terminated_asynchronously ()) {
|
|
1070
|
+
#ifndef QUIET
|
|
1071
|
+
int eliminated =
|
|
1072
|
+
#endif
|
|
1073
|
+
elim_round (round_complete, deleted_binary_clause);
|
|
1074
|
+
|
|
1075
|
+
if (!round_complete) {
|
|
1076
|
+
PHASE ("elim-phase", stats.elimphases, "last round %d incomplete %s",
|
|
1077
|
+
round, eliminated ? "but successful" : "and unsuccessful");
|
|
1078
|
+
assert (!phase_complete);
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
if (round++ >= opts.elimrounds) {
|
|
1083
|
+
PHASE ("elim-phase", stats.elimphases, "round limit %d hit (%s)",
|
|
1084
|
+
round - 1,
|
|
1085
|
+
eliminated ? "though last round successful"
|
|
1086
|
+
: "last round unsuccessful anyhow");
|
|
1087
|
+
assert (!phase_complete);
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Prioritize 'subsumption' over blocked and covered clause elimination.
|
|
1092
|
+
|
|
1093
|
+
if (subsume_round ())
|
|
1094
|
+
continue;
|
|
1095
|
+
if (block ())
|
|
1096
|
+
continue;
|
|
1097
|
+
if (cover ())
|
|
1098
|
+
continue;
|
|
1099
|
+
|
|
1100
|
+
// Was not able to generate new variable elimination candidates after
|
|
1101
|
+
// variable elimination round, neither through subsumption, nor blocked,
|
|
1102
|
+
// nor covered clause elimination.
|
|
1103
|
+
//
|
|
1104
|
+
PHASE ("elim-phase", stats.elimphases,
|
|
1105
|
+
"no new variable elimination candidates");
|
|
1106
|
+
|
|
1107
|
+
assert (round_complete);
|
|
1108
|
+
phase_complete = true;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
if (phase_complete) {
|
|
1112
|
+
stats.elimcompleted++;
|
|
1113
|
+
PHASE ("elim-phase", stats.elimphases,
|
|
1114
|
+
"fully completed elimination %" PRId64
|
|
1115
|
+
" at elimination bound %" PRId64 "",
|
|
1116
|
+
stats.elimcompleted, lim.elimbound);
|
|
1117
|
+
} else {
|
|
1118
|
+
PHASE ("elim-phase", stats.elimphases,
|
|
1119
|
+
"incomplete elimination %" PRId64
|
|
1120
|
+
" at elimination bound %" PRId64 "",
|
|
1121
|
+
stats.elimcompleted + 1, lim.elimbound);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
reset_citten ();
|
|
1125
|
+
if (deleted_binary_clause)
|
|
1126
|
+
delete_garbage_clauses ();
|
|
1127
|
+
init_watches ();
|
|
1128
|
+
connect_watches ();
|
|
1129
|
+
|
|
1130
|
+
if (unsat)
|
|
1131
|
+
LOG ("elimination derived empty clause");
|
|
1132
|
+
else if (propagated < trail.size ()) {
|
|
1133
|
+
LOG ("elimination produced %zd units",
|
|
1134
|
+
(size_t) (trail.size () - propagated));
|
|
1135
|
+
if (!propagate ()) {
|
|
1136
|
+
LOG ("propagating units after elimination results in empty clause");
|
|
1137
|
+
learn_empty_clause ();
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// If we ran variable elimination until completion we increase the
|
|
1142
|
+
// variable elimination bound and reschedule elimination of all variables.
|
|
1143
|
+
//
|
|
1144
|
+
if (phase_complete)
|
|
1145
|
+
increase_elimination_bound ();
|
|
1146
|
+
|
|
1147
|
+
#ifndef QUIET
|
|
1148
|
+
eliminated = stats.all.eliminated - old_eliminated;
|
|
1149
|
+
PHASE ("elim-phase", stats.elimphases, "eliminated %d variables %.2f%%",
|
|
1150
|
+
eliminated, percent (eliminated, old_active_variables));
|
|
1151
|
+
#endif
|
|
1152
|
+
|
|
1153
|
+
if (external_prop) {
|
|
1154
|
+
assert (!level);
|
|
1155
|
+
private_steps = false;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (!update_limits)
|
|
1159
|
+
return;
|
|
1160
|
+
|
|
1161
|
+
int64_t delta = scale (opts.elimint * (stats.elimphases + 1));
|
|
1162
|
+
lim.elim = stats.conflicts + delta;
|
|
1163
|
+
|
|
1164
|
+
PHASE ("elim-phase", stats.elimphases,
|
|
1165
|
+
"new limit at %" PRId64 " conflicts after %" PRId64 " conflicts",
|
|
1166
|
+
lim.elim, delta);
|
|
1167
|
+
|
|
1168
|
+
last.elim.fixed = stats.all.fixed;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
} // namespace CaDiCaL
|