ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_walk.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// Random walk local search based on 'ProbSAT' ideas.
12
13struct Walker {
14
16
17 Random random; // local random number generator
18 int64_t propagations; // number of propagations
19 int64_t limit; // limit on number of propagations
20 vector<Clause *> broken; // currently unsatisfied clauses
21 double epsilon; // smallest considered score
22 vector<double> table; // break value to score table
23 vector<double> scores; // scores of candidate literals
24
25 double score (unsigned); // compute score from break count
26
27 Walker (Internal *, double size, int64_t limit);
28};
29
30// These are in essence the CB values from Adrian Balint's thesis. They
31// denote the inverse 'cb' of the base 'b' of the (probability) weight
32// 'b^-i' for picking a literal with the break value 'i' (first column is
33// the 'size', second the 'CB' value).
34
35static double cbvals[][2] = {
36 {0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70},
37 {6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better.
38};
39
40static const int ncbvals = sizeof cbvals / sizeof cbvals[0];
41
42// We interpolate the CB values for uniform random SAT formula to the non
43// integer situation of average clause size by piecewise linear functions.
44//
45// y2 - y1
46// ------- * (x - x1) + y1
47// x2 - x1
48//
49// where 'x' is the average size of clauses and 'y' the CB value.
50
51inline static double fitcbval (double size) {
52 int i = 0;
53 while (i + 2 < ncbvals &&
54 (cbvals[i][0] > size || cbvals[i + 1][0] < size))
55 i++;
56 const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0];
57 const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1];
58 const double dx = x2 - x1, dy = y2 - y1;
59 CADICAL_assert (dx);
60 const double res = dy * (size - x1) / dx + y1;
61 CADICAL_assert (res > 0);
62 return res;
63}
64
65// Initialize the data structures for one local search round.
66
67Walker::Walker (Internal *i, double size, int64_t l)
68 : internal (i), random (internal->opts.seed), // global random seed
69 propagations (0), limit (l) {
70 random += internal->stats.walk.count; // different seed every time
71
72 // This is the magic constant in ProbSAT (also called 'CB'), which we pick
73 // according to the average size every second invocation and otherwise
74 // just the default '2.0', which turns into the base '0.5'.
75 //
76 const bool use_size_based_cb = (internal->stats.walk.count & 1);
77 const double cb = use_size_based_cb ? fitcbval (size) : 2.0;
78 CADICAL_assert (cb);
79 const double base = 1 / cb; // scores are 'base^0,base^1,base^2,...
80
81 double next = 1;
82 for (epsilon = next; next; next = epsilon * base)
83 table.push_back (epsilon = next);
84
85 PHASE ("walk", internal->stats.walk.count,
86 "CB %.2f with inverse %.2f as base and table size %zd", cb, base,
87 table.size ());
88}
89
90// The scores are tabulated for faster computation (to avoid 'pow').
91
92inline double Walker::score (unsigned i) {
93 const double res = (i < table.size () ? table[i] : epsilon);
94 LOG ("break %u mapped to score %g", i, res);
95 return res;
96}
97
98/*------------------------------------------------------------------------*/
99
102 CADICAL_assert (!walker.broken.empty ());
103 int64_t size = walker.broken.size ();
104 if (size > INT_MAX)
105 size = INT_MAX;
106 int pos = walker.random.pick_int (0, size - 1);
107 Clause *res = walker.broken[pos];
108 LOG (res, "picking random position %d", pos);
109 return res;
110}
111
112/*------------------------------------------------------------------------*/
113
114// Compute the number of clauses which would be become unsatisfied if 'lit'
115// is flipped and set to false. This is called the 'break-count' of 'lit'.
116
118
120 CADICAL_assert (val (lit) > 0);
121
122 unsigned res = 0; // The computed break-count of 'lit'.
123
124 for (auto &w : watches (lit)) {
125 CADICAL_assert (w.blit != lit);
126 if (val (w.blit) > 0)
127 continue;
128 if (w.binary ()) {
129 res++;
130 continue;
131 }
132
133 Clause *c = w.clause;
134 CADICAL_assert (lit == c->literals[0]);
135
136 // Now try to find a second satisfied literal starting at 'literals[1]'
137 // shifting all the traversed literals to right by one position in order
138 // to move such a second satisfying literal to 'literals[1]'. This move
139 // to front strategy improves the chances to find the second satisfying
140 // literal earlier in subsequent break-count computations.
141 //
142 auto begin = c->begin () + 1;
143 const auto end = c->end ();
144 auto i = begin;
145 int prev = 0;
146 while (i != end) {
147 const int other = *i;
148 *i++ = prev;
149 prev = other;
150 if (val (other) < 0)
151 continue;
152
153 // Found 'other' as second satisfying literal.
154
155 w.blit = other; // Update 'blit'
156 *begin = other; // and move to front.
157
158 break;
159 }
160
161 if (i != end)
162 continue; // Double satisfied!
163
164 // Otherwise restore literals (undo shift to the right).
165 //
166 while (i != begin) {
167 const int other = *--i;
168 *i = prev;
169 prev = other;
170 }
171
172 res++; // Literal 'lit' single satisfies clause 'c'.
173 }
174
175 return res;
176}
177
178/*------------------------------------------------------------------------*/
179
180// Given an unsatisfied clause 'c', in which we want to flip a literal, we
181// first determine the exponential score based on the break-count of its
182// literals and then sample the literals based on these scores. The CB
183// value is smaller than one and thus the score is exponentially decreasing
184// with the break-count increasing. The sampling works as in 'ProbSAT' and
185// 'YalSAT' by summing up the scores and then picking a random limit in the
186// range of zero to the sum, then summing up the scores again and picking
187// the first literal which reaches the limit. Note, that during incremental
188// SAT solving we can not flip assumed variables. Those are assigned at
189// decision level one, while the other variables are assigned at two.
190
192 LOG ("picking literal by break-count");
193 CADICAL_assert (walker.scores.empty ());
194 double sum = 0;
195 int64_t propagations = 0;
196 for (const auto lit : *c) {
198 if (var (lit).level == 1) {
199 LOG ("skipping assumption %d for scoring", -lit);
200 continue;
201 }
203 propagations++;
204 unsigned tmp = walk_break_value (-lit);
205 double score = walker.score (tmp);
206 LOG ("literal %d break-count %u score %g", lit, tmp, score);
207 walker.scores.push_back (score);
208 sum += score;
209 }
210 LOG ("scored %zd literals", walker.scores.size ());
211 CADICAL_assert (!walker.scores.empty ());
212 walker.propagations += propagations;
213 stats.propagations.walk += propagations;
214 CADICAL_assert (walker.scores.size () <= (size_t) c->size);
215 const double lim = sum * walker.random.generate_double ();
216 LOG ("score sum %g limit %g", sum, lim);
217 const auto end = c->end ();
218 auto i = c->begin ();
219 auto j = walker.scores.begin ();
220 int res;
221 for (;;) {
222 CADICAL_assert (i != end);
223 res = *i++;
224 if (var (res).level > 1)
225 break;
226 LOG ("skipping assumption %d without score", -res);
227 }
228 sum = *j++;
229 while (sum <= lim && i != end) {
230 res = *i++;
231 if (var (res).level == 1) {
232 LOG ("skipping assumption %d without score", -res);
233 continue;
234 }
235 sum += *j++;
236 }
237 walker.scores.clear ();
238 LOG ("picking literal %d by break-count", res);
239 return res;
240}
241
242/*------------------------------------------------------------------------*/
243
245
247 LOG ("flipping assign %d", lit);
248 CADICAL_assert (val (lit) < 0);
249
250 // First flip the literal value.
251 //
252 const int tmp = sign (lit);
253 const int idx = abs (lit);
254 set_val (idx, tmp);
255 CADICAL_assert (val (lit) > 0);
256
257 // Then remove 'c' and all other now satisfied (made) clauses.
258 {
259 // Simply go over all unsatisfied (broken) clauses.
260
261 LOG ("trying to make %zd broken clauses", walker.broken.size ());
262
263 // We need to measure (and bound) the memory accesses during traversing
264 // broken clauses in terms of 'propagations'. This is tricky since we
265 // are not actually propagating literals. Instead we use the clause
266 // variable 'ratio' as an approximation to the number of clauses used
267 // during propagating a literal. Note that we use a one-watch scheme.
268 // Accordingly the number of broken clauses traversed divided by that
269 // ratio is an approximation of the number of propagation this would
270 // correspond to (in terms of memory access). To eagerly update these
271 // statistics we simply increment the propagation counter after every
272 // 'ratio' traversed clause. These propagations are particularly
273 // expensive if the number of broken clauses is large which usually
274 // happens initially.
275 //
276 const double ratio = clause_variable_ratio ();
277 const auto eou = walker.broken.end ();
278 auto j = walker.broken.begin (), i = j;
279#ifdef LOGGING
280 int64_t made = 0;
281#endif
282 int64_t count = 0;
283
284 while (i != eou) {
285
286 Clause *d = *j++ = *i++;
287
288 int *literals = d->literals, prev = 0;
289
290 // Find 'lit' in 'd'.
291 //
292 const int size = d->size;
293 for (int i = 0; i < size; i++) {
294 const int other = literals[i];
295 CADICAL_assert (active (other));
296 literals[i] = prev;
297 prev = other;
298 if (other == lit)
299 break;
300 CADICAL_assert (val (other) < 0);
301 }
302
303 // If 'lit' is in 'd' then move it to the front to watch it.
304 //
305 if (prev == lit) {
306 literals[0] = lit;
307 LOG (d, "made");
308 watch_literal (literals[0], literals[1], d);
309#ifdef LOGGING
310 made++;
311#endif
312 j--;
313
314 } else { // Otherwise the clause is not satisfied, undo shift.
315
316 for (int i = size - 1; i >= 0; i--) {
317 int other = literals[i];
318 literals[i] = prev;
319 prev = other;
320 }
321 }
322
323 if (count--)
324 continue;
325
326 // Update these counters eagerly. Otherwise if we delay the update
327 // until all clauses are traversed, interrupting the solver has a high
328 // chance of giving bogus statistics on the number of 'propagations'
329 // in 'walk', if it is interrupted in this loop.
330
331 count = ratio; // Starting counting down again.
332 walker.propagations++;
333 stats.propagations.walk++;
334 }
335 LOG ("made %" PRId64 " clauses by flipping %d", made, lit);
336 walker.broken.resize (j - walker.broken.begin ());
337 }
338
339 // Finally add all new unsatisfied (broken) clauses.
340 {
341 walker.propagations++; // This really corresponds now to one
342 stats.propagations.walk++; // propagation (in a one-watch scheme).
343
344#ifdef LOGGING
345 int64_t broken = 0;
346#endif
347 Watches &ws = watches (-lit);
348
349 LOG ("trying to break %zd watched clauses", ws.size ());
350
351 for (const auto &w : ws) {
352 Clause *d = w.clause;
353 LOG (d, "unwatch %d in", -lit);
354 int *literals = d->literals, replacement = 0, prev = -lit;
355 CADICAL_assert (literals[0] == -lit);
356 const int size = d->size;
357 for (int i = 1; i < size; i++) {
358 const int other = literals[i];
359 CADICAL_assert (active (other));
360 literals[i] = prev; // shift all to right
361 prev = other;
362 const signed char tmp = val (other);
363 if (tmp < 0)
364 continue;
365 replacement = other; // satisfying literal
366 break;
367 }
368 if (replacement) {
369 literals[1] = -lit;
370 literals[0] = replacement;
371 CADICAL_assert (-lit != replacement);
372 watch_literal (replacement, -lit, d);
373 } else {
374 for (int i = size - 1; i > 0; i--) { // undo shift
375 const int other = literals[i];
376 literals[i] = prev;
377 prev = other;
378 }
379 CADICAL_assert (literals[0] == -lit);
380 LOG (d, "broken");
381 walker.broken.push_back (d);
382#ifdef LOGGING
383 broken++;
384#endif
385 }
386 }
387 LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit);
388 ws.clear ();
389 }
390}
391
392/*------------------------------------------------------------------------*/
393
394// Check whether to save the current phases as new global minimum.
395
397 int64_t broken = walker.broken.size ();
398 if (broken >= stats.walk.minimum)
399 return;
400 VERBOSE (3, "new global minimum %" PRId64 "", broken);
401 stats.walk.minimum = broken;
402 for (auto i : vars) {
403 const signed char tmp = vals[i];
404 if (tmp)
405 phases.min[i] = phases.saved[i] = tmp;
406 }
407}
408
409/*------------------------------------------------------------------------*/
410
411int Internal::walk_round (int64_t limit, bool prev) {
412
413 backtrack ();
414 if (propagated < trail.size () && !propagate ()) {
415 LOG ("empty clause after root level propagation");
417 return 20;
418 }
419
420 stats.walk.count++;
421
422 clear_watches ();
423
424 // Remove all fixed variables first (assigned at decision level zero).
425 //
426 if (last.collect.fixed < stats.all.fixed)
428
429#ifndef CADICAL_QUIET
430 // We want to see more messages during initial local search.
431 //
432 if (localsearching) {
433 CADICAL_assert (!force_phase_messages);
434 force_phase_messages = true;
435 }
436#endif
437
438 PHASE ("walk", stats.walk.count,
439 "random walk limit of %" PRId64 " propagations", limit);
440
441 // First compute the average clause size for picking the CB constant.
442 //
443 double size = 0;
444 int64_t n = 0;
445 for (const auto c : clauses) {
446 if (c->garbage)
447 continue;
448 if (c->redundant) {
449 if (!opts.walkredundant)
450 continue;
452 continue;
453 }
454 size += c->size;
455 n++;
456 }
457 double average_size = relative (size, n);
458
459 PHASE ("walk", stats.walk.count,
460 "%" PRId64 " clauses average size %.2f over %d variables", n,
461 average_size, active ());
462
463 // Instantiate data structures for this local search round.
464 //
465 Walker walker (internal, average_size, limit);
466
467 bool failed = false; // Inconsistent assumptions?
468
469 level = 1; // Assumed variables assigned at level 1.
470
471 if (assumptions.empty ()) {
472 LOG ("no assumptions so assigning all variables to decision phase");
473 } else {
474 LOG ("assigning assumptions to their forced phase first");
475 for (const auto lit : assumptions) {
476 signed char tmp = val (lit);
477 if (tmp > 0)
478 continue;
479 if (tmp < 0) {
480 LOG ("inconsistent assumption %d", lit);
481 failed = true;
482 break;
483 }
484 if (!active (lit))
485 continue;
486 tmp = sign (lit);
487 const int idx = abs (lit);
488 LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx);
489 set_val (idx, tmp);
490 CADICAL_assert (level == 1);
491 var (idx).level = 1;
492 }
493 if (!failed)
494 LOG ("now assigning remaining variables to their decision phase");
495 }
496
497 level = 2; // All other non assumed variables assigned at level 2.
498
499 if (!failed) {
500
501 for (auto idx : vars) {
502 if (!active (idx)) {
503 LOG ("skipping inactive variable %d", idx);
504 continue;
505 }
506 if (vals[idx]) {
507 CADICAL_assert (var (idx).level == 1);
508 LOG ("skipping assumed variable %d", idx);
509 continue;
510 }
511 int tmp = 0;
512 if (prev)
513 tmp = phases.prev[idx];
514 if (!tmp)
515 tmp = sign (decide_phase (idx, true));
516 CADICAL_assert (tmp == 1 || tmp == -1);
517 set_val (idx, tmp);
518 CADICAL_assert (level == 2);
519 var (idx).level = 2;
520 LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx);
521 }
522
523 LOG ("watching satisfied and registering broken clauses");
524#ifdef LOGGING
525 int64_t watched = 0;
526#endif
527 for (const auto c : clauses) {
528
529 if (c->garbage)
530 continue;
531 if (c->redundant) {
532 if (!opts.walkredundant)
533 continue;
535 continue;
536 }
537
538 bool satisfiable = false; // contains not only assumptions
539 int satisfied = 0; // clause satisfied?
540
541 int *lits = c->literals;
542 const int size = c->size;
543
544 // Move to front satisfied literals and determine whether there
545 // is at least one (non-assumed) literal that can be flipped.
546 //
547 for (int i = 0; satisfied < 2 && i < size; i++) {
548 const int lit = lits[i];
549 CADICAL_assert (active (lit)); // Due to garbage collection.
550 if (val (lit) > 0) {
551 swap (lits[satisfied], lits[i]);
552 if (!satisfied++)
553 LOG ("first satisfying literal %d", lit);
554 } else if (!satisfiable && var (lit).level > 1) {
555 LOG ("non-assumption potentially satisfying literal %d", lit);
556 satisfiable = true;
557 }
558 }
559
560 if (!satisfied && !satisfiable) {
561 LOG (c, "due to assumptions unsatisfiable");
562 LOG ("stopping local search since assumptions falsify a clause");
563 failed = true;
564 break;
565 }
566
567 if (satisfied) {
568 watch_literal (lits[0], lits[1], c);
569#ifdef LOGGING
570 watched++;
571#endif
572 } else {
573 CADICAL_assert (satisfiable); // at least one non-assumed variable ...
574 LOG (c, "broken");
575 walker.broken.push_back (c);
576 }
577 }
578#ifdef LOGGING
579 if (!failed) {
580 int64_t broken = walker.broken.size ();
581 int64_t total = watched + broken;
582 LOG ("watching %" PRId64 " clauses %.0f%% "
583 "out of %" PRId64 " (watched and broken)",
584 watched, percent (watched, total), total);
585 }
586#endif
587 }
588
589 int64_t old_global_minimum = stats.walk.minimum;
590
591 int res; // Tells caller to continue with local search.
592
593 if (!failed) {
594
595 int64_t broken = walker.broken.size ();
596
597 PHASE ("walk", stats.walk.count,
598 "starting with %" PRId64 " unsatisfied clauses "
599 "(%.0f%% out of %" PRId64 ")",
600 broken, percent (broken, stats.current.irredundant),
601 stats.current.irredundant);
602
604
605 int64_t minimum = broken;
606#ifndef CADICAL_QUIET
607 int64_t flips = 0;
608#endif
609 while (!terminated_asynchronously () && !walker.broken.empty () &&
610 walker.propagations < walker.limit) {
611#ifndef CADICAL_QUIET
612 flips++;
613#endif
614 stats.walk.flips++;
615 stats.walk.broken += broken;
617 const int lit = walk_pick_lit (walker, c);
619 broken = walker.broken.size ();
620 LOG ("now have %" PRId64 " broken clauses in total", broken);
621 if (broken >= minimum)
622 continue;
623 minimum = broken;
624 VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips",
625 minimum, flips);
627 }
628
629 if (minimum < old_global_minimum)
630 PHASE ("walk", stats.walk.count,
631 "%snew global minimum %" PRId64 "%s in %" PRId64 " flips and "
632 "%" PRId64 " propagations",
633 tout.bright_yellow_code (), minimum, tout.normal_code (),
634 flips, walker.propagations);
635 else
636 PHASE ("walk", stats.walk.count,
637 "best phase minimum %" PRId64 " in %" PRId64 " flips and "
638 "%" PRId64 " propagations",
639 minimum, flips, walker.propagations);
640
641 if (opts.profile >= 2) {
642 PHASE ("walk", stats.walk.count,
643 "%.2f million propagations per second",
644 relative (1e-6 * walker.propagations,
645 time () - profiles.walk.started));
646
647 PHASE ("walk", stats.walk.count, "%.2f thousand flips per second",
648 relative (1e-3 * flips, time () - profiles.walk.started));
649
650 } else {
651 PHASE ("walk", stats.walk.count, "%.2f million propagations",
652 1e-6 * walker.propagations);
653
654 PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips);
655 }
656
657 if (minimum > 0) {
658 LOG ("minimum %" PRId64 " non-zero thus potentially continue",
659 minimum);
660 res = 0;
661 } else {
662 LOG ("minimum is zero thus stop local search");
663 res = 10;
664 }
665
666 } else {
667
668 res = 20;
669
670 PHASE ("walk", stats.walk.count,
671 "aborted due to inconsistent assumptions");
672 }
673
674 copy_phases (phases.prev);
675
676 for (auto idx : vars)
677 if (active (idx))
678 set_val (idx, 0);
679
680 CADICAL_assert (level == 2);
681 level = 0;
682
683 clear_watches ();
685
686#ifndef CADICAL_QUIET
687 if (localsearching) {
688 CADICAL_assert (force_phase_messages);
689 force_phase_messages = false;
690 }
691#endif
692
693 return res;
694}
695
698 int64_t limit = stats.propagations.search;
699 limit *= 1e-3 * opts.walkeffort;
700 if (limit < opts.walkmineff)
701 limit = opts.walkmineff;
702 if (limit > opts.walkmaxeff)
703 limit = opts.walkmaxeff;
704 (void) walk_round (limit, false);
706}
707
708} // namespace CaDiCaL
709
#define ABC_NAMESPACE_IMPL_START
#define ABC_NAMESPACE_IMPL_END
#define CADICAL_assert(ignore)
Definition global.h:14
#define LOG(...)
bool pos
Definition globals.c:30
#define PHASE(...)
Definition message.hpp:52
#define VERBOSE(...)
Definition message.hpp:58
int sign(int lit)
Definition util.hpp:22
double relative(double a, double b)
Definition util.hpp:20
Terminal tout(stdout)
Definition terminal.hpp:97
vector< Watch > Watches
Definition watch.hpp:45
double percent(double a, double b)
Definition util.hpp:21
unsigned long long size
Definition giaNewBdd.h:39
#define START_INNER_WALK()
Definition profile.hpp:222
#define STOP_INNER_WALK()
Definition profile.hpp:240
int lit
Definition satVec.h:130
literal_iterator begin()
Definition clause.hpp:131
literal_iterator end()
Definition clause.hpp:132
double clause_variable_ratio() const
Definition internal.hpp:385
void watch_literal(int lit, int blit, Clause *c)
Definition internal.hpp:623
Var & var(int lit)
Definition internal.hpp:452
vector< int > trail
Definition internal.hpp:259
Clause * walk_pick_clause(Walker &)
bool terminated_asynchronously(int factor=1)
const Sange lits
Definition internal.hpp:325
int decide_phase(int idx, bool target)
void walk_flip_lit(Walker &, int lit)
signed char val(int lit) const
bool limit(const char *name, int)
void set_val(int lit, signed char val)
int walk_pick_lit(Walker &, Clause *)
vector< int > assumptions
Definition internal.hpp:261
bool active(int lit)
Definition internal.hpp:360
void backtrack(int target_level=0)
vector< Clause * > clauses
Definition internal.hpp:283
void walk_save_minimum(Walker &)
signed char * vals
Definition internal.hpp:221
const Range vars
Definition internal.hpp:324
double & score(int lit)
Definition internal.hpp:457
void require_mode(Mode m) const
Definition internal.hpp:173
unsigned walk_break_value(int lit)
void copy_phases(vector< signed char > &)
Internal * internal
Definition internal.hpp:311
Watches & watches(int lit)
Definition internal.hpp:467
bool likely_to_be_kept_clause(Clause *c)
int walk_round(int64_t limit, bool prev)
void connect_watches(bool irredundant_only=false)
int level
Definition var.hpp:19
vector< double > scores
vector< double > table
double score(unsigned)
Walker(Internal *, double size, int64_t limit)
Internal * internal
vector< Clause * > broken
unsigned size
Definition vector.h:35
struct walker walker
Definition walk.c:19
vector watches
Definition watch.h:49