ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_collect.cpp
Go to the documentation of this file.
1#include "global.h"
2
3#include "internal.hpp"
4
6
7namespace CaDiCaL {
8
9/*------------------------------------------------------------------------*/
10
11// Returns the positive number '1' ( > 0) if the given clause is root level
12// satisfied or the negative number '-1' ( < 0) if it is not root level
13// satisfied but contains a root level falsified literal. Otherwise, if it
14// contains neither a satisfied nor falsified literal, then '0' is returned.
15
17 int satisfied = 0, falsified = 0;
18 for (const auto &lit : *c) {
19 const int tmp = fixed (lit);
20 if (tmp > 0) {
21 LOG (c, "root level satisfied literal %d in", lit);
22 satisfied++;
23 }
24 if (tmp < 0) {
25 LOG (c, "root level falsified literal %d in", lit);
26 falsified++;
27 }
28 }
29 if (satisfied)
30 return 1;
31 else if (falsified)
32 return -1;
33 else
34 return 0;
35}
36
37// Assume that the clause is not root level satisfied but contains a literal
38// set to false (root level falsified literal), so it can be shrunken. The
39// clause data is not actually reallocated at this point to avoid dealing
40// with issues of special policies for watching binary clauses or whether a
41// clause is extended or not. Only its size field is adjusted accordingly
42// after flushing out root level falsified literals.
43
45 const const_literal_iterator end = c->end ();
47 int num_non_false = 0;
48 for (i = c->begin (); num_non_false < 2 && i != end; i++)
49 if (fixed (*i) >= 0)
50 num_non_false++;
51 if (num_non_false < 2)
52 return;
53 if (proof) {
54 // Flush changes the clause id, external forgettables need to be
55 // marked here (or the new id could be used instead of old one)
56 if (opts.check && is_external_forgettable (c->id))
58 proof->flush_clause (c);
59 }
60 literal_iterator j = c->begin ();
61 for (i = j; i != end; i++) {
62 const int lit = *j++ = *i, tmp = fixed (lit);
63 CADICAL_assert (tmp <= 0);
64 if (tmp >= 0)
65 continue;
66 LOG ("flushing %d", lit);
67 j--;
68 }
69 stats.collected += shrink_clause (c, j - c->begin ());
70}
71
72// If there are new units (fixed variables) since the last garbage
73// collection we go over all clauses, mark satisfied ones as garbage and
74// flush falsified literals. Otherwise if no new units have been generated
75// since the last garbage collection just skip this step.
76
78
79 if (last.collect.fixed >= stats.all.fixed)
80 return;
81 last.collect.fixed = stats.all.fixed;
82
83 LOG ("marking satisfied clauses and removing falsified literals");
84
85 for (const auto &c : clauses) {
86 if (c->garbage)
87 continue;
88 const int tmp = clause_contains_fixed_literal (c);
89 if (tmp > 0)
90 mark_garbage (c);
91 else if (tmp < 0)
93 }
94}
95
96/*------------------------------------------------------------------------*/
97
98// Reason clauses can not be collected.
99//
100// We protect reasons before and release protection after garbage collection
101// (actually within garbage collection).
102//
103// For 'reduce' we still need to make sure that all clauses which should not
104// be removed are marked as such and thus we need to call it before marking
105// clauses to be flushed.
106
108 LOG ("protecting reason clauses of all assigned variables on trail");
110#ifdef LOGGING
111 size_t count = 0;
112#endif
113 for (const auto &lit : trail) {
114 if (!active (lit))
115 continue;
117 Var &v = var (lit);
118 CADICAL_assert (v.level > 0);
119 Clause *reason = v.reason;
120 if (!reason)
121 continue;
122 if (reason == external_reason)
123 continue;
124 LOG (reason, "protecting assigned %d reason %p", lit, (void *) reason);
125 CADICAL_assert (!reason->reason);
126 reason->reason = true;
127#ifdef LOGGING
128 count++;
129#endif
130 }
131 LOG ("protected %zd reason clauses referenced on trail", count);
132 protected_reasons = true;
133}
134
135/*------------------------------------------------------------------------*/
136
137// After garbage collection we reset the 'reason' flag of the reasons
138// of assigned literals on the trail.
139
141 LOG ("unprotecting reasons clauses of all assigned variables on trail");
143#ifdef LOGGING
144 size_t count = 0;
145#endif
146 for (const auto &lit : trail) {
147 if (!active (lit))
148 continue;
150 Var &v = var (lit);
151 CADICAL_assert (v.level > 0);
152 Clause *reason = v.reason;
153 if (!reason)
154 continue;
155 if (reason == external_reason)
156 continue;
157 LOG (reason, "unprotecting assigned %d reason %p", lit,
158 (void *) reason);
159 CADICAL_assert (reason->reason);
160 reason->reason = false;
161#ifdef LOGGING
162 count++;
163#endif
164 }
165 LOG ("unprotected %zd reason clauses referenced on trail", count);
166 protected_reasons = false;
167}
168
169/*------------------------------------------------------------------------*/
170
171// Update occurrence lists before deleting garbage clauses in the context of
172// preprocessing, e.g., during bounded variable elimination 'elim'. The
173// result is the number of remaining clauses, which in this context means
174// the number of non-garbage clauses.
175
177 Occs &os = occs (lit);
178 const const_occs_iterator end = os.end ();
179 occs_iterator j = os.begin ();
181 size_t res = 0;
182 Clause *c;
183 for (i = j; i != end; i++) {
184 c = *i;
185 if (c->collect ())
186 continue;
187 *j++ = c->moved ? c->copy : c;
188 // CADICAL_assert (!c->redundant); // -> not true in sweeping
189 res++;
190 }
191 os.resize (j - os.begin ());
192 shrink_occs (os);
193 return res;
194}
195
196// Update watch lists before deleting garbage clauses in the context of
197// 'reduce' where we watch and no occurrence lists. We have to protect
198// reason clauses not be collected and thus we have this additional check
199// hidden in 'Clause.collect', which for the root level context of
200// preprocessing is actually redundant.
201
202inline void Internal::flush_watches (int lit, Watches &saved) {
203 CADICAL_assert (saved.empty ());
204 Watches &ws = watches (lit);
205 const const_watch_iterator end = ws.end ();
206 watch_iterator j = ws.begin ();
208 for (i = j; i != end; i++) {
209 Watch w = *i;
210 Clause *c = w.clause;
211 if (c->collect ())
212 continue;
213 if (c->moved)
214 c = w.clause = c->copy;
215 w.size = c->size;
216 const int new_blit_pos = (c->literals[0] == lit);
217 LOG (c, "clause in flush_watch starting from %d", lit);
218 CADICAL_assert (c->literals[!new_blit_pos] == lit); /*FW1*/
219 w.blit = c->literals[new_blit_pos];
220 if (w.binary ())
221 *j++ = w;
222 else
223 saved.push_back (w);
224 }
225 ws.resize (j - ws.begin ());
226 for (const auto &w : saved)
227 ws.push_back (w);
228 saved.clear ();
229 shrink_vector (ws);
230}
231
233 if (occurring ())
234 for (auto idx : vars)
235 flush_occs (idx), flush_occs (-idx);
236
237 if (watching ()) {
238 Watches tmp;
239 for (auto idx : vars)
240 flush_watches (idx, tmp), flush_watches (-idx, tmp);
241 }
242}
243
244/*------------------------------------------------------------------------*/
245
247 LOG ("update assigned reason references");
248#ifdef LOGGING
249 size_t count = 0;
250#endif
251 for (auto &lit : trail) {
252 if (!active (lit))
253 continue;
254 Var &v = var (lit);
255 Clause *c = v.reason;
256 if (!c)
257 continue;
258 if (c == external_reason)
259 continue;
260 LOG (c, "updating assigned %d reason", lit);
263 Clause *d = c->copy;
264 v.reason = d;
265#ifdef LOGGING
266 count++;
267#endif
268 }
269 LOG ("updated %zd assigned reason references", count);
270}
271
272/*------------------------------------------------------------------------*/
273
274// This is a simple garbage collector which does not move clauses. It needs
275// less space than the arena based clause allocator, but is not as cache
276// efficient, since the copying garbage collector can put clauses together
277// which are likely accessed after each other.
278
280
282
283 LOG ("deleting garbage clauses");
284#ifndef CADICAL_QUIET
285 int64_t collected_bytes = 0, collected_clauses = 0;
286#endif
287 const auto end = clauses.end ();
288 auto j = clauses.begin (), i = j;
289 while (i != end) {
290 Clause *c = *j++ = *i++;
291 if (!c->collect ())
292 continue;
293#ifndef CADICAL_QUIET
294 collected_bytes += c->bytes ();
295 collected_clauses++;
296#endif
297 delete_clause (c);
298 j--;
299 }
300 clauses.resize (j - clauses.begin ());
302
303 PHASE ("collect", stats.collections,
304 "collected %" PRId64 " bytes of %" PRId64 " garbage clauses",
305 collected_bytes, collected_clauses);
306}
307
308/*------------------------------------------------------------------------*/
309
310// This is the start of the copying garbage collector using the arena. At
311// the core is the following function, which copies a clause to the 'to'
312// space of the arena. Be careful if this clause is a reason of an
313// assignment. In that case update the reason reference.
314//
316 LOG (c, "moving");
317 CADICAL_assert (!c->moved);
318 char *p = (char *) c;
319 char *q = arena.copy (p, c->bytes ());
320 c->copy = (Clause *) q;
321 c->moved = true;
322 LOG ("copied clause[%" PRId64 "] from %p to %p", c->id, (void *) c,
323 (void *) c->copy);
324}
325
326// This is the moving garbage collector.
327
329
330 size_t collected_clauses = 0, collected_bytes = 0;
331 size_t moved_clauses = 0, moved_bytes = 0;
332
333 // First determine 'moved_bytes' and 'collected_bytes'.
334 //
335 for (const auto &c : clauses)
336 if (!c->collect ())
337 moved_bytes += c->bytes (), moved_clauses++;
338 else
339 collected_bytes += c->bytes (), collected_clauses++;
340
341 PHASE ("collect", stats.collections,
342 "moving %zd bytes %.0f%% of %zd non garbage clauses", moved_bytes,
343 percent (moved_bytes, collected_bytes + moved_bytes),
344 moved_clauses);
345 (void) moved_clauses, (void) collected_clauses, (void) collected_bytes;
346 // Prepare 'to' space of size 'moved_bytes'.
347 //
348 arena.prepare (moved_bytes);
349
350 // Keep clauses in arena in the same order.
351 //
352 if (opts.arenacompact)
353 for (const auto &c : clauses)
354 if (!c->collect () && arena.contains (c))
355 copy_clause (c);
356
357 if (opts.arenatype == 1 || !watching ()) {
358
359 // Localize according to current clause order.
360
361 // If the option 'opts.arenatype == 1' is set, then this means the
362 // solver uses the original order of clauses. If there are no watches,
363 // we can not use the watched based copying policies below. This
364 // happens if garbage collection is triggered during bounded variable
365 // elimination.
366
367 // Copy clauses according to the order of calling 'copy_clause', which
368 // in essence just gives a compacting garbage collector, since their
369 // relative order is kept, and actually already gives the largest
370 // benefit due to better cache locality.
371
372 for (const auto &c : clauses)
373 if (!c->moved && !c->collect ())
374 copy_clause (c);
375
376 } else if (opts.arenatype == 2) {
377
378 // Localize according to (original) variable order.
379
380 // This is almost the version used by MiniSAT and descendants.
381 // Our version uses saved phases too.
382
383 for (int sign = -1; sign <= 1; sign += 2)
384 for (auto idx : vars)
385 for (const auto &w : watches (sign * likely_phase (idx)))
386 if (!w.clause->moved && !w.clause->collect ())
387 copy_clause (w.clause);
388
389 } else {
390
391 // Localize according to decision queue order.
392
393 // This is the default for search. It allocates clauses in the order of
394 // the decision queue and also uses saved phases. It seems faster than
395 // the MiniSAT version and thus we keep 'opts.arenatype == 3'.
396
397 CADICAL_assert (opts.arenatype == 3);
398
399 for (int sign = -1; sign <= 1; sign += 2)
400 for (int idx = queue.last; idx; idx = link (idx).prev)
401 for (const auto &w : watches (sign * likely_phase (idx)))
402 if (!w.clause->moved && !w.clause->collect ())
403 copy_clause (w.clause);
404 }
405
406 // Do not forget to move clauses which are not watched, which happened in
407 // a rare situation, and now is only left as defensive code.
408 //
409 for (const auto &c : clauses)
410 if (!c->collect () && !c->moved)
411 copy_clause (c);
412
415
416 // Replace and flush clause references in 'clauses'.
417 //
418 const auto end = clauses.end ();
419 auto j = clauses.begin (), i = j;
420 for (; i != end; i++) {
421 Clause *c = *i;
422 if (c->collect ())
423 delete_clause (c);
424 else
425 CADICAL_assert (c->moved), *j++ = c->copy, deallocate_clause (c);
426 }
427 clauses.resize (j - clauses.begin ());
428 if (clauses.size () < clauses.capacity () / 2)
430
431 if (opts.arenasort)
432 rsort (clauses.begin (), clauses.end (), pointer_rank ());
433
434 // Release 'from' space completely and then swap 'to' with 'from'.
435 //
436 arena.swap ();
437
438 PHASE ("collect", stats.collections,
439 "collected %zd bytes %.0f%% of %zd garbage clauses",
440 collected_bytes,
441 percent (collected_bytes, collected_bytes + moved_bytes),
442 collected_clauses);
443}
444
445/*------------------------------------------------------------------------*/
446
447// Maintaining clause statistics is complex and error prone but necessary
448// for proper scheduling of garbage collection, particularly during bounded
449// variable elimination. With this function we can check whether these
450// statistics are updated correctly.
451
453#ifndef CADICAL_NDEBUG
454 int64_t irredundant = 0, redundant = 0, total = 0, irrlits = 0;
455 for (const auto &c : clauses) {
456 if (c->garbage)
457 continue;
458 if (c->redundant)
459 redundant++;
460 else
461 irredundant++;
462 if (!c->redundant)
463 irrlits += c->size;
464 total++;
465 }
466 CADICAL_assert (stats.current.irredundant == irredundant);
467 CADICAL_assert (stats.current.redundant == redundant);
468 CADICAL_assert (stats.current.total == total);
469 CADICAL_assert (stats.irrlits == irrlits);
470#endif
471}
472
473/*------------------------------------------------------------------------*/
474
475// only delete binary clauses from watch list that are already mark as
476// deleted.
478 if (unsat)
479 return;
480 START (collect);
481
484 int backtrack_level = level + 1;
485 Watches saved;
486 for (auto v : vars) {
487 for (auto lit : {-v, v}) {
488 CADICAL_assert (saved.empty ());
489 Watches &ws = watches (lit);
490 const const_watch_iterator end = ws.end ();
491 watch_iterator j = ws.begin ();
493 for (i = j; i != end; i++) {
494 Watch w = *i;
495 *j++ = w;
496 Clause *c = w.clause;
497 COVER (!w.binary () && c->size == 2);
498 if (!w.binary ())
499 continue;
500 if (c->reason && c->garbage) {
501 COVER (true);
502 CADICAL_assert (c->size == 2);
503 backtrack_level =
504 min (backtrack_level, var (c->literals[0]).level);
505 LOG ("need to backtrack to before level %d", backtrack_level);
506 --j;
507 continue;
508 }
509 if (!c->collect ())
510 continue;
511 LOG (c, "removing from watch list");
512 --j;
513 }
514 ws.resize (j - ws.begin ());
515 shrink_vector (ws);
516 }
517 }
520 if (backtrack_level - 1 < level)
521 backtrack (backtrack_level - 1);
522 STOP (collect);
523}
524
525/*------------------------------------------------------------------------*/
526
527bool Internal::arenaing () { return opts.arena && (stats.collections > 1); }
528
530 if (unsat)
531 return;
532 START (collect);
533 report ('G', 1);
534 stats.collections++;
538 if (arenaing ())
540 else
545 report ('C', 1);
546 STOP (collect);
547}
548
549} // namespace CaDiCaL
550
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
Cube * p
Definition exorList.c:222
#define PHASE(...)
Definition message.hpp:52
int sign(int lit)
Definition util.hpp:22
void shrink_vector(std::vector< T > &v)
Definition util.hpp:101
Watches::const_iterator const_watch_iterator
Definition watch.hpp:48
vector< Clause * > Occs
Definition occs.hpp:18
int * literal_iterator
Definition clause.hpp:17
Occs::iterator occs_iterator
Definition occs.hpp:35
void shrink_occs(Occs &os)
Definition occs.hpp:20
Occs::const_iterator const_occs_iterator
Definition occs.hpp:36
const int * const_literal_iterator
Definition clause.hpp:18
vector< Watch > Watches
Definition watch.hpp:45
double percent(double a, double b)
Definition util.hpp:21
void rsort(I first, I last, Rank rank)
Definition radix.hpp:45
Watches::iterator watch_iterator
Definition watch.hpp:47
#define STOP(P)
Definition profile.hpp:158
#define START(P)
Definition profile.hpp:150
int lit
Definition satVec.h:130
literal_iterator begin()
Definition clause.hpp:131
static size_t bytes(int size)
Definition clause.hpp:137
literal_iterator end()
Definition clause.hpp:132
Clause * copy
Definition clause.hpp:37
bool collect() const
Definition clause.hpp:166
int64_t irredundant() const
Definition internal.hpp:383
void copy_clause(Clause *)
Var & var(int lit)
Definition internal.hpp:452
void mark_garbage(Clause *)
void flush_watches(int lit, Watches &)
void report(char type, int verbose_level=0)
vector< int > trail
Definition internal.hpp:259
int fixed(int lit)
void mark_garbage_external_forgettable(int64_t id)
Link & link(int lit)
Definition internal.hpp:453
void remove_falsified_literals(Clause *)
bool watching() const
Definition internal.hpp:462
signed char val(int lit) const
int clause_contains_fixed_literal(Clause *)
int likely_phase(int idx)
void deallocate_clause(Clause *)
bool active(int lit)
Definition internal.hpp:360
void backtrack(int target_level=0)
vector< Clause * > clauses
Definition internal.hpp:283
const Range vars
Definition internal.hpp:324
Clause * external_reason
Definition internal.hpp:245
Occs & occs(int lit)
Definition internal.hpp:465
bool occurring() const
Definition internal.hpp:461
void mark_satisfied_clauses_as_garbage()
size_t shrink_clause(Clause *, int new_size)
Watches & watches(int lit)
Definition internal.hpp:467
void delete_clause(Clause *)
int64_t redundant() const
Definition internal.hpp:381
size_t flush_occs(int lit)
int level
Definition var.hpp:19
Clause * reason
Definition var.hpp:21
bool binary() const
Definition watch.hpp:42
Clause * clause
Definition watch.hpp:35
vector watches
Definition watch.h:49