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