1 #include "internal.hpp"
2
3 namespace CaDiCaL {
4
5 /*------------------------------------------------------------------------*/
6
7 // We are using the address of 'decision_reason' as pseudo reason for
8 // decisions to distinguish assignment decisions from other assignments.
9 // Before we added chronological backtracking all learned units were
10 // assigned at decision level zero ('Solver.level == 0') and we just used a
11 // zero pointer as reason. After allowing chronological backtracking units
12 // were also assigned at higher decision level (but with assignment level
13 // zero), and it was not possible anymore to just distinguish the case
14 // 'unit' versus 'decision' by just looking at the current level. Both had
15 // a zero pointer as reason. Now only units have a zero reason and
16 // decisions need to use the pseudo reason 'decision_reason'.
17
18 static Clause decision_reason_clause;
19 static Clause * decision_reason = &decision_reason_clause;
20
21 // If chronological backtracking is used the actual assignment level might
22 // be lower than the current decision level. In this case the assignment
23 // level is defined as the maximum level of the literals in the reason
24 // clause except the literal for which the clause is a reason. This
25 // function determines this assignment level. For non-chronological
26 // backtracking as in classical CDCL this function always returns the
27 // current decision level, the concept of assignment level does not make
28 // sense, and accordingly this function can be skipped.
29
assignment_level(int lit,Clause * reason)30 inline int Internal::assignment_level (int lit, Clause * reason) {
31
32 assert (opts.chrono);
33 if (!reason) return level;
34
35 int res = 0;
36
37 for (const auto & other : *reason) {
38 if (other == lit) continue;
39 assert (val (other));
40 int tmp = var (other).level;
41 if (tmp > res) res = tmp;
42 }
43
44 return res;
45 }
46
47 /*------------------------------------------------------------------------*/
48
search_assign(int lit,Clause * reason)49 inline void Internal::search_assign (int lit, Clause * reason) {
50
51 if (level) require_mode (SEARCH);
52
53 const int idx = vidx (lit);
54 assert (!vals[idx]);
55 assert (!flags (idx).eliminated () || reason == decision_reason);
56 Var & v = var (idx);
57 int lit_level;
58
59 // The following cases are explained in the two comments above before
60 // 'decision_reason' and 'assignment_level'.
61 //
62 if (!reason) lit_level = 0; // unit
63 else if (reason == decision_reason) lit_level = level, reason = 0;
64 else if (opts.chrono) lit_level = assignment_level (lit, reason);
65 else lit_level = level;
66 if (!lit_level) reason = 0;
67
68 v.level = lit_level;
69 v.trail = (int) trail.size ();
70 v.reason = reason;
71 if (!lit_level) learn_unit_clause (lit); // increases 'stats.fixed'
72 const signed_char tmp = sign (lit);
73 vals[idx] = tmp;
74 vals[-idx] = -tmp;
75 assert (val (lit) > 0);
76 assert (val (-lit) < 0);
77 if (!searching_lucky_phases)
78 phases.saved[idx] = tmp; // phase saving during search
79 trail.push_back (lit);
80 #ifdef LOGGING
81 if (!lit_level) LOG ("root-level unit assign %d @ 0", lit);
82 else LOG (reason, "search assign %d @ %d", lit, lit_level);
83 #endif
84 }
85
86 /*------------------------------------------------------------------------*/
87
88 // External versions of 'search_assign' which are not inlined. They either
89 // are used to assign unit clauses on the root-level, in 'decide' to assign
90 // a decision or in 'analyze' to assign the literal 'driven' by a learned
91 // clause. This happens far less frequently than the 'search_assign' above,
92 // which is called directly in 'propagate' below and thus is inlined.
93
assign_unit(int lit)94 void Internal::assign_unit (int lit) {
95 assert (!level);
96 search_assign (lit, 0);
97 }
98
99 // Just assume the given literal as decision (increase decision level and
100 // assign it). This is used below in 'decide'.
101
search_assume_decision(int lit)102 void Internal::search_assume_decision (int lit) {
103 require_mode (SEARCH);
104 assert (propagated == trail.size ());
105 level++;
106 control.push_back (Level (lit, trail.size ()));
107 LOG ("search decide %d", lit);
108 search_assign (lit, decision_reason);
109 }
110
search_assign_driving(int lit,Clause * c)111 void Internal::search_assign_driving (int lit, Clause * c) {
112 require_mode (SEARCH);
113 search_assign (lit, c);
114 }
115
116 /*------------------------------------------------------------------------*/
117
118 // The 'propagate' function is usually the hot-spot of a CDCL SAT solver.
119 // The 'trail' stack saves assigned variables and is used here as BFS queue
120 // for checking clauses with the negation of assigned variables for being in
121 // conflict or whether they produce additional assignments.
122
123 // This version of 'propagate' uses lazy watches and keeps two watched
124 // literals at the beginning of the clause. We also use 'blocking literals'
125 // to reduce the number of times clauses have to be visited (2008 JSAT paper
126 // by Chu, Harwood and Stuckey). The watches know if a watched clause is
127 // binary, in which case it never has to be visited. If a binary clause is
128 // falsified we continue propagating.
129
130 // Finally, for long clauses we save the position of the last watch
131 // replacement in 'pos', which in turn reduces certain quadratic accumulated
132 // propagation costs (2013 JAIR article by Ian Gent) at the expense of four
133 // more bytes for each clause.
134
propagate()135 bool Internal::propagate () {
136
137 if (level) require_mode (SEARCH);
138 assert (!unsat);
139
140 START (propagate);
141
142 // Updating statistics counter in the propagation loops is costly so we
143 // delay until propagation ran to completion.
144 //
145 int64_t before = propagated;
146
147 while (!conflict && propagated != trail.size ()) {
148
149 const int lit = -trail[propagated++];
150 LOG ("propagating %d", -lit);
151 Watches & ws = watches (lit);
152
153 const const_watch_iterator eow = ws.end ();
154 const_watch_iterator i = ws.begin ();
155 watch_iterator j = ws.begin ();
156
157 while (i != eow) {
158
159 const Watch w = *j++ = *i++;
160 const int b = val (w.blit);
161
162 if (b > 0) continue; // blocking literal satisfied
163
164 if (w.binary ()) {
165
166 // In principle we can ignore garbage binary clauses too, but that
167 // would require to dereference the clause pointer all the time with
168 //
169 // if (w.clause->garbage) { j--; continue; } // (*)
170 //
171 // This is too costly. It is however necessary to produce correct
172 // proof traces if binary clauses are traced to be deleted ('d ...'
173 // line) immediately as soon they are marked as garbage. Actually
174 // finding instances where this happens is pretty difficult (six
175 // parallel fuzzing jobs in parallel took an hour), but it does
176 // occur. Our strategy to avoid generating incorrect proofs now is
177 // to delay tracing the deletion of binary clauses marked as garbage
178 // until they are really deleted from memory. For large clauses
179 // this is not necessary since we have to access the clause anyhow.
180 //
181 // Thanks go to Mathias Fleury, who wanted me to explain why the
182 // line '(*)' above was in the code. Removing it actually really
183 // improved running times and thus I tried to find concrete
184 // instances where this happens (which I found), and then
185 // implemented the described fix.
186
187 // Binary clauses are treated separately since they do not require
188 // to access the clause at all (only during conflict analysis, and
189 // there also only to simplify the code).
190
191 if (b < 0) conflict = w.clause; // but continue ...
192 else search_assign (w.blit, w.clause);
193
194 } else {
195
196 if (conflict) break; // Stop if there was a binary conflict already.
197
198 // The cache line with the clause data is forced to be loaded here
199 // and thus this first memory access below is the real hot-spot of
200 // the solver. Note, that this check is positive very rarely and
201 // thus branch prediction should be almost perfect here.
202
203 if (w.clause->garbage) { j--; continue; }
204
205 literal_iterator lits = w.clause->begin ();
206
207 // Simplify code by forcing 'lit' to be the second literal in the
208 // clause. This goes back to MiniSAT. We use a branch-less version
209 // for conditionally swapping the first two literals, since it
210 // turned out to be substantially faster than this one
211 //
212 // if (lits[0] == lit) swap (lits[0], lits[1]);
213 //
214 // which achieves the same effect, but needs a branch.
215 //
216 const int other = lits[0]^lits[1]^lit;
217 lits[0] = other, lits[1] = lit;
218
219 const int u = val (other); // value of the other watch
220
221 if (u > 0) j[-1].blit = other; // satisfied, just replace blit
222 else {
223
224 // This follows Ian Gent's (JAIR'13) idea of saving the position
225 // of the last watch replacement. In essence it needs two copies
226 // of the default search for a watch replacement (in essence the
227 // code in the 'if (v < 0) { ... }' block below), one starting at
228 // the saved position until the end of the clause and then if that
229 // one failed to find a replacement another one starting at the
230 // first non-watched literal until the saved position.
231
232 const int size = w.clause->size;
233 const literal_iterator middle = lits + w.clause->pos;
234 const const_literal_iterator end = lits + size;
235 literal_iterator k = middle;
236
237 // Find replacement watch 'r' at position 'k' with value 'v'.
238
239 int v = -1, r = 0;
240
241 while (k != end && (v = val (r = *k)) < 0)
242 k++;
243
244 if (v < 0) { // need second search starting at the head?
245
246 k = lits + 2;
247 assert (w.clause->pos <= size);
248 while (k != middle && (v = val (r = *k)) < 0)
249 k++;
250 }
251
252 w.clause->pos = k - lits; // always save position
253
254 assert (lits + 2 <= k), assert (k <= w.clause->end ());
255
256 if (v > 0) {
257
258 // Replacement satisfied, so just replace 'blit'.
259
260 j[-1].blit = r;
261
262 } else if (!v) {
263
264 // Found new unassigned replacement literal to be watched.
265
266 LOG (w.clause, "unwatch %d in", lit);
267
268 lits[1] = r;
269 *k = lit;
270 watch_literal (r, lit, w.clause);
271
272 j--; // Drop this watch from the watch list of 'lit'.
273
274 } else if (!u) {
275
276 assert (v < 0);
277
278 // The other watch is unassigned ('!u') and all other literals
279 // assigned to false (still 'v < 0'), thus we found a unit.
280 //
281 search_assign (other, w.clause);
282
283 // Similar code is in the implementation of the SAT'18 paper on
284 // chronological backtracking but in our experience, this code
285 // first does not really seem to be necessary for correctness,
286 // and further does not improve running time either.
287 //
288 if (opts.chrono > 1) {
289
290 const int other_level = var (other).level;
291
292 if (other_level > var (lit).level) {
293
294 // The assignment level of the new unit 'other' is larger
295 // than the assignment level of 'lit'. Thus we should find
296 // another literal in the clause at that higher assignment
297 // level and watch that instead of 'lit'.
298
299 assert (size > 2);
300 assert (lits[0] == other);
301 assert (lits[1] == lit);
302
303 int pos, s = 0;
304
305 for (pos = 2; pos < size; pos++)
306 if (var (s = lits[pos]).level == other_level)
307 break;
308
309 assert (s);
310 assert (pos < size);
311
312 LOG (w.clause, "unwatch %d in", lit);
313 lits[pos] = lit;
314 lits[1] = s;
315 watch_literal (s, other, w.clause);
316
317 j--; // Drop this watch from the watch list of 'lit'.
318 }
319 }
320 } else {
321
322 assert (u < 0);
323 assert (v < 0);
324
325 // The other watch is assigned false ('u < 0') and all other
326 // literals as well (still 'v < 0'), thus we found a conflict.
327
328 conflict = w.clause;
329 break;
330 }
331 }
332 }
333 }
334
335 if (j != i) {
336
337 while (i != eow)
338 *j++ = *i++;
339
340 ws.resize (j - ws.begin ());
341 }
342 }
343
344 if (searching_lucky_phases) {
345
346 if (conflict)
347 LOG (conflict, "ignoring lucky conflict");
348
349 } else {
350
351 // Avoid updating stats eagerly in the hot-spot of the solver.
352 //
353 stats.propagations.search += propagated - before;
354
355 if (!conflict) no_conflict_until = propagated;
356 else {
357
358 if (stable) stats.stabconflicts++;
359 stats.conflicts++;
360
361 LOG (conflict, "conflict");
362
363 // The trail before the current decision level was conflict free.
364 //
365 no_conflict_until = control[level].trail;
366 }
367 }
368
369 STOP (propagate);
370
371 return !conflict;
372 }
373
374 }
375