ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_checker.cpp
Go to the documentation of this file.
1#include "global.h"
2
3#include "internal.hpp"
4
6
7namespace CaDiCaL {
8
9/*------------------------------------------------------------------------*/
10
11inline unsigned Checker::l2u (int lit) {
13 CADICAL_assert (lit != INT_MIN);
14 unsigned res = 2 * (abs (lit) - 1);
15 if (lit < 0)
16 res++;
17 return res;
18}
19
20inline signed char Checker::val (int lit) {
22 CADICAL_assert (lit != INT_MIN);
23 CADICAL_assert (abs (lit) < size_vars);
24 CADICAL_assert (vals[lit] == -vals[-lit]);
25 return vals[lit];
26}
27
28signed char &Checker::mark (int lit) {
29 const unsigned u = l2u (lit);
30 CADICAL_assert (u < marks.size ());
31 return marks[u];
32}
33
34inline CheckerWatcher &Checker::watcher (int lit) {
35 const unsigned u = l2u (lit);
36 CADICAL_assert (u < watchers.size ());
37 return watchers[u];
38}
39
40/*------------------------------------------------------------------------*/
41
42CheckerClause *Checker::new_clause () {
43 const size_t size = simplified.size ();
44 CADICAL_assert (size > 1), CADICAL_assert (size <= UINT_MAX);
45 const size_t bytes = sizeof (CheckerClause) + (size - 2) * sizeof (int);
46 CheckerClause *res = (CheckerClause *) new char[bytes];
47 DeferDeleteArray<char> delete_res ((char *) res);
48 res->next = 0;
49 res->hash = last_hash;
50 res->size = size;
51 int *literals = res->literals, *p = literals;
52 for (const auto &lit : simplified)
53 *p++ = lit;
54 num_clauses++;
55
56 // First two literals are used as watches and should not be false.
57 //
58 for (unsigned i = 0; i < 2; i++) {
59 int lit = literals[i];
60 if (!val (lit))
61 continue;
62 for (unsigned j = i + 1; j < size; j++) {
63 int other = literals[j];
64 if (val (other))
65 continue;
66 swap (literals[i], literals[j]);
67 break;
68 }
69 }
70 CADICAL_assert (!val (literals[0]));
71 CADICAL_assert (!val (literals[1]));
72 watcher (literals[0]).push_back (CheckerWatch (literals[1], res));
73 watcher (literals[1]).push_back (CheckerWatch (literals[0], res));
74
75 delete_res.release ();
76 return res;
77}
78
79void Checker::delete_clause (CheckerClause *c) {
80 if (c->size) {
81 CADICAL_assert (c->size > 1);
82 CADICAL_assert (num_clauses);
83 num_clauses--;
84 } else {
85 CADICAL_assert (num_garbage);
86 num_garbage--;
87 }
88 delete[] (char *) c;
89}
90
91void Checker::enlarge_clauses () {
92 CADICAL_assert (num_clauses == size_clauses);
93 const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1;
94 LOG ("CHECKER enlarging clauses of checker from %" PRIu64 " to %" PRIu64,
95 (uint64_t) size_clauses, (uint64_t) new_size_clauses);
96 CheckerClause **new_clauses;
97 new_clauses = new CheckerClause *[new_size_clauses];
98 clear_n (new_clauses, new_size_clauses);
99 for (uint64_t i = 0; i < size_clauses; i++) {
100 for (CheckerClause *c = clauses[i], *next; c; c = next) {
101 next = c->next;
102 const uint64_t h = reduce_hash (c->hash, new_size_clauses);
103 c->next = new_clauses[h];
104 new_clauses[h] = c;
105 }
106 }
107 delete[] clauses;
108 clauses = new_clauses;
109 size_clauses = new_size_clauses;
110}
111
112bool Checker::clause_satisfied (CheckerClause *c) {
113 for (unsigned i = 0; i < c->size; i++)
114 if (val (c->literals[i]) > 0)
115 return true;
116 return false;
117}
118
119// The main reason why we have an explicit garbage collection phase is that
120// removing clauses from watcher lists eagerly might lead to an accumulated
121// quadratic algorithm. Thus we delay removing garbage clauses from watcher
122// lists until garbage collection (even though we remove garbage clauses on
123// the fly during propagation too). We also remove satisfied clauses.
124//
125void Checker::collect_garbage_clauses () {
126
127 stats.collections++;
128
129 for (size_t i = 0; i < size_clauses; i++) {
130 CheckerClause **p = clauses + i, *c;
131 while ((c = *p)) {
132 if (clause_satisfied (c)) {
133 c->size = 0; // mark as garbage
134 *p = c->next;
135 c->next = garbage;
136 garbage = c;
137 num_garbage++;
138 CADICAL_assert (num_clauses);
139 num_clauses--;
140 } else
141 p = &c->next;
142 }
143 }
144
145 LOG ("CHECKER collecting %" PRIu64 " garbage clauses %.0f%%", num_garbage,
146 percent (num_garbage, num_clauses));
147
148 for (int lit = -size_vars + 1; lit < size_vars; lit++) {
149 if (!lit)
150 continue;
151 CheckerWatcher &ws = watcher (lit);
152 const auto end = ws.end ();
153 auto j = ws.begin (), i = j;
154 for (; i != end; i++) {
155 CheckerWatch &w = *i;
156 if (w.clause->size)
157 *j++ = w;
158 }
159 if (j == ws.end ())
160 continue;
161 if (j == ws.begin ())
162 erase_vector (ws);
163 else
164 ws.resize (j - ws.begin ());
165 }
166
167 for (CheckerClause *c = garbage, *next; c; c = next)
168 next = c->next, delete_clause (c);
169
170 CADICAL_assert (!num_garbage);
171 garbage = 0;
172}
173
174/*------------------------------------------------------------------------*/
175
177 : internal (i), size_vars (0), vals (0), inconsistent (false),
178 num_clauses (0), num_garbage (0), size_clauses (0), clauses (0),
179 garbage (0), next_to_propagate (0), last_hash (0) {
180
181 // Initialize random number table for hash function.
182 //
183 Random random (42);
184 for (unsigned n = 0; n < num_nonces; n++) {
185 uint64_t nonce = random.next ();
186 if (!(nonce & 1))
187 nonce++;
188 CADICAL_assert (nonce), CADICAL_assert (nonce & 1);
189 nonces[n] = nonce;
190 }
191
192 memset (&stats, 0, sizeof (stats)); // Initialize statistics.
193}
194
196 internal = i;
197 LOG ("CHECKER connected to internal");
198}
199
201 LOG ("CHECKER delete");
202 vals -= size_vars;
203 delete[] vals;
204 for (size_t i = 0; i < size_clauses; i++)
205 for (CheckerClause *c = clauses[i], *next; c; c = next)
206 next = c->next, delete_clause (c);
207 for (CheckerClause *c = garbage, *next; c; c = next)
208 next = c->next, delete_clause (c);
209 delete[] clauses;
210}
211
212/*------------------------------------------------------------------------*/
213
214// The simplicity for accessing 'vals' and 'watchers' directly through a
215// signed integer literal, comes with the price of slightly more complex
216// code in deleting and enlarging the checker data structures.
217
218void Checker::enlarge_vars (int64_t idx) {
219
220 CADICAL_assert (0 < idx), CADICAL_assert (idx <= INT_MAX);
221
222 int64_t new_size_vars = size_vars ? 2 * size_vars : 2;
223 while (idx >= new_size_vars)
224 new_size_vars *= 2;
225 LOG ("CHECKER enlarging variables of checker from %" PRId64 " to %" PRId64
226 "",
227 size_vars, new_size_vars);
228
229 signed char *new_vals;
230 new_vals = new signed char[2 * new_size_vars];
231 clear_n (new_vals, 2 * new_size_vars);
232 new_vals += new_size_vars;
233 if (size_vars) // To make sanitizer happy (without '-O').
234 memcpy ((void *) (new_vals - size_vars), (void *) (vals - size_vars),
235 2 * size_vars);
236 vals -= size_vars;
237 delete[] vals;
238 vals = new_vals;
239 size_vars = new_size_vars;
240
241 watchers.resize (2 * new_size_vars);
242 marks.resize (2 * new_size_vars);
243
244 CADICAL_assert (idx < new_size_vars);
245}
246
247inline void Checker::import_literal (int lit) {
249 CADICAL_assert (lit != INT_MIN);
250 int idx = abs (lit);
251 if (idx >= size_vars)
252 enlarge_vars (idx);
253 simplified.push_back (lit);
254 unsimplified.push_back (lit);
255}
256
257void Checker::import_clause (const vector<int> &c) {
258 for (const auto &lit : c)
259 import_literal (lit);
260}
261
263 bool operator() (int a, int b) const {
264 int c = abs (a), d = abs (b);
265 if (c < d)
266 return true;
267 if (c > d)
268 return false;
269 return a < b;
270 }
271};
272
273bool Checker::tautological () {
274 sort (simplified.begin (), simplified.end (), lit_smaller ());
275 const auto end = simplified.end ();
276 auto j = simplified.begin ();
277 int prev = 0;
278 for (auto i = j; i != end; i++) {
279 int lit = *i;
280 if (lit == prev)
281 continue; // duplicated literal
282 if (lit == -prev)
283 return true; // tautological clause
284 const signed char tmp = val (lit);
285 if (tmp > 0)
286 return true; // satisfied literal and clause
287 *j++ = prev = lit;
288 }
289 simplified.resize (j - simplified.begin ());
290 return false;
291}
292
293/*------------------------------------------------------------------------*/
294
295uint64_t Checker::reduce_hash (uint64_t hash, uint64_t size) {
296 CADICAL_assert (size > 0);
297 unsigned shift = 32;
298 uint64_t res = hash;
299 while ((((uint64_t) 1) << shift) > size) {
300 res ^= res >> shift;
301 shift >>= 1;
302 }
303 res &= size - 1;
304 CADICAL_assert (res < size);
305 return res;
306}
307
308uint64_t Checker::compute_hash () {
309 unsigned j = last_id % num_nonces;
310 uint64_t tmp = nonces[j] * last_id;
311 return last_hash = tmp;
312}
313
314CheckerClause **Checker::find () {
315 stats.searches++;
316 CheckerClause **res, *c;
317 const uint64_t hash = compute_hash ();
318 const unsigned size = simplified.size ();
319 const uint64_t h = reduce_hash (hash, size_clauses);
320 for (const auto &lit : simplified)
321 mark (lit) = true;
322 for (res = clauses + h; (c = *res); res = &c->next) {
323 if (c->hash == hash && c->size == size) {
324 bool found = true;
325 const int *literals = c->literals;
326 for (unsigned i = 0; found && i != size; i++)
327 found = mark (literals[i]);
328 if (found)
329 break;
330 }
331 stats.collisions++;
332 }
333 for (const auto &lit : simplified)
334 mark (lit) = false;
335 return res;
336}
337
338void Checker::insert () {
339 stats.insertions++;
340 if (num_clauses == size_clauses)
341 enlarge_clauses ();
342 const uint64_t h = reduce_hash (compute_hash (), size_clauses);
343 CheckerClause *c = new_clause ();
344 c->next = clauses[h];
345 clauses[h] = c;
346}
347
348/*------------------------------------------------------------------------*/
349
350inline void Checker::assign (int lit) {
351 CADICAL_assert (!val (lit));
352 vals[lit] = 1;
353 vals[-lit] = -1;
354 trail.push_back (lit);
355}
356
357inline void Checker::assume (int lit) {
358 signed char tmp = val (lit);
359 if (tmp > 0)
360 return;
361 CADICAL_assert (!tmp);
362 stats.assumptions++;
363 assign (lit);
364}
365
366void Checker::backtrack (unsigned previously_propagated) {
367
368 CADICAL_assert (previously_propagated <= trail.size ());
369
370 while (trail.size () > previously_propagated) {
371 int lit = trail.back ();
372 CADICAL_assert (val (lit) > 0);
373 CADICAL_assert (val (-lit) < 0);
374 vals[lit] = vals[-lit] = 0;
375 trail.pop_back ();
376 }
377
378 trail.resize (previously_propagated);
379 next_to_propagate = previously_propagated;
380 CADICAL_assert (trail.size () == next_to_propagate);
381}
382
383/*------------------------------------------------------------------------*/
384
385// This is a standard propagation routine without using blocking literals
386// nor without saving the last replacement position.
387
388bool Checker::propagate () {
389 bool res = true;
390 while (res && next_to_propagate < trail.size ()) {
391 int lit = trail[next_to_propagate++];
392 stats.propagations++;
393 CADICAL_assert (val (lit) > 0);
394 CADICAL_assert (abs (lit) < size_vars);
395 CheckerWatcher &ws = watcher (-lit);
396 const auto end = ws.end ();
397 auto j = ws.begin (), i = j;
398 for (; res && i != end; i++) {
399 CheckerWatch &w = *j++ = *i;
400 const int blit = w.blit;
401 CADICAL_assert (blit != -lit);
402 const signed char blit_val = val (blit);
403 if (blit_val > 0)
404 continue;
405 const unsigned size = w.size;
406 if (size == 2) { // not precise since
407 if (blit_val < 0)
408 res = false; // clause might be garbage
409 else
410 assign (w.blit); // but still sound
411 } else {
412 CADICAL_assert (size > 2);
413 CheckerClause *c = w.clause;
414 if (!c->size) {
415 j--;
416 continue;
417 } // skip garbage clauses
418 CADICAL_assert (size == c->size);
419 int *lits = c->literals;
420 int other = lits[0] ^ lits[1] ^ (-lit);
421 CADICAL_assert (other != -lit);
422 signed char other_val = val (other);
423 if (other_val > 0) {
424 j[-1].blit = other;
425 continue;
426 }
427 lits[0] = other, lits[1] = -lit;
428 unsigned k;
429 int replacement = 0;
430 signed char replacement_val = -1;
431 for (k = 2; k < size; k++)
432 if ((replacement_val = val (replacement = lits[k])) >= 0)
433 break;
434 if (replacement_val >= 0) {
435 watcher (replacement).push_back (CheckerWatch (-lit, c));
436 swap (lits[1], lits[k]);
437 j--;
438 } else if (!other_val)
439 assign (other);
440 else
441 res = false;
442 }
443 }
444 while (i != end)
445 *j++ = *i++;
446 ws.resize (j - ws.begin ());
447 }
448 return res;
449}
450
451bool Checker::check () {
452 stats.checks++;
453 if (inconsistent)
454 return true;
455 unsigned previously_propagated = next_to_propagate;
456 for (const auto &lit : simplified)
457 assume (-lit);
458 bool res = !propagate ();
459 backtrack (previously_propagated);
460 return res;
461}
462
463bool Checker::check_blocked () {
464 for (const auto &lit : unsimplified) {
465 mark (-lit) = true;
466 }
467 vector<int> not_blocked;
468 for (size_t i = 0; i < size_clauses; i++) {
469 for (CheckerClause *c = clauses[i], *next; c; c = next) {
470 next = c->next;
471 unsigned count = 0;
472 int first;
473 for (int *i = c->literals; i < c->literals + c->size; i++) {
474 const int lit = *i;
475 if (val (lit) > 0) {
476 LOG (c->literals, c->size, "satisfied clause");
477 count = 2;
478 break;
479 }
480 if (mark (lit)) {
481 count++;
482 LOG (c->literals, c->size, "clause");
483 first = lit;
484 }
485 }
486 if (count == 1)
487 not_blocked.push_back (first);
488 }
489 }
490 for (const auto &lit : not_blocked) {
491 mark (lit) = false;
492 }
493 bool blocked = false;
494 for (const auto &lit : unsimplified) {
495 if (mark (-lit))
496 blocked = true;
497 mark (-lit) = false;
498 }
499 return blocked;
500}
501
502/*------------------------------------------------------------------------*/
503
504void Checker::add_clause (const char *type) {
505#ifndef LOGGING
506 (void) type;
507#endif
508
509 // If there are enough garbage clauses collect them first.
510 if (num_garbage > 0.5 * max ((size_t) size_clauses, (size_t) size_vars))
511 collect_garbage_clauses ();
512
513 int unit = 0;
514 for (const auto &lit : simplified) {
515 const signed char tmp = val (lit);
516 if (tmp < 0)
517 continue;
518 CADICAL_assert (!tmp);
519 if (unit) {
520 unit = INT_MIN;
521 break;
522 }
523 unit = lit;
524 }
525
526 if (simplified.empty ()) {
527 LOG ("CHECKER added empty %s clause", type);
528 inconsistent = true;
529 }
530 if (!unit) {
531 LOG ("CHECKER added and checked falsified %s clause", type);
532 inconsistent = true;
533 } else if (unit != INT_MIN) {
534 LOG ("CHECKER added and checked %s unit clause %d", type, unit);
535 assign (unit);
536 stats.units++;
537 if (!propagate ()) {
538 LOG ("CHECKER inconsistent after propagating %s unit", type);
539 inconsistent = true;
540 }
541 } else
542 insert ();
543}
544
545void Checker::add_original_clause (int64_t id, bool, const vector<int> &c,
546 bool) {
547 if (inconsistent)
548 return;
549 START (checking);
550 LOG (c, "CHECKER addition of original clause");
551 stats.added++;
552 stats.original++;
553 import_clause (c);
554 last_id = id;
555 if (tautological ())
556 LOG ("CHECKER ignoring satisfied original clause");
557 else
558 add_clause ("original");
559 simplified.clear ();
560 unsimplified.clear ();
561 STOP (checking);
562}
563
564void Checker::add_derived_clause (int64_t id, bool, const vector<int> &c,
565 const vector<int64_t> &) {
566 if (inconsistent)
567 return;
568 START (checking);
569 LOG (c, "CHECKER addition of derived clause");
570 stats.added++;
571 stats.derived++;
572 import_clause (c);
573 last_id = id;
574 if (tautological ())
575 LOG ("CHECKER ignoring satisfied derived clause");
576 else if (!check () && !check_blocked ()) { // needed for ER proof support
578 fputs ("failed to check derived clause:\n", stderr);
579 for (const auto &lit : unsimplified)
580 fprintf (stderr, "%d ", lit);
581 fputc ('0', stderr);
583 } else
584 add_clause ("derived");
585 simplified.clear ();
586 unsimplified.clear ();
587 STOP (checking);
588}
589
590/*------------------------------------------------------------------------*/
591
592void Checker::delete_clause (int64_t id, bool, const vector<int> &c) {
593 if (inconsistent)
594 return;
595 START (checking);
596 LOG (c, "CHECKER checking deletion of clause");
597 stats.deleted++;
598 simplified.clear (); // Can be non-empty if clause allocation fails.
599 unsimplified.clear (); // Can be non-empty if clause allocation fails.
600 import_clause (c);
601 last_id = id;
602 if (!tautological ()) {
603 CheckerClause **p = find (), *d = *p;
604 if (d) {
605 CADICAL_assert (d->size > 1);
606 // Remove from hash table, mark as garbage, connect to garbage list.
607 num_garbage++;
608 CADICAL_assert (num_clauses);
609 num_clauses--;
610 *p = d->next;
611 d->next = garbage;
612 garbage = d;
613 d->size = 0;
614 } else {
616 fputs ("deleted clause not in proof:\n", stderr);
617 for (const auto &lit : unsimplified)
618 fprintf (stderr, "%d ", lit);
619 fputc ('0', stderr);
621 }
622 }
623 simplified.clear ();
624 unsimplified.clear ();
625 STOP (checking);
626}
627
629 const vector<int64_t> &chain) {
630 add_derived_clause (id, true, c, chain);
631 delete_clause (id, true, c);
632}
633
634/*------------------------------------------------------------------------*/
635
637 int max_var = 0;
638 for (uint64_t i = 0; i < size_clauses; i++)
639 for (CheckerClause *c = clauses[i]; c; c = c->next)
640 for (unsigned i = 0; i < c->size; i++)
641 if (abs (c->literals[i]) > max_var)
642 max_var = abs (c->literals[i]);
643 printf ("p cnf %d %" PRIu64 "\n", max_var, num_clauses);
644 for (uint64_t i = 0; i < size_clauses; i++)
645 for (CheckerClause *c = clauses[i]; c; c = c->next) {
646 for (unsigned i = 0; i < c->size; i++)
647 printf ("%d ", c->literals[i]);
648 printf ("0\n");
649 }
650}
651
652} // namespace CaDiCaL
653
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
void add_assumption_clause(int64_t, const vector< int > &, const vector< int64_t > &) override
void connect_internal(Internal *i) override
void add_original_clause(int64_t, bool, const vector< int > &, bool=false) override
void add_derived_clause(int64_t, bool, const vector< int > &, const vector< int64_t > &) override
Cube * p
Definition exorList.c:222
type
CUBE COVER and CUBE typedefs ///.
Definition exor.h:90
void fatal_message_start()
void fatal_message_end()
void clear_n(T *base, size_t n)
Definition util.hpp:149
vector< CheckerWatch > CheckerWatcher
Definition checker.hpp:50
void erase_vector(std::vector< T > &v)
Definition util.hpp:90
double percent(double a, double b)
Definition util.hpp:21
unsigned long long size
Definition giaNewBdd.h:39
#define false
Definition place_base.h:29
#define STOP(P)
Definition profile.hpp:158
#define START(P)
Definition profile.hpp:150
int lit
Definition satVec.h:130
CheckerClause * next
Definition checker.hpp:35
bool operator()(int a, int b) const
char * memcpy()
char * memset()
long random()
signed char mark
Definition value.h:8