ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_elimfast.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// Implements a variant of elimination with a much lower limit to be run as
12// preprocessing. See elim for comments
13
14/*------------------------------------------------------------------------*/
15
16// Flush garbage clause, check fast elimination limits and return number of
17// remaining occurrences (or 'fastelimbound + 1' if some limit was hit).
18
20 const int64_t occslim = opts.fastelimbound;
21 const int64_t clslim = opts.fastelimocclim;
22 const int64_t failed = occslim + 1;
23 Occs &os = occs (lit);
24 const const_occs_iterator end = os.end ();
25 occs_iterator j = os.begin (), i = j;
26 int64_t res = 0;
27 while (i != end) {
28 Clause *c = *i++;
29 if (c->collect ())
30 continue;
31 *j++ = c;
32 if (c->size > clslim) {
33 res = failed;
34 break;
35 }
36 if (++res > occslim) {
37 CADICAL_assert (opts.fastelimbound < 0 || res == failed);
38 break;
39 }
40 }
41 if (i != j) {
42 while (i != end)
43 *j++ = *i++;
44 os.resize (j - os.begin ());
45 shrink_occs (os);
46 }
47 return res;
48}
49
50/*------------------------------------------------------------------------*/
51
52// Check whether the number of non-tautological resolvents on 'pivot' is
53// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This
54// is the main criteria of bounded variable elimination. As a side effect
55// it flushes garbage clauses with that variable, sorts its occurrence lists
56// (smallest clauses first) and also negates pivot if it has more positive
57// than negative occurrences.
58
60 int pivot) {
61 CADICAL_assert (eliminator.gates.empty ());
62 CADICAL_assert (!eliminator.definition_unit);
63
64 stats.elimtried++;
65
67 CADICAL_assert (active (pivot));
68
69 const Occs &ps = occs (pivot);
70 const Occs &ns = occs (-pivot);
71
72 int64_t pos = ps.size ();
73 int64_t neg = ns.size ();
74
75 int64_t bound = opts.fastelimbound;
76
77 if (!pos || !neg)
78 return bound >= 0;
79
80 const int64_t sum = pos + neg;
81 const int64_t product = pos * neg;
82 if (bound > sum)
83 bound = sum;
84
85 LOG ("checking number resolvents on %d bounded by "
86 "%" PRId64 " = %" PRId64 " + %" PRId64 " + %d",
87 pivot, bound, pos, neg, opts.fastelimbound);
88
89 if (product <= bound) {
90 LOG ("fast elimination occurrence limits sufficiently small enough");
91 return true;
92 }
93
94 // Try all resolutions between a positive occurrence (outer loop) of
95 // 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the
96 // bound on non-tautological resolvents is not hit and the size of the
97 // generated resolvents does not exceed the resolvent clause size limit.
98
99 int64_t resolvents = 0; // Non-tautological resolvents.
100
101 for (const auto &c : ps) {
102 CADICAL_assert (!c->redundant);
103 if (c->garbage)
104 continue;
105 for (const auto &d : ns) {
106 CADICAL_assert (!d->redundant);
107 if (d->garbage)
108 continue;
109 if (resolve_clauses (eliminator, c, pivot, d, true)) {
110 resolvents++;
111 int size = clause.size ();
112 clause.clear ();
113 LOG ("now at least %" PRId64
114 " non-tautological resolvents on pivot %d",
115 resolvents, pivot);
116 if (size > opts.fastelimclslim) {
117 LOG ("resolvent size %d too big after %" PRId64
118 " resolvents on %d",
119 size, resolvents, pivot);
120 return false;
121 }
122 if (resolvents > bound) {
123 LOG ("too many non-tautological resolvents on %d", pivot);
124 return false;
125 }
126 } else if (unsat)
127 return false;
128 else if (val (pivot))
129 return false;
130 }
131 }
132
133 LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents",
134 resolvents, bound);
135
136 return true;
137}
138/*------------------------------------------------------------------------*/
139
140/*------------------------------------------------------------------------*/
141// Add all resolvents on 'pivot' and connect them.
142
144 int pivot) {
145
146 CADICAL_assert (eliminator.gates.empty ());
147 CADICAL_assert (!eliminator.definition_unit);
148
149 LOG ("adding all resolvents on %d", pivot);
150
151 CADICAL_assert (!val (pivot));
152 CADICAL_assert (!flags (pivot).eliminated ());
153
154 const Occs &ps = occs (pivot);
155 const Occs &ns = occs (-pivot);
156#ifdef LOGGING
157 int64_t resolvents = 0;
158#endif
159 for (auto &c : ps) {
160 if (unsat)
161 break;
162 if (c->garbage)
163 continue;
164 for (auto &d : ns) {
165 if (unsat)
166 break;
167 if (d->garbage)
168 continue;
169 if (!resolve_clauses (eliminator, c, pivot, d, false))
170 continue;
171 CADICAL_assert (!lrat || !lrat_chain.empty ());
173 elim_update_added_clause (eliminator, r);
174 eliminator.enqueue (r);
175 lrat_chain.clear ();
176 clause.clear ();
177#ifdef LOGGING
178 resolvents++;
179#endif
180 }
181 }
182
183 LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot);
184}
185
186/*------------------------------------------------------------------------*/
187
188// Try to eliminate 'pivot' by bounded variable elimination.
190 int pivot,
191 bool &deleted_binary_clause) {
192
193 if (!active (pivot))
194 return;
195 CADICAL_assert (!frozen (pivot));
196
197 // First flush garbage clauses and check limits.
198
199 int64_t bound = opts.fastelimbound;
200
201 int64_t pos = flush_elimfast_occs (pivot);
202 if (pos > bound) {
203 LOG ("too many occurrences thus not eliminated %d", pivot);
204 CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
205 return;
206 }
207
208 int64_t neg = flush_elimfast_occs (-pivot);
209 if (neg > bound) {
210 LOG ("too many occurrences thus not eliminated %d", -pivot);
211 CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
212 return;
213 }
214
215 const int64_t product = pos * neg;
216 const int64_t sum = pos + neg;
217 if (bound > sum)
218 bound = sum;
219
220 if (pos > neg) {
221 pivot = -pivot;
222 swap (pos, neg);
223 }
224
225 LOG ("pivot %d occurs positively %" PRId64
226 " times and negatively %" PRId64 " times",
227 pivot, pos, neg);
228
229 CADICAL_assert (!eliminator.schedule.contains (abs (pivot)));
230 CADICAL_assert (pos <= neg);
231
232 LOG ("trying to eliminate %d", pivot);
233 CADICAL_assert (!flags (pivot).eliminated ());
234
235 // Sort occurrence lists, such that shorter clauses come first.
236 Occs &ps = occs (pivot);
237 stable_sort (ps.begin (), ps.end (), clause_smaller_size ());
238 Occs &ns = occs (-pivot);
239 stable_sort (ns.begin (), ns.end (), clause_smaller_size ());
240
241 if (!unsat && !val (pivot)) {
242 if (product <= bound ||
243 elimfast_resolvents_are_bounded (eliminator, pivot)) {
244 LOG ("number of resolvents on %d are bounded", pivot);
245 elimfast_add_resolvents (eliminator, pivot);
246 if (!unsat)
247 mark_eliminated_clauses_as_garbage (eliminator, pivot,
248 deleted_binary_clause);
249 if (active (pivot))
250 mark_eliminated (pivot);
251 } else {
252 LOG ("too many resolvents on %d so not eliminated", pivot);
253 }
254 }
255
256 unmark_gate_clauses (eliminator);
257 elim_backward_clauses (eliminator);
258}
259
260/*------------------------------------------------------------------------*/
261
262// This function performs one round of bounded variable elimination and
263// returns the number of eliminated variables. The additional result
264// 'completed' is true if this elimination round ran to completion (all
265// variables have been tried). Otherwise it was asynchronously terminated
266// or the resolution limit was hit.
267
268int Internal::elimfast_round (bool &completed,
269 bool &deleted_binary_clause) {
270
271 CADICAL_assert (opts.fastelim);
273
274 START_SIMPLIFIER (fastelim, ELIM);
275
276 stats.elimfastrounds++;
277
279
280 int64_t resolution_limit;
281
282 if (opts.elimlimited) {
283 int64_t delta = stats.propagations.search;
284 delta *= 1e-3 * opts.elimeffort;
285 if (delta < opts.elimmineff)
286 delta = opts.elimmineff;
287 if (delta > opts.elimmaxeff)
288 delta = opts.elimmaxeff;
289 delta = max (delta, (int64_t) 2l * active ());
290
291 PHASE ("fastelim-round", stats.elimfastrounds,
292 "limit of %" PRId64 " resolutions", delta);
293
294 resolution_limit = stats.elimres + delta;
295 } else {
296 PHASE ("fastelim-round", stats.elimfastrounds, "resolutions unlimited");
297 resolution_limit = LONG_MAX;
298 }
299
300 init_noccs ();
301
302 // First compute the number of occurrences of each literal and at the same
303 // time mark satisfied clauses and update 'elim' flags of variables in
304 // clauses with root level assigned literals (both false and true).
305 //
306 for (const auto &c : clauses) {
307 if (c->garbage || c->redundant)
308 continue;
309 bool satisfied = false, falsified = false;
310 for (const auto &lit : *c) {
311 const signed char tmp = val (lit);
312 if (tmp > 0)
313 satisfied = true;
314 else if (tmp < 0)
315 falsified = true;
316 else
318 }
319 if (satisfied)
320 mark_garbage (c); // forces more precise counts
321 else {
322 for (const auto &lit : *c) {
323 if (!active (lit))
324 continue;
325 if (falsified)
326 mark_elim (lit); // simulate unit propagation
327 noccs (lit)++;
328 }
329 }
330 }
331
332 init_occs ();
333
334 Eliminator eliminator (this);
335 ElimSchedule &schedule = eliminator.schedule;
336 CADICAL_assert (schedule.empty ());
337
338 // Now find elimination candidates which occurred in clauses removed since
339 // the last time we ran bounded variable elimination, which in turned
340 // triggered their 'elim' bit to be set.
341 //
342 for (auto idx : vars) {
343 if (!active (idx))
344 continue;
345 if (frozen (idx))
346 continue;
347 if (!flags (idx).elim)
348 continue;
349 LOG ("scheduling %d for elimination initially", idx);
350 schedule.push_back (idx);
351 }
352
353 schedule.shrink ();
354
355#ifndef CADICAL_QUIET
356 int64_t scheduled = schedule.size ();
357#endif
358
359 PHASE ("fastelim-round", stats.elimfastrounds,
360 "scheduled %" PRId64 " variables %.0f%% for elimination",
361 scheduled, percent (scheduled, active ()));
362
363 // Connect irredundant clauses.
364 //
365 for (const auto &c : clauses)
366 if (!c->garbage && !c->redundant)
367 for (const auto &lit : *c)
368 if (active (lit))
369 occs (lit).push_back (c);
370
371#ifndef CADICAL_QUIET
372 const int64_t old_resolutions = stats.elimres;
373#endif
374 const int old_eliminated = stats.all.eliminated;
375 const int old_fixed = stats.all.fixed;
376
377 // Limit on garbage literals during variable elimination. If the limit is
378 // hit a garbage collection is performed.
379 //
380 const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20);
381
382 // Main loops tries to eliminate variables according to the schedule. The
383 // schedule is updated dynamically and variables are potentially
384 // rescheduled to be tried again if they occur in a removed clause.
385 //
386#ifndef CADICAL_QUIET
387 int64_t tried = 0;
388#endif
389 while (!unsat && !terminated_asynchronously () &&
390 stats.elimres <= resolution_limit && !schedule.empty ()) {
391 int idx = schedule.front ();
392 schedule.pop_front ();
393 flags (idx).elim = false;
394 try_to_fasteliminate_variable (eliminator, idx, deleted_binary_clause);
395#ifndef CADICAL_QUIET
396 tried++;
397#endif
398 if (stats.garbage.literals <= garbage_limit)
399 continue;
402 }
403
404 // If the schedule is empty all variables have been tried (even
405 // rescheduled ones). Otherwise asynchronous termination happened or we
406 // ran into the resolution limit (or derived unsatisfiability).
407 //
408 completed = !schedule.size ();
409
410 PHASE ("fastelim-round", stats.elimfastrounds,
411 "tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)",
412 tried, percent (tried, scheduled), schedule.size ());
413
414 schedule.erase ();
415
416 reset_occs ();
417 reset_noccs ();
418
419 // Mark all redundant clauses with eliminated variables as garbage.
420 //
421 if (!unsat)
423
424 int eliminated = stats.all.eliminated - old_eliminated;
425 stats.all.fasteliminated += eliminated;
426#ifndef CADICAL_QUIET
427 int64_t resolutions = stats.elimres - old_resolutions;
428 PHASE ("fastelim-round", stats.elimfastrounds,
429 "eliminated %d variables %.0f%% in %" PRId64 " resolutions",
430 eliminated, percent (eliminated, scheduled), resolutions);
431#endif
432
433 const int units = stats.all.fixed - old_fixed;
434 report ('e', !opts.reportall && !(eliminated + units));
435 STOP_SIMPLIFIER (fastelim, ELIM);
436
437 return eliminated; // non-zero if successful
438}
439
440/*------------------------------------------------------------------------*/
441
443
444 if (unsat)
445 return;
446 if (level)
447 backtrack ();
448 if (!propagate ()) {
450 return;
451 }
452
453 stats.elimfastphases++;
454 PHASE ("fastelim-phase", stats.elimfastphases,
455 "starting at most %d elimination rounds", opts.fastelimrounds);
456
457 if (external_prop) {
459 private_steps = true;
460 }
461
462#ifndef CADICAL_QUIET
463 int old_active_variables = active ();
464 int old_eliminated = stats.all.eliminated;
465#endif
466
467 reset_watches (); // saves lots of memory
468
469 // Alternate one round of bounded variable elimination ('elim_round') and
470 // subsumption ('subsume_round'), blocked ('block') and covered clause
471 // elimination ('cover') until nothing changes, or the round limit is hit.
472 // The loop also aborts early if no variable could be eliminated, the
473 // empty clause is resolved, it is asynchronously terminated or a
474 // resolution limit is hit.
475
476 // This variable determines whether the whole loop of this bounded
477 // variable elimination phase ('elim') ran until completion. This
478 // potentially triggers an incremental increase of the elimination bound.
479 //
480 bool phase_complete = false, deleted_binary_clause = false;
481
482 int round = 1;
483#ifndef CADICAL_QUIET
484 int eliminated = 0;
485#endif
486
487 bool round_complete = false;
488 while (!unsat && !phase_complete && !terminated_asynchronously ()) {
489#ifndef CADICAL_QUIET
490 int eliminated =
491#endif
492 elimfast_round (round_complete, deleted_binary_clause);
493
494 if (!round_complete) {
495 PHASE ("fastelim-phase", stats.elimphases,
496 "last round %d incomplete %s", round,
497 eliminated ? "but successful" : "and unsuccessful");
498 CADICAL_assert (!phase_complete);
499 break;
500 }
501
502 if (round++ >= opts.fastelimrounds) {
503 PHASE ("fastelim-phase", stats.elimphases, "round limit %d hit (%s)",
504 round - 1,
505 eliminated ? "though last round successful"
506 : "last round unsuccessful anyhow");
507 CADICAL_assert (!phase_complete);
508 break;
509 }
510
511 // Prioritize 'subsumption' over blocked and covered clause elimination.
512
513 if (subsume_round ())
514 continue;
515
516 // Was not able to generate new variable elimination candidates after
517 // variable elimination round, neither through subsumption, nor blocked,
518 // nor covered clause elimination.
519 //
520 PHASE ("fastelim-phase", stats.elimphases,
521 "no new variable elimination candidates");
522
523 CADICAL_assert (round_complete);
524 phase_complete = true;
525 }
526
527 for (auto idx : vars) {
528 if (active (idx))
529 flags (idx).elim = true;
530 }
531
532 if (phase_complete) {
533 stats.elimcompleted++;
534 PHASE ("fastelim-phase", stats.elimphases,
535 "fully completed elimination %" PRId64
536 " at elimination bound %" PRId64 "",
537 stats.elimcompleted, lim.elimbound);
538 } else {
539 PHASE ("fastelim-phase", stats.elimphases,
540 "incomplete elimination %" PRId64
541 " at elimination bound %" PRId64 "",
542 stats.elimcompleted + 1, lim.elimbound);
543 }
544
545 if (deleted_binary_clause)
547 init_watches ();
549
550 if (unsat)
551 LOG ("elimination derived empty clause");
552 else if (propagated < trail.size ()) {
553 LOG ("elimination produced %zd units",
554 (size_t) (trail.size () - propagated));
555 if (!propagate ()) {
556 LOG ("propagating units after elimination results in empty clause");
558 }
559 }
560
561#ifndef CADICAL_QUIET
562 eliminated = stats.all.eliminated - old_eliminated;
563 PHASE ("fastelim-phase", stats.elimphases,
564 "eliminated %d variables %.2f%%", eliminated,
565 percent (eliminated, old_active_variables));
566#endif
567
568 if (external_prop) {
570 private_steps = false;
571 }
572}
573
574} // namespace CaDiCaL
575
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
void erase()
Definition heap.hpp:194
bool empty() const
Definition heap.hpp:135
void shrink()
Definition heap.hpp:199
bool contains(unsigned e) const
Definition heap.hpp:139
size_t size() const
Definition heap.hpp:131
void push_back(unsigned e)
Definition heap.hpp:147
unsigned pop_front()
Definition heap.hpp:167
unsigned front() const
Definition heap.hpp:160
bool pos
Definition globals.c:30
#define PHASE(...)
Definition message.hpp:52
heap< elim_more > ElimSchedule
Definition elim.hpp:20
vector< Clause * > Occs
Definition occs.hpp:18
Occs::iterator occs_iterator
Definition occs.hpp:35
void shrink_occs(Occs &os)
Definition occs.hpp:20
const char * flags()
Occs::const_iterator const_occs_iterator
Definition occs.hpp:36
double percent(double a, double b)
Definition util.hpp:21
#define START_SIMPLIFIER(S, M)
Definition profile.hpp:172
#define STOP_SIMPLIFIER(S, M)
Definition profile.hpp:197
int lit
Definition satVec.h:130
bool collect() const
Definition clause.hpp:166
unsigned definition_unit
Definition elim.hpp:49
ElimSchedule schedule
Definition elim.hpp:36
vector< Clause * > gates
Definition elim.hpp:48
void elimfast_add_resolvents(Eliminator &, int pivot)
void try_to_fasteliminate_variable(Eliminator &, int pivot, bool &)
void elim_update_added_clause(Eliminator &, Clause *)
void mark_garbage(Clause *)
vector< int64_t > lrat_chain
Definition internal.hpp:210
void report(char type, int verbose_level=0)
void mark_redundant_clauses_with_eliminated_variables_as_garbage()
Flags & flags(int lit)
Definition internal.hpp:454
vector< int > trail
Definition internal.hpp:259
bool terminated_asynchronously(int factor=1)
bool frozen(int lit)
void mark_eliminated(int)
bool resolve_clauses(Eliminator &, Clause *, int pivot, Clause *, bool)
void elim(bool update_limits=true)
signed char val(int lit) const
void unmark_gate_clauses(Eliminator &)
int64_t flush_elimfast_occs(int lit)
bool elimfast_resolvents_are_bounded(Eliminator &, int pivot)
int elimfast_round(bool &completed, bool &)
bool active(int lit)
Definition internal.hpp:360
void backtrack(int target_level=0)
int64_t & noccs(int lit)
Definition internal.hpp:466
void elim_backward_clauses(Eliminator &)
void mark_elim(int lit)
vector< Clause * > clauses
Definition internal.hpp:283
void mark_eliminated_clauses_as_garbage(Eliminator &, int pivot, bool &)
Clause * new_resolved_irredundant_clause()
const Range vars
Definition internal.hpp:324
Occs & occs(int lit)
Definition internal.hpp:465
int active() const
Definition internal.hpp:362
vector< int > clause
Definition internal.hpp:260
void connect_watches(bool irredundant_only=false)
unsigned size
Definition vector.h:35