cadical 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +26 -0
- data/README.md +101 -0
- data/ext/cadical/cadical/LICENSE +28 -0
- data/ext/cadical/cadical/README.md +65 -0
- data/ext/cadical/cadical/VERSION +1 -0
- data/ext/cadical/cadical/src/analyze.cpp +1360 -0
- data/ext/cadical/cadical/src/arena.cpp +30 -0
- data/ext/cadical/cadical/src/arena.hpp +105 -0
- data/ext/cadical/cadical/src/assume.cpp +616 -0
- data/ext/cadical/cadical/src/averages.cpp +34 -0
- data/ext/cadical/cadical/src/averages.hpp +37 -0
- data/ext/cadical/cadical/src/backbone.cpp +631 -0
- data/ext/cadical/cadical/src/backtrack.cpp +177 -0
- data/ext/cadical/cadical/src/backward.cpp +231 -0
- data/ext/cadical/cadical/src/bins.cpp +22 -0
- data/ext/cadical/cadical/src/bins.hpp +22 -0
- data/ext/cadical/cadical/src/block.cpp +824 -0
- data/ext/cadical/cadical/src/block.hpp +37 -0
- data/ext/cadical/cadical/src/cadical.cpp +1026 -0
- data/ext/cadical/cadical/src/cadical.hpp +1419 -0
- data/ext/cadical/cadical/src/ccadical.cpp +208 -0
- data/ext/cadical/cadical/src/ccadical.h +74 -0
- data/ext/cadical/cadical/src/checker.cpp +649 -0
- data/ext/cadical/cadical/src/checker.hpp +173 -0
- data/ext/cadical/cadical/src/clause.cpp +689 -0
- data/ext/cadical/cadical/src/clause.hpp +190 -0
- data/ext/cadical/cadical/src/collect.cpp +545 -0
- data/ext/cadical/cadical/src/compact.cpp +550 -0
- data/ext/cadical/cadical/src/condition.cpp +940 -0
- data/ext/cadical/cadical/src/config.cpp +101 -0
- data/ext/cadical/cadical/src/config.hpp +20 -0
- data/ext/cadical/cadical/src/congruence.cpp +7880 -0
- data/ext/cadical/cadical/src/congruence.hpp +757 -0
- data/ext/cadical/cadical/src/constrain.cpp +64 -0
- data/ext/cadical/cadical/src/contract.cpp +27 -0
- data/ext/cadical/cadical/src/contract.hpp +139 -0
- data/ext/cadical/cadical/src/cover.cpp +704 -0
- data/ext/cadical/cadical/src/cover.hpp +34 -0
- data/ext/cadical/cadical/src/decide.cpp +347 -0
- data/ext/cadical/cadical/src/decompose.cpp +738 -0
- data/ext/cadical/cadical/src/decompose.hpp +23 -0
- data/ext/cadical/cadical/src/deduplicate.cpp +275 -0
- data/ext/cadical/cadical/src/definition.cpp +283 -0
- data/ext/cadical/cadical/src/delay.hpp +38 -0
- data/ext/cadical/cadical/src/drattracer.cpp +153 -0
- data/ext/cadical/cadical/src/drattracer.hpp +55 -0
- data/ext/cadical/cadical/src/elim.cpp +1171 -0
- data/ext/cadical/cadical/src/elim.hpp +53 -0
- data/ext/cadical/cadical/src/elimfast.cpp +570 -0
- data/ext/cadical/cadical/src/ema.cpp +95 -0
- data/ext/cadical/cadical/src/ema.hpp +68 -0
- data/ext/cadical/cadical/src/extend.cpp +289 -0
- data/ext/cadical/cadical/src/external.cpp +1034 -0
- data/ext/cadical/cadical/src/external.hpp +487 -0
- data/ext/cadical/cadical/src/external_propagate.cpp +1316 -0
- data/ext/cadical/cadical/src/factor.cpp +1011 -0
- data/ext/cadical/cadical/src/factor.hpp +54 -0
- data/ext/cadical/cadical/src/file.cpp +506 -0
- data/ext/cadical/cadical/src/file.hpp +210 -0
- data/ext/cadical/cadical/src/flags.cpp +135 -0
- data/ext/cadical/cadical/src/flags.hpp +93 -0
- data/ext/cadical/cadical/src/flip.cpp +269 -0
- data/ext/cadical/cadical/src/format.cpp +89 -0
- data/ext/cadical/cadical/src/format.hpp +36 -0
- data/ext/cadical/cadical/src/frattracer.cpp +277 -0
- data/ext/cadical/cadical/src/frattracer.hpp +67 -0
- data/ext/cadical/cadical/src/gates.cpp +766 -0
- data/ext/cadical/cadical/src/heap.hpp +212 -0
- data/ext/cadical/cadical/src/idruptracer.cpp +566 -0
- data/ext/cadical/cadical/src/idruptracer.hpp +112 -0
- data/ext/cadical/cadical/src/instantiate.cpp +365 -0
- data/ext/cadical/cadical/src/instantiate.hpp +45 -0
- data/ext/cadical/cadical/src/internal.cpp +1265 -0
- data/ext/cadical/cadical/src/internal.hpp +1941 -0
- data/ext/cadical/cadical/src/inttypes.hpp +34 -0
- data/ext/cadical/cadical/src/ipasir.cpp +47 -0
- data/ext/cadical/cadical/src/ipasir.h +37 -0
- data/ext/cadical/cadical/src/kitten.c +2599 -0
- data/ext/cadical/cadical/src/kitten.h +97 -0
- data/ext/cadical/cadical/src/level.hpp +33 -0
- data/ext/cadical/cadical/src/lidruptracer.cpp +657 -0
- data/ext/cadical/cadical/src/lidruptracer.hpp +122 -0
- data/ext/cadical/cadical/src/limit.cpp +146 -0
- data/ext/cadical/cadical/src/limit.hpp +168 -0
- data/ext/cadical/cadical/src/logging.cpp +214 -0
- data/ext/cadical/cadical/src/logging.hpp +98 -0
- data/ext/cadical/cadical/src/lookahead.cpp +520 -0
- data/ext/cadical/cadical/src/lratchecker.cpp +833 -0
- data/ext/cadical/cadical/src/lratchecker.hpp +168 -0
- data/ext/cadical/cadical/src/lrattracer.cpp +200 -0
- data/ext/cadical/cadical/src/lrattracer.hpp +61 -0
- data/ext/cadical/cadical/src/lucky.cpp +506 -0
- data/ext/cadical/cadical/src/message.cpp +218 -0
- data/ext/cadical/cadical/src/message.hpp +65 -0
- data/ext/cadical/cadical/src/minimize.cpp +224 -0
- data/ext/cadical/cadical/src/mobical.cpp +5588 -0
- data/ext/cadical/cadical/src/occs.cpp +52 -0
- data/ext/cadical/cadical/src/occs.hpp +36 -0
- data/ext/cadical/cadical/src/options.cpp +359 -0
- data/ext/cadical/cadical/src/options.hpp +442 -0
- data/ext/cadical/cadical/src/parse.cpp +471 -0
- data/ext/cadical/cadical/src/parse.hpp +75 -0
- data/ext/cadical/cadical/src/phases.cpp +56 -0
- data/ext/cadical/cadical/src/phases.hpp +19 -0
- data/ext/cadical/cadical/src/probe.cpp +990 -0
- data/ext/cadical/cadical/src/profile.cpp +107 -0
- data/ext/cadical/cadical/src/profile.hpp +283 -0
- data/ext/cadical/cadical/src/proof.cpp +691 -0
- data/ext/cadical/cadical/src/proof.hpp +125 -0
- data/ext/cadical/cadical/src/propagate.cpp +584 -0
- data/ext/cadical/cadical/src/queue.cpp +90 -0
- data/ext/cadical/cadical/src/queue.hpp +74 -0
- data/ext/cadical/cadical/src/radix.hpp +180 -0
- data/ext/cadical/cadical/src/random.cpp +207 -0
- data/ext/cadical/cadical/src/random.h +44 -0
- data/ext/cadical/cadical/src/random.hpp +98 -0
- data/ext/cadical/cadical/src/range.hpp +125 -0
- data/ext/cadical/cadical/src/reap.cpp +127 -0
- data/ext/cadical/cadical/src/reap.hpp +28 -0
- data/ext/cadical/cadical/src/reduce.cpp +278 -0
- data/ext/cadical/cadical/src/reluctant.hpp +82 -0
- data/ext/cadical/cadical/src/rephase.cpp +402 -0
- data/ext/cadical/cadical/src/report.cpp +313 -0
- data/ext/cadical/cadical/src/resources.cpp +160 -0
- data/ext/cadical/cadical/src/resources.hpp +16 -0
- data/ext/cadical/cadical/src/restart.cpp +178 -0
- data/ext/cadical/cadical/src/restore.cpp +267 -0
- data/ext/cadical/cadical/src/score.cpp +51 -0
- data/ext/cadical/cadical/src/score.hpp +16 -0
- data/ext/cadical/cadical/src/shrink.cpp +507 -0
- data/ext/cadical/cadical/src/signal.cpp +136 -0
- data/ext/cadical/cadical/src/signal.hpp +33 -0
- data/ext/cadical/cadical/src/solution.cpp +50 -0
- data/ext/cadical/cadical/src/solver.cpp +1871 -0
- data/ext/cadical/cadical/src/stable.cpp +31 -0
- data/ext/cadical/cadical/src/stack.h +110 -0
- data/ext/cadical/cadical/src/stats.cpp +920 -0
- data/ext/cadical/cadical/src/stats.hpp +418 -0
- data/ext/cadical/cadical/src/subsume.cpp +645 -0
- data/ext/cadical/cadical/src/sweep.cpp +1960 -0
- data/ext/cadical/cadical/src/sweep.hpp +61 -0
- data/ext/cadical/cadical/src/terminal.cpp +38 -0
- data/ext/cadical/cadical/src/terminal.hpp +96 -0
- data/ext/cadical/cadical/src/ternary.cpp +450 -0
- data/ext/cadical/cadical/src/testing.hpp +24 -0
- data/ext/cadical/cadical/src/tier.cpp +189 -0
- data/ext/cadical/cadical/src/tracer.hpp +186 -0
- data/ext/cadical/cadical/src/transred.cpp +253 -0
- data/ext/cadical/cadical/src/unstable.cpp +29 -0
- data/ext/cadical/cadical/src/util.cpp +129 -0
- data/ext/cadical/cadical/src/util.hpp +181 -0
- data/ext/cadical/cadical/src/var.cpp +39 -0
- data/ext/cadical/cadical/src/var.hpp +22 -0
- data/ext/cadical/cadical/src/veripbtracer.cpp +427 -0
- data/ext/cadical/cadical/src/veripbtracer.hpp +106 -0
- data/ext/cadical/cadical/src/version.cpp +107 -0
- data/ext/cadical/cadical/src/version.hpp +13 -0
- data/ext/cadical/cadical/src/vivify.cpp +1893 -0
- data/ext/cadical/cadical/src/vivify.hpp +50 -0
- data/ext/cadical/cadical/src/walk.cpp +1088 -0
- data/ext/cadical/cadical/src/walk.hpp +91 -0
- data/ext/cadical/cadical/src/walk_full_occs.cpp +966 -0
- data/ext/cadical/cadical/src/warmup.cpp +403 -0
- data/ext/cadical/cadical/src/watch.cpp +126 -0
- data/ext/cadical/cadical/src/watch.hpp +77 -0
- data/ext/cadical/cadical_ext.c +778 -0
- data/ext/cadical/extconf.rb +26 -0
- data/lib/cadical/version.rb +3 -0
- data/lib/cadical.rb +12 -0
- metadata +218 -0
|
@@ -0,0 +1,1088 @@
|
|
|
1
|
+
#include "walk.hpp"
|
|
2
|
+
#include "internal.hpp"
|
|
3
|
+
#include "random.hpp"
|
|
4
|
+
|
|
5
|
+
namespace CaDiCaL {
|
|
6
|
+
|
|
7
|
+
/*------------------------------------------------------------------------*/
|
|
8
|
+
|
|
9
|
+
// Random walk local search based on 'ProbSAT' ideas.
|
|
10
|
+
|
|
11
|
+
// We (based on the Master project from Leah Hohl) tried to ticks
|
|
12
|
+
// local search similarly to the other parts of the solver with
|
|
13
|
+
// limited success however.
|
|
14
|
+
//
|
|
15
|
+
// On the problem `ncc_none_5047_6_3_3_3_0_435991723', the broken part
|
|
16
|
+
// of walk_flip is very cheap and should not be counted in ticks, but
|
|
17
|
+
// on various other problems `9pipe_k' it is very important to ticks
|
|
18
|
+
// this part too.
|
|
19
|
+
|
|
20
|
+
// using ClauseOrBinary = std::variant <Clause*, TaggedBinary>;
|
|
21
|
+
|
|
22
|
+
struct Walker {
|
|
23
|
+
|
|
24
|
+
Internal *internal;
|
|
25
|
+
|
|
26
|
+
// for efficiency, storing the model each time an improvement is
|
|
27
|
+
// found is too costly. Instead we store some of the flips since
|
|
28
|
+
// last time and the position of the best model found so far.
|
|
29
|
+
Random random; // local random number generator
|
|
30
|
+
int64_t ticks; // ticks to approximate run time
|
|
31
|
+
int64_t limit; // limit on number of propagations
|
|
32
|
+
vector<ClauseOrBinary> broken; // currently unsatisfied clauses
|
|
33
|
+
double epsilon; // smallest considered score
|
|
34
|
+
vector<double> table; // break value to score table
|
|
35
|
+
vector<double> scores; // scores of candidate literals
|
|
36
|
+
std::vector<int>
|
|
37
|
+
flips; // remember the flips compared to the last best saved model
|
|
38
|
+
int best_trail_pos;
|
|
39
|
+
int64_t minimum = INT64_MAX;
|
|
40
|
+
std::vector<signed char> best_values; // best model stored so far
|
|
41
|
+
double score (unsigned); // compute score from break count
|
|
42
|
+
#ifndef NDEBUG
|
|
43
|
+
std::vector<signed char> current_best_model; // best model found so far
|
|
44
|
+
#endif
|
|
45
|
+
Walker (Internal *, int64_t limit);
|
|
46
|
+
void populate_table (double size);
|
|
47
|
+
void push_flipped (int flipped);
|
|
48
|
+
void save_walker_trail (bool);
|
|
49
|
+
void save_final_minimum (int64_t old_minimum);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// These are in essence the CB values from Adrian Balint's thesis. They
|
|
53
|
+
// denote the inverse 'cb' of the base 'b' of the (probability) weight
|
|
54
|
+
// 'b^-i' for picking a literal with the break value 'i' (first column is
|
|
55
|
+
// the 'size', second the 'CB' value).
|
|
56
|
+
|
|
57
|
+
static double cbvals[][2] = {
|
|
58
|
+
{0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70},
|
|
59
|
+
{6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better.
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
static const int ncbvals = sizeof cbvals / sizeof cbvals[0];
|
|
63
|
+
|
|
64
|
+
// We interpolate the CB values for uniform random SAT formula to the non
|
|
65
|
+
// integer situation of average clause size by piecewise linear functions.
|
|
66
|
+
//
|
|
67
|
+
// y2 - y1
|
|
68
|
+
// ------- * (x - x1) + y1
|
|
69
|
+
// x2 - x1
|
|
70
|
+
//
|
|
71
|
+
// where 'x' is the average size of clauses and 'y' the CB value.
|
|
72
|
+
|
|
73
|
+
inline static double fitcbval (double size) {
|
|
74
|
+
int i = 0;
|
|
75
|
+
while (i + 2 < ncbvals &&
|
|
76
|
+
(cbvals[i][0] > size || cbvals[i + 1][0] < size))
|
|
77
|
+
i++;
|
|
78
|
+
const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0];
|
|
79
|
+
const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1];
|
|
80
|
+
const double dx = x2 - x1, dy = y2 - y1;
|
|
81
|
+
assert (dx);
|
|
82
|
+
const double res = dy * (size - x1) / dx + y1;
|
|
83
|
+
assert (res > 0);
|
|
84
|
+
return res;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Initialize the data structures for one local search round.
|
|
88
|
+
|
|
89
|
+
Walker::Walker (Internal *i, int64_t l)
|
|
90
|
+
: internal (i), random (internal->opts.seed), // global random seed
|
|
91
|
+
ticks (0), limit (l), best_trail_pos (-1) {
|
|
92
|
+
random += internal->stats.walk.count; // different seed every time
|
|
93
|
+
flips.reserve (i->max_var / 4);
|
|
94
|
+
best_values.resize (i->max_var + 1, 0);
|
|
95
|
+
#ifndef NDEBUG
|
|
96
|
+
current_best_model.resize (i->max_var + 1, 0);
|
|
97
|
+
#endif
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
void Walker::populate_table (double size) {
|
|
101
|
+
// This is the magic constant in ProbSAT (also called 'CB'), which we pick
|
|
102
|
+
// according to the average size every second invocation and otherwise
|
|
103
|
+
// just the default '2.0', which turns into the base '0.5'.
|
|
104
|
+
//
|
|
105
|
+
const bool use_size_based_cb = (internal->stats.walk.count & 1);
|
|
106
|
+
const double cb = use_size_based_cb ? fitcbval (size) : 2.0;
|
|
107
|
+
assert (cb);
|
|
108
|
+
const double base = 1 / cb; // scores are 'base^0,base^1,base^2,...
|
|
109
|
+
|
|
110
|
+
double next = 1;
|
|
111
|
+
for (epsilon = next; next; next = epsilon * base)
|
|
112
|
+
table.push_back (epsilon = next);
|
|
113
|
+
|
|
114
|
+
PHASE ("walk", internal->stats.walk.count,
|
|
115
|
+
"CB %.2f with inverse %.2f as base and table size %zd", cb, base,
|
|
116
|
+
table.size ());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Add the literal to flip to the queue
|
|
120
|
+
|
|
121
|
+
void Walker::push_flipped (int flipped) {
|
|
122
|
+
LOG ("push literal %s on the flips", LOGLIT (flipped));
|
|
123
|
+
assert (flipped);
|
|
124
|
+
if (best_trail_pos < 0) {
|
|
125
|
+
LOG ("not pushing flipped %s to already invalid trail",
|
|
126
|
+
LOGLIT (flipped));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const size_t size_trail = flips.size ();
|
|
131
|
+
const size_t limit = internal->max_var / 4 + 1;
|
|
132
|
+
if (size_trail < limit) {
|
|
133
|
+
flips.push_back (flipped);
|
|
134
|
+
LOG ("pushed flipped %s to trail which now has size %zd",
|
|
135
|
+
LOGLIT (flipped), size_trail + 1);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (best_trail_pos) {
|
|
140
|
+
LOG ("trail reached limit %zd but has best position %d", limit,
|
|
141
|
+
best_trail_pos);
|
|
142
|
+
save_walker_trail (true);
|
|
143
|
+
flips.push_back (flipped);
|
|
144
|
+
LOG ("pushed flipped %s to trail which now has size %zu",
|
|
145
|
+
LOGLIT (flipped), flips.size ());
|
|
146
|
+
return;
|
|
147
|
+
} else {
|
|
148
|
+
LOG ("trail reached limit %zd without best position", limit);
|
|
149
|
+
flips.clear ();
|
|
150
|
+
LOG ("not pushing %s to invalidated trail", LOGLIT (flipped));
|
|
151
|
+
best_trail_pos = -1;
|
|
152
|
+
LOG ("best trail position becomes invalid");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
void Walker::save_walker_trail (bool keep) {
|
|
157
|
+
assert (best_trail_pos != -1);
|
|
158
|
+
assert ((size_t) best_trail_pos <= flips.size ());
|
|
159
|
+
// assert (!keep || best_trail_pos == flips.size());
|
|
160
|
+
#ifdef LOGGING
|
|
161
|
+
const size_t size_trail = flips.size ();
|
|
162
|
+
#endif
|
|
163
|
+
const int kept = flips.size () - best_trail_pos;
|
|
164
|
+
LOG ("saving %d values of flipped literals on trail of size %zd",
|
|
165
|
+
best_trail_pos, flips.size ());
|
|
166
|
+
|
|
167
|
+
const auto begin = flips.begin ();
|
|
168
|
+
const auto best = flips.begin () + best_trail_pos;
|
|
169
|
+
const auto end = flips.end ();
|
|
170
|
+
|
|
171
|
+
auto it = begin;
|
|
172
|
+
for (; it != best; ++it) {
|
|
173
|
+
const int lit = *it;
|
|
174
|
+
assert (lit);
|
|
175
|
+
const signed char value = sign (lit);
|
|
176
|
+
const int idx = std::abs (lit);
|
|
177
|
+
best_values[idx] = value;
|
|
178
|
+
}
|
|
179
|
+
if (!keep) {
|
|
180
|
+
LOG ("no need to shift and keep remaining %u literals", kept);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#ifndef NDEBUG
|
|
185
|
+
for (auto v : internal->vars) {
|
|
186
|
+
if (internal->active (v))
|
|
187
|
+
assert (best_values[v] == current_best_model[v]);
|
|
188
|
+
}
|
|
189
|
+
#endif
|
|
190
|
+
LOG ("flushed %u literals %.0f%% from trail", best_trail_pos,
|
|
191
|
+
percent (best_trail_pos, size_trail));
|
|
192
|
+
assert (it == best);
|
|
193
|
+
auto jt = begin;
|
|
194
|
+
for (; it != end; ++it, ++jt) {
|
|
195
|
+
assert (jt <= it);
|
|
196
|
+
assert (it < end);
|
|
197
|
+
*jt = *it;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
assert ((int) (end - jt) == best_trail_pos);
|
|
201
|
+
assert ((int) (jt - begin) == kept);
|
|
202
|
+
flips.resize (kept);
|
|
203
|
+
LOG ("keeping %u literals %.0f%% on trail", kept,
|
|
204
|
+
percent (kept, size_trail));
|
|
205
|
+
LOG ("reset best trail position to 0");
|
|
206
|
+
best_trail_pos = 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// finally export the final minimum
|
|
210
|
+
void Walker::save_final_minimum (int64_t old_init_minimum) {
|
|
211
|
+
assert (minimum <= old_init_minimum);
|
|
212
|
+
#ifdef NDEBUG
|
|
213
|
+
(void) old_init_minimum;
|
|
214
|
+
#endif
|
|
215
|
+
|
|
216
|
+
if (!best_trail_pos || best_trail_pos == -1)
|
|
217
|
+
LOG ("minimum already saved");
|
|
218
|
+
else
|
|
219
|
+
save_walker_trail (false);
|
|
220
|
+
|
|
221
|
+
++internal->stats.walk.improved;
|
|
222
|
+
for (auto v : internal->vars) {
|
|
223
|
+
if (best_values[v])
|
|
224
|
+
internal->phases.saved[v] = best_values[v];
|
|
225
|
+
else
|
|
226
|
+
assert (!internal->active (v));
|
|
227
|
+
}
|
|
228
|
+
internal->copy_phases (internal->phases.prev);
|
|
229
|
+
}
|
|
230
|
+
// The scores are tabulated for faster computation (to avoid 'pow').
|
|
231
|
+
|
|
232
|
+
inline double Walker::score (unsigned i) {
|
|
233
|
+
const double res = (i < table.size () ? table[i] : epsilon);
|
|
234
|
+
LOG ("break %u mapped to score %g", i, res);
|
|
235
|
+
return res;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/*------------------------------------------------------------------------*/
|
|
239
|
+
|
|
240
|
+
ClauseOrBinary Internal::walk_pick_clause (Walker &walker) {
|
|
241
|
+
require_mode (WALK);
|
|
242
|
+
assert (!walker.broken.empty ());
|
|
243
|
+
int64_t size = walker.broken.size ();
|
|
244
|
+
if (size > INT_MAX)
|
|
245
|
+
size = INT_MAX;
|
|
246
|
+
int pos = walker.random.pick_int (0, size - 1);
|
|
247
|
+
ClauseOrBinary res = walker.broken[pos];
|
|
248
|
+
#ifdef LOGGING
|
|
249
|
+
Clause *c;
|
|
250
|
+
if (!res.is_binary ())
|
|
251
|
+
c = res.clause ();
|
|
252
|
+
else
|
|
253
|
+
c = res.tagged_binary ().d;
|
|
254
|
+
LOG (c, "picking random position %d", pos);
|
|
255
|
+
#endif
|
|
256
|
+
return res;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/*------------------------------------------------------------------------*/
|
|
260
|
+
|
|
261
|
+
// Compute the number of clauses which would be become unsatisfied if 'lit'
|
|
262
|
+
// is flipped and set to false. This is called the 'break-count' of 'lit'.
|
|
263
|
+
|
|
264
|
+
unsigned Internal::walk_break_value (int lit, int64_t &ticks) {
|
|
265
|
+
require_mode (WALK);
|
|
266
|
+
START (walkbreak);
|
|
267
|
+
assert (val (lit) > 0);
|
|
268
|
+
const int64_t oldticks = ticks;
|
|
269
|
+
|
|
270
|
+
unsigned res = 0; // The computed break-count of 'lit'.
|
|
271
|
+
ticks += (1 + cache_lines (watches (lit).size (), sizeof (Clause *)));
|
|
272
|
+
|
|
273
|
+
for (auto &w : watches (lit)) {
|
|
274
|
+
assert (w.blit != lit);
|
|
275
|
+
if (val (w.blit) > 0)
|
|
276
|
+
continue;
|
|
277
|
+
if (w.binary ()) {
|
|
278
|
+
res++;
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
Clause *c = w.clause;
|
|
283
|
+
#ifdef LOGGING
|
|
284
|
+
assert (c != dummy_binary);
|
|
285
|
+
#endif
|
|
286
|
+
++ticks;
|
|
287
|
+
|
|
288
|
+
assert (lit == c->literals[0]);
|
|
289
|
+
|
|
290
|
+
// Now try to find a second satisfied literal starting at 'literals[1]'
|
|
291
|
+
// shifting all the traversed literals to right by one position in order
|
|
292
|
+
// to move such a second satisfying literal to 'literals[1]'. This move
|
|
293
|
+
// to front strategy improves the chances to find the second satisfying
|
|
294
|
+
// literal earlier in subsequent break-count computations.
|
|
295
|
+
//
|
|
296
|
+
auto begin = c->begin () + 1;
|
|
297
|
+
const auto end = c->end ();
|
|
298
|
+
auto i = begin;
|
|
299
|
+
int prev = 0;
|
|
300
|
+
while (i != end) {
|
|
301
|
+
const int other = *i;
|
|
302
|
+
*i++ = prev;
|
|
303
|
+
prev = other;
|
|
304
|
+
if (val (other) < 0)
|
|
305
|
+
continue;
|
|
306
|
+
|
|
307
|
+
// Found 'other' as second satisfying literal.
|
|
308
|
+
|
|
309
|
+
w.blit = other; // Update 'blit'
|
|
310
|
+
*begin = other; // and move to front.
|
|
311
|
+
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (i != end)
|
|
316
|
+
continue; // Double satisfied!
|
|
317
|
+
|
|
318
|
+
// Otherwise restore literals (undo shift to the right).
|
|
319
|
+
//
|
|
320
|
+
while (i != begin) {
|
|
321
|
+
const int other = *--i;
|
|
322
|
+
*i = prev;
|
|
323
|
+
prev = other;
|
|
324
|
+
}
|
|
325
|
+
res++; // Literal 'lit' single satisfies clause 'c'.
|
|
326
|
+
}
|
|
327
|
+
stats.ticks.walkbreak += (ticks - oldticks);
|
|
328
|
+
STOP (walkbreak);
|
|
329
|
+
|
|
330
|
+
return res;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/*------------------------------------------------------------------------*/
|
|
334
|
+
|
|
335
|
+
// Given an unsatisfied clause 'c', in which we want to flip a literal, we
|
|
336
|
+
// first determine the exponential score based on the break-count of its
|
|
337
|
+
// literals and then sample the literals based on these scores. The CB
|
|
338
|
+
// value is smaller than one and thus the score is exponentially decreasing
|
|
339
|
+
// with the break-count increasing. The sampling works as in 'ProbSAT' and
|
|
340
|
+
// 'YalSAT' by summing up the scores and then picking a random limit in the
|
|
341
|
+
// range of zero to the sum, then summing up the scores again and picking
|
|
342
|
+
// the first literal which reaches the limit. Note, that during incremental
|
|
343
|
+
// SAT solving we can not flip assumed variables. Those are assigned at
|
|
344
|
+
// decision level one, while the other variables are assigned at two.
|
|
345
|
+
|
|
346
|
+
int Internal::walk_pick_lit (Walker &walker, Clause *c) {
|
|
347
|
+
LOG ("picking literal by break-count");
|
|
348
|
+
assert (walker.scores.empty ());
|
|
349
|
+
const int64_t old = walker.ticks;
|
|
350
|
+
walker.ticks += 1;
|
|
351
|
+
double sum = 0;
|
|
352
|
+
int64_t propagations = 0;
|
|
353
|
+
for (const auto lit : *c) {
|
|
354
|
+
assert (active (lit));
|
|
355
|
+
if (var (lit).level == 1) {
|
|
356
|
+
LOG ("skipping assumption %d for scoring", -lit);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
assert (active (lit));
|
|
360
|
+
propagations++;
|
|
361
|
+
unsigned tmp = walk_break_value (-lit, walker.ticks);
|
|
362
|
+
double score = walker.score (tmp);
|
|
363
|
+
LOG ("literal %d break-count %u score %g", lit, tmp, score);
|
|
364
|
+
walker.scores.push_back (score);
|
|
365
|
+
sum += score;
|
|
366
|
+
}
|
|
367
|
+
(void) propagations; // TODO actually unused?
|
|
368
|
+
LOG ("scored %zd literals", walker.scores.size ());
|
|
369
|
+
assert (!walker.scores.empty ());
|
|
370
|
+
assert (walker.scores.size () <= (size_t) c->size);
|
|
371
|
+
const double lim = sum * walker.random.generate_double ();
|
|
372
|
+
LOG ("score sum %g limit %g", sum, lim);
|
|
373
|
+
const auto end = c->end ();
|
|
374
|
+
auto i = c->begin ();
|
|
375
|
+
auto j = walker.scores.begin ();
|
|
376
|
+
int res;
|
|
377
|
+
for (;;) {
|
|
378
|
+
assert (i != end);
|
|
379
|
+
res = *i++;
|
|
380
|
+
if (var (res).level > 1)
|
|
381
|
+
break;
|
|
382
|
+
LOG ("skipping assumption %d without score", -res);
|
|
383
|
+
}
|
|
384
|
+
sum = *j++;
|
|
385
|
+
while (sum <= lim && i != end) {
|
|
386
|
+
res = *i++;
|
|
387
|
+
if (var (res).level == 1) {
|
|
388
|
+
LOG ("skipping assumption %d without score", -res);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
sum += *j++;
|
|
392
|
+
}
|
|
393
|
+
walker.scores.clear ();
|
|
394
|
+
LOG ("picking literal %d by break-count", res);
|
|
395
|
+
stats.ticks.walkpick += walker.ticks - old;
|
|
396
|
+
return res;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
int Internal::walk_pick_lit (Walker &walker, ClauseOrBinary c) {
|
|
400
|
+
if (c.is_binary ())
|
|
401
|
+
return walk_pick_lit (walker, c.tagged_binary ());
|
|
402
|
+
return walk_pick_lit (walker, c.clause ());
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
int Internal::walk_pick_lit (Walker &walker, const TaggedBinary c) {
|
|
406
|
+
LOG ("picking literal by break-count on binary clause [%" PRIu64 "]%s %s",
|
|
407
|
+
c.d->id, LOGLIT (c.lit), LOGLIT (c.other));
|
|
408
|
+
assert (walker.scores.empty ());
|
|
409
|
+
const int64_t old = walker.ticks;
|
|
410
|
+
double sum = 0;
|
|
411
|
+
int64_t propagations = 0;
|
|
412
|
+
const std::array<int, 2> clause = {c.lit, c.other};
|
|
413
|
+
for (const auto lit : clause) {
|
|
414
|
+
assert (active (lit));
|
|
415
|
+
if (var (lit).level == 1) {
|
|
416
|
+
LOG ("skipping assumption %d for scoring", -lit);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
assert (active (lit));
|
|
420
|
+
assert (val (lit) < 0);
|
|
421
|
+
propagations++;
|
|
422
|
+
unsigned tmp = walk_break_value (-lit, walker.ticks);
|
|
423
|
+
double score = walker.score (tmp);
|
|
424
|
+
LOG ("literal %d break-count %u score %g", lit, tmp, score);
|
|
425
|
+
walker.scores.push_back (score);
|
|
426
|
+
sum += score;
|
|
427
|
+
}
|
|
428
|
+
(void) propagations; // TODO unused?
|
|
429
|
+
LOG ("scored %zd literals", walker.scores.size ());
|
|
430
|
+
assert (!walker.scores.empty ());
|
|
431
|
+
assert (walker.scores.size () <= (size_t) 2);
|
|
432
|
+
const double lim = sum * walker.random.generate_double ();
|
|
433
|
+
LOG ("score sum %g limit %g", sum, lim);
|
|
434
|
+
const auto end = clause.end ();
|
|
435
|
+
auto i = clause.begin ();
|
|
436
|
+
auto j = walker.scores.begin ();
|
|
437
|
+
int res = 0;
|
|
438
|
+
for (;;) {
|
|
439
|
+
assert (i != end);
|
|
440
|
+
res = *i++;
|
|
441
|
+
if (var (res).level > 1)
|
|
442
|
+
break;
|
|
443
|
+
LOG ("skipping assumption %d without score", -res);
|
|
444
|
+
}
|
|
445
|
+
sum = *j++;
|
|
446
|
+
while (sum <= lim && i != end) {
|
|
447
|
+
res = *i++;
|
|
448
|
+
if (var (res).level == 1) {
|
|
449
|
+
LOG ("skipping assumption %d without score", -res);
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
sum += *j++;
|
|
453
|
+
}
|
|
454
|
+
assert (res);
|
|
455
|
+
walker.scores.clear ();
|
|
456
|
+
LOG ("picking literal %d by break-count", res);
|
|
457
|
+
stats.ticks.walkpick += walker.ticks - old;
|
|
458
|
+
return res;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/*------------------------------------------------------------------------*/
|
|
462
|
+
|
|
463
|
+
// flips a literal unless we run out of ticks.
|
|
464
|
+
bool Internal::walk_flip_lit (Walker &walker, int lit) {
|
|
465
|
+
START (walkflip);
|
|
466
|
+
const int64_t old = walker.ticks;
|
|
467
|
+
require_mode (WALK);
|
|
468
|
+
LOG ("flipping assign %d", lit);
|
|
469
|
+
assert (val (lit) < 0);
|
|
470
|
+
|
|
471
|
+
// First flip the literal value.
|
|
472
|
+
//
|
|
473
|
+
const int tmp = sign (lit);
|
|
474
|
+
const int idx = abs (lit);
|
|
475
|
+
set_val (idx, tmp);
|
|
476
|
+
assert (val (lit) > 0);
|
|
477
|
+
|
|
478
|
+
// we are going to need it anyway and it probably still is in memory
|
|
479
|
+
const Watches &ws = watches (-lit);
|
|
480
|
+
if (!ws.empty ()) {
|
|
481
|
+
const Watch &w = ws[0];
|
|
482
|
+
__builtin_prefetch (&w, 0, 1);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Then remove 'c' and all other now satisfied (made) clauses.
|
|
486
|
+
{
|
|
487
|
+
// Simply go over all unsatisfied (broken) clauses.
|
|
488
|
+
|
|
489
|
+
LOG ("trying to make %zd broken clauses", walker.broken.size ());
|
|
490
|
+
|
|
491
|
+
const auto eou = walker.broken.end ();
|
|
492
|
+
// broken is in cache given how central it is... but not always (see the
|
|
493
|
+
// ncc problems). Value was heuristically determined to give reasonnable
|
|
494
|
+
// values.
|
|
495
|
+
walker.ticks +=
|
|
496
|
+
1 + cache_lines (walker.broken.size (), sizeof (Clause *));
|
|
497
|
+
auto j = walker.broken.begin (), i = j;
|
|
498
|
+
#if defined(LOGGING) || !defined(NDEBUG)
|
|
499
|
+
int64_t made = 0;
|
|
500
|
+
#endif
|
|
501
|
+
|
|
502
|
+
while (i != eou) {
|
|
503
|
+
|
|
504
|
+
ClauseOrBinary tagged = *j++ = *i++;
|
|
505
|
+
|
|
506
|
+
if (tagged.is_binary ()) {
|
|
507
|
+
const TaggedBinary &b = tagged.tagged_binary ();
|
|
508
|
+
const int clit = b.lit;
|
|
509
|
+
const int other = b.other;
|
|
510
|
+
assert (val (clit) < 0 || val (other) < 0);
|
|
511
|
+
#if defined(LOGGING)
|
|
512
|
+
assert (b.d->literals[0] == clit || b.d->literals[1] == clit);
|
|
513
|
+
assert (b.d->literals[0] == other || b.d->literals[1] == other);
|
|
514
|
+
#endif
|
|
515
|
+
if (clit == lit || other == lit) {
|
|
516
|
+
LOG (b.d, "made");
|
|
517
|
+
const int first_lit = lit;
|
|
518
|
+
const int second_lit = clit ^ lit ^ other;
|
|
519
|
+
#ifdef LOGGING
|
|
520
|
+
watch_binary_literal (first_lit, second_lit, b.d);
|
|
521
|
+
#else
|
|
522
|
+
// placeholder for the clause, does not matter
|
|
523
|
+
watch_binary_literal (first_lit, second_lit, dummy_binary);
|
|
524
|
+
#endif
|
|
525
|
+
|
|
526
|
+
++walker.ticks;
|
|
527
|
+
#if defined(LOGGING) || !defined(NDEBUG)
|
|
528
|
+
made++;
|
|
529
|
+
#endif
|
|
530
|
+
j--;
|
|
531
|
+
} else {
|
|
532
|
+
LOG (b.d, "still broken");
|
|
533
|
+
assert (val (clit) < 0 && val (other) < 0);
|
|
534
|
+
}
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// now the expansive part
|
|
539
|
+
Clause *d = tagged.clause ();
|
|
540
|
+
++walker.ticks;
|
|
541
|
+
int *literals = d->literals;
|
|
542
|
+
LOG (d, "search for replacement");
|
|
543
|
+
int prev = 0;
|
|
544
|
+
// Find 'lit' in 'd'.
|
|
545
|
+
//
|
|
546
|
+
const int size = d->size;
|
|
547
|
+
for (int i = 0; i < size; i++) {
|
|
548
|
+
const int other = literals[i];
|
|
549
|
+
assert (active (other));
|
|
550
|
+
literals[i] = prev;
|
|
551
|
+
prev = other;
|
|
552
|
+
if (other == lit)
|
|
553
|
+
break;
|
|
554
|
+
assert (val (other) < 0);
|
|
555
|
+
}
|
|
556
|
+
// If 'lit' is in 'd' then move it to the front to watch it.
|
|
557
|
+
//
|
|
558
|
+
if (prev == lit) {
|
|
559
|
+
literals[0] = lit;
|
|
560
|
+
LOG (d, "made");
|
|
561
|
+
watch_literal (literals[0], literals[1], d);
|
|
562
|
+
++walker.ticks;
|
|
563
|
+
#if defined(LOGGING) || !defined(NDEBUG)
|
|
564
|
+
made++;
|
|
565
|
+
#endif
|
|
566
|
+
j--;
|
|
567
|
+
} else { // Otherwise the clause is not satisfied, undo shift.
|
|
568
|
+
|
|
569
|
+
for (int i = size - 1; i >= 0; i--) {
|
|
570
|
+
int other = literals[i];
|
|
571
|
+
literals[i] = prev;
|
|
572
|
+
prev = other;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
LOG (d, "clause after undoing shift");
|
|
576
|
+
}
|
|
577
|
+
assert ((int64_t) (j - walker.broken.begin ()) + made ==
|
|
578
|
+
(int64_t) walker.broken.size ());
|
|
579
|
+
walker.broken.resize (j - walker.broken.begin ());
|
|
580
|
+
LOG ("made %" PRId64 " clauses by flipping %d, still %zu broken", made,
|
|
581
|
+
lit, walker.broken.size ());
|
|
582
|
+
#ifndef NDEBUG
|
|
583
|
+
for (auto d : walker.broken) {
|
|
584
|
+
if (d.is_binary ()) {
|
|
585
|
+
const TaggedBinary &b = d.tagged_binary ();
|
|
586
|
+
assert (val (b.lit) < 0 && val (b.other) < 0);
|
|
587
|
+
} else {
|
|
588
|
+
for (auto lit : *d.clause ())
|
|
589
|
+
assert (val (lit) < 0);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
#endif
|
|
593
|
+
if (walker.ticks > walker.limit) {
|
|
594
|
+
STOP (walkflip);
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
stats.ticks.walkflipbroken += walker.ticks - old;
|
|
600
|
+
|
|
601
|
+
const int64_t old_after_broken = walker.ticks;
|
|
602
|
+
|
|
603
|
+
// Finally add all new unsatisfied (broken) clauses.
|
|
604
|
+
{
|
|
605
|
+
#ifdef LOGGING
|
|
606
|
+
int64_t broken = 0;
|
|
607
|
+
#endif
|
|
608
|
+
Watches &ws = watches (-lit);
|
|
609
|
+
// probably still in cache
|
|
610
|
+
walker.ticks += 1 + cache_lines (ws.size (), sizeof (Clause *));
|
|
611
|
+
|
|
612
|
+
LOG ("trying to break %zd watched clauses", ws.size ());
|
|
613
|
+
|
|
614
|
+
for (const auto &w : ws) {
|
|
615
|
+
Clause *d = w.clause;
|
|
616
|
+
const bool binary = w.binary ();
|
|
617
|
+
if (binary) {
|
|
618
|
+
const int other = w.blit;
|
|
619
|
+
assert (w.blit != -lit);
|
|
620
|
+
if (val (other) > 0) {
|
|
621
|
+
LOG (d, "unwatch %d in", -lit);
|
|
622
|
+
watch_binary_literal (other, -lit, d);
|
|
623
|
+
++walker.ticks;
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
LOG (d, "broken");
|
|
627
|
+
#ifdef LOGGING
|
|
628
|
+
assert (d != dummy_binary);
|
|
629
|
+
#endif
|
|
630
|
+
walker.broken.push_back (TaggedBinary (d, -lit, other));
|
|
631
|
+
++walker.ticks;
|
|
632
|
+
#ifdef LOGGING
|
|
633
|
+
broken++;
|
|
634
|
+
#endif
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (walker.ticks > walker.limit) {
|
|
639
|
+
STOP (walkflip);
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
// now the expansive part
|
|
643
|
+
assert (d->size != 2);
|
|
644
|
+
++walker.ticks;
|
|
645
|
+
int *literals = d->literals, replacement = 0, prev = -lit;
|
|
646
|
+
assert (d->size == w.size);
|
|
647
|
+
const int size = d->size;
|
|
648
|
+
assert (literals[0] == -lit);
|
|
649
|
+
|
|
650
|
+
for (int i = 1; i < size; i++) {
|
|
651
|
+
const int other = literals[i];
|
|
652
|
+
assert (active (other));
|
|
653
|
+
literals[i] = prev; // shift all to right
|
|
654
|
+
prev = other;
|
|
655
|
+
const signed char tmp = val (other);
|
|
656
|
+
if (tmp < 0)
|
|
657
|
+
continue;
|
|
658
|
+
replacement = other; // satisfying literal
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
if (replacement) {
|
|
662
|
+
assert (-lit != replacement);
|
|
663
|
+
literals[1] = -lit;
|
|
664
|
+
literals[0] = replacement;
|
|
665
|
+
watch_literal (replacement, -lit, d);
|
|
666
|
+
++walker.ticks;
|
|
667
|
+
LOG (d, "found replacement");
|
|
668
|
+
} else {
|
|
669
|
+
for (int i = size - 1; i > 0; i--) { // undo shift
|
|
670
|
+
const int other = literals[i];
|
|
671
|
+
literals[i] = prev;
|
|
672
|
+
prev = other;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
assert (literals[0] == -lit);
|
|
676
|
+
LOG (d, "broken");
|
|
677
|
+
walker.broken.push_back (d);
|
|
678
|
+
++walker.ticks;
|
|
679
|
+
#ifdef LOGGING
|
|
680
|
+
broken++;
|
|
681
|
+
#endif
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit);
|
|
685
|
+
ws.clear ();
|
|
686
|
+
}
|
|
687
|
+
STOP (walkflip);
|
|
688
|
+
stats.ticks.walkflipWL += walker.ticks - old_after_broken;
|
|
689
|
+
stats.ticks.walkflip += walker.ticks - old;
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/*------------------------------------------------------------------------*/
|
|
694
|
+
|
|
695
|
+
// Check whether to save the current phases as new global minimum.
|
|
696
|
+
|
|
697
|
+
inline void Internal::walk_save_minimum (Walker &walker) {
|
|
698
|
+
int64_t broken = walker.broken.size ();
|
|
699
|
+
if (broken >= walker.minimum)
|
|
700
|
+
return;
|
|
701
|
+
if (broken <= stats.walk.minimum) {
|
|
702
|
+
stats.walk.minimum = broken;
|
|
703
|
+
VERBOSE (3, "new global minimum %" PRId64 "", broken);
|
|
704
|
+
} else {
|
|
705
|
+
VERBOSE (3, "new walk minimum %" PRId64 "", broken);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
walker.minimum = broken;
|
|
709
|
+
|
|
710
|
+
#ifndef NDEBUG
|
|
711
|
+
for (auto i : vars) {
|
|
712
|
+
const signed char tmp = vals[i];
|
|
713
|
+
if (tmp)
|
|
714
|
+
walker.current_best_model[i] = tmp;
|
|
715
|
+
}
|
|
716
|
+
if (walker.minimum == 0) {
|
|
717
|
+
for (auto c : clauses) {
|
|
718
|
+
if (c->garbage)
|
|
719
|
+
continue;
|
|
720
|
+
if (c->redundant)
|
|
721
|
+
continue;
|
|
722
|
+
int satisfied = 0;
|
|
723
|
+
for (const auto &lit : *c) {
|
|
724
|
+
const int tmp = internal->val (lit);
|
|
725
|
+
if (tmp > 0) {
|
|
726
|
+
LOG (c, "satisfied literal %d in", lit);
|
|
727
|
+
satisfied++;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
assert (satisfied);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
#endif
|
|
734
|
+
if (walker.best_trail_pos == -1) {
|
|
735
|
+
VERBOSE (3, "saving the new walk minimum %" PRId64 "", broken);
|
|
736
|
+
for (auto i : vars) {
|
|
737
|
+
const signed char tmp = vals[i];
|
|
738
|
+
if (tmp) {
|
|
739
|
+
walker.best_values[i] = tmp;
|
|
740
|
+
#ifndef NDEBUG
|
|
741
|
+
assert (tmp == walker.current_best_model[i]);
|
|
742
|
+
#endif
|
|
743
|
+
} else {
|
|
744
|
+
assert (!active (i));
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
walker.best_trail_pos = 0;
|
|
748
|
+
} else {
|
|
749
|
+
walker.best_trail_pos = walker.flips.size ();
|
|
750
|
+
LOG ("new best trail position %u", walker.best_trail_pos);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/*------------------------------------------------------------------------*/
|
|
755
|
+
|
|
756
|
+
int Internal::walk_round (int64_t limit, bool prev) {
|
|
757
|
+
|
|
758
|
+
stats.walk.count++;
|
|
759
|
+
|
|
760
|
+
clear_watches ();
|
|
761
|
+
|
|
762
|
+
// Remove all fixed variables first (assigned at decision level zero).
|
|
763
|
+
//
|
|
764
|
+
if (last.collect.fixed < stats.all.fixed)
|
|
765
|
+
garbage_collection ();
|
|
766
|
+
|
|
767
|
+
#ifndef QUIET
|
|
768
|
+
// We want to see more messages during initial local search.
|
|
769
|
+
//
|
|
770
|
+
if (localsearching) {
|
|
771
|
+
assert (!force_phase_messages);
|
|
772
|
+
force_phase_messages = true;
|
|
773
|
+
}
|
|
774
|
+
#endif
|
|
775
|
+
|
|
776
|
+
PHASE ("walk", stats.walk.count, "random walk limit of %" PRId64 " ticks",
|
|
777
|
+
limit);
|
|
778
|
+
|
|
779
|
+
// Instantiate data structures for this local search round.
|
|
780
|
+
//
|
|
781
|
+
Walker walker (internal, limit);
|
|
782
|
+
#ifndef QUIET
|
|
783
|
+
int old_global_minimum = stats.walk.minimum;
|
|
784
|
+
#endif
|
|
785
|
+
|
|
786
|
+
bool failed = false; // Inconsistent assumptions?
|
|
787
|
+
|
|
788
|
+
level = 1; // Assumed variables assigned at level 1.
|
|
789
|
+
|
|
790
|
+
if (assumptions.empty ()) {
|
|
791
|
+
LOG ("no assumptions so assigning all variables to decision phase");
|
|
792
|
+
} else {
|
|
793
|
+
LOG ("assigning assumptions to their forced phase first");
|
|
794
|
+
for (const auto lit : assumptions) {
|
|
795
|
+
signed char tmp = val (lit);
|
|
796
|
+
if (tmp > 0)
|
|
797
|
+
continue;
|
|
798
|
+
if (tmp < 0) {
|
|
799
|
+
LOG ("inconsistent assumption %d", lit);
|
|
800
|
+
failed = true;
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
if (!active (lit))
|
|
804
|
+
continue;
|
|
805
|
+
tmp = sign (lit);
|
|
806
|
+
const int idx = abs (lit);
|
|
807
|
+
LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx);
|
|
808
|
+
set_val (idx, tmp);
|
|
809
|
+
assert (level == 1);
|
|
810
|
+
var (idx).level = 1;
|
|
811
|
+
}
|
|
812
|
+
if (!failed)
|
|
813
|
+
LOG ("now assigning remaining variables to their decision phase");
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
level = 2; // All other non assumed variables assigned at level 2.
|
|
817
|
+
|
|
818
|
+
if (!failed) {
|
|
819
|
+
|
|
820
|
+
// warmup stores the result in phases, not in target
|
|
821
|
+
const bool target = opts.warmup ? false : stable || opts.target == 2;
|
|
822
|
+
for (auto idx : vars) {
|
|
823
|
+
if (!active (idx)) {
|
|
824
|
+
LOG ("skipping inactive variable %d", idx);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (vals[idx]) {
|
|
828
|
+
assert (var (idx).level == 1);
|
|
829
|
+
LOG ("skipping assumed variable %d", idx);
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
int tmp = 0;
|
|
833
|
+
if (prev)
|
|
834
|
+
tmp = phases.prev[idx];
|
|
835
|
+
if (!tmp)
|
|
836
|
+
tmp = sign (decide_phase (idx, target));
|
|
837
|
+
assert (tmp == 1 || tmp == -1);
|
|
838
|
+
set_val (idx, tmp);
|
|
839
|
+
assert (level == 2);
|
|
840
|
+
var (idx).level = 2;
|
|
841
|
+
LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
LOG ("watching satisfied and registering broken clauses");
|
|
845
|
+
#ifdef LOGGING
|
|
846
|
+
int64_t watched = 0;
|
|
847
|
+
#endif
|
|
848
|
+
|
|
849
|
+
double size = 0;
|
|
850
|
+
int64_t n = 0;
|
|
851
|
+
for (const auto c : clauses) {
|
|
852
|
+
|
|
853
|
+
if (c->garbage)
|
|
854
|
+
continue;
|
|
855
|
+
if (c->redundant) {
|
|
856
|
+
if (!opts.walkredundant)
|
|
857
|
+
continue;
|
|
858
|
+
if (!likely_to_be_kept_clause (c))
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
bool satisfiable = false; // contains not only assumptions
|
|
863
|
+
int satisfied = 0; // clause satisfied?
|
|
864
|
+
|
|
865
|
+
int *lits = c->literals;
|
|
866
|
+
size += c->size;
|
|
867
|
+
n++;
|
|
868
|
+
const int size = c->size;
|
|
869
|
+
|
|
870
|
+
// Move to front satisfied literals and determine whether there
|
|
871
|
+
// is at least one (non-assumed) literal that can be flipped.
|
|
872
|
+
//
|
|
873
|
+
for (int i = 0; satisfied < 2 && i < size; i++) {
|
|
874
|
+
const int lit = lits[i];
|
|
875
|
+
assert (active (lit)); // Due to garbage collection.
|
|
876
|
+
if (val (lit) > 0) {
|
|
877
|
+
swap (lits[satisfied], lits[i]);
|
|
878
|
+
if (!satisfied++)
|
|
879
|
+
LOG ("first satisfying literal %d", lit);
|
|
880
|
+
} else if (!satisfiable && var (lit).level > 1) {
|
|
881
|
+
LOG ("non-assumption potentially satisfying literal %d", lit);
|
|
882
|
+
satisfiable = true;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (!satisfied && !satisfiable) {
|
|
887
|
+
LOG (c, "due to assumptions unsatisfiable");
|
|
888
|
+
LOG ("stopping local search since assumptions falsify a clause");
|
|
889
|
+
failed = true;
|
|
890
|
+
break;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
if (satisfied) {
|
|
894
|
+
LOG (c, "pushing to satisfied");
|
|
895
|
+
if (c->size == 2)
|
|
896
|
+
watch_binary_literal (lits[0], lits[1], c);
|
|
897
|
+
else
|
|
898
|
+
watch_literal (lits[0], lits[1], c);
|
|
899
|
+
#ifdef LOGGING
|
|
900
|
+
watched++;
|
|
901
|
+
#endif
|
|
902
|
+
} else {
|
|
903
|
+
assert (satisfiable); // at least one non-assumed variable ...
|
|
904
|
+
LOG (c, "broken");
|
|
905
|
+
assert (c->size == size);
|
|
906
|
+
if (size == 2)
|
|
907
|
+
walker.broken.push_back (TaggedBinary (c));
|
|
908
|
+
else
|
|
909
|
+
walker.broken.push_back (c);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
double average_size = relative (size, n);
|
|
914
|
+
walker.populate_table (average_size);
|
|
915
|
+
PHASE ("walk", stats.walk.count,
|
|
916
|
+
"%" PRId64 " clauses average size %.2f over %d variables", n,
|
|
917
|
+
average_size, active ());
|
|
918
|
+
|
|
919
|
+
#ifdef LOGGING
|
|
920
|
+
if (!failed) {
|
|
921
|
+
int64_t broken = walker.broken.size ();
|
|
922
|
+
int64_t total = watched + broken;
|
|
923
|
+
LOG ("watching %" PRId64 " clauses %.0f%% "
|
|
924
|
+
"out of %" PRId64 " (watched and broken)",
|
|
925
|
+
watched, percent (watched, total), total);
|
|
926
|
+
}
|
|
927
|
+
#endif
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
assert (failed || walker.table.size ());
|
|
931
|
+
|
|
932
|
+
int res; // Tells caller to continue with local search.
|
|
933
|
+
|
|
934
|
+
if (!failed) {
|
|
935
|
+
|
|
936
|
+
int64_t broken = walker.broken.size ();
|
|
937
|
+
int64_t initial_minimum = broken;
|
|
938
|
+
|
|
939
|
+
PHASE ("walk", stats.walk.count,
|
|
940
|
+
"starting with %" PRId64 " unsatisfied clauses "
|
|
941
|
+
"(%.0f%% out of %" PRId64 ")",
|
|
942
|
+
broken, percent (broken, stats.current.irredundant),
|
|
943
|
+
stats.current.irredundant);
|
|
944
|
+
|
|
945
|
+
walk_save_minimum (walker);
|
|
946
|
+
assert (stats.walk.minimum <= walker.minimum);
|
|
947
|
+
|
|
948
|
+
int64_t minimum = broken;
|
|
949
|
+
#ifndef QUIET
|
|
950
|
+
int64_t flips = 0;
|
|
951
|
+
#endif
|
|
952
|
+
while (!terminated_asynchronously () && !walker.broken.empty () &&
|
|
953
|
+
walker.ticks < walker.limit) {
|
|
954
|
+
#ifndef QUIET
|
|
955
|
+
flips++;
|
|
956
|
+
#endif
|
|
957
|
+
stats.walk.flips++;
|
|
958
|
+
stats.walk.broken += broken;
|
|
959
|
+
ClauseOrBinary c = walk_pick_clause (walker);
|
|
960
|
+
const int lit = walk_pick_lit (walker, c);
|
|
961
|
+
bool finished = walk_flip_lit (walker, lit);
|
|
962
|
+
if (!finished)
|
|
963
|
+
break;
|
|
964
|
+
walker.push_flipped (lit);
|
|
965
|
+
broken = walker.broken.size ();
|
|
966
|
+
LOG ("now have %" PRId64 " broken clauses in total", broken);
|
|
967
|
+
if (broken >= minimum)
|
|
968
|
+
continue;
|
|
969
|
+
minimum = broken;
|
|
970
|
+
VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips",
|
|
971
|
+
minimum, flips);
|
|
972
|
+
walk_save_minimum (walker);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
walker.save_final_minimum (initial_minimum);
|
|
976
|
+
|
|
977
|
+
#ifndef QUIET
|
|
978
|
+
if (minimum == initial_minimum) {
|
|
979
|
+
PHASE ("walk", internal->stats.walk.count,
|
|
980
|
+
"%sno improvement %" PRId64 "%s in %" PRId64 " flips and "
|
|
981
|
+
"%" PRId64 " ticks",
|
|
982
|
+
tout.bright_yellow_code (), minimum, tout.normal_code (),
|
|
983
|
+
flips, walker.ticks);
|
|
984
|
+
} else if (minimum < old_global_minimum)
|
|
985
|
+
PHASE ("walk", stats.walk.count,
|
|
986
|
+
"%snew global minimum %" PRId64 "%s in %" PRId64 " flips and "
|
|
987
|
+
"%" PRId64 " ticks",
|
|
988
|
+
tout.bright_yellow_code (), minimum, tout.normal_code (),
|
|
989
|
+
flips, walker.ticks);
|
|
990
|
+
else
|
|
991
|
+
PHASE ("walk", stats.walk.count,
|
|
992
|
+
"best phase minimum %" PRId64 " in %" PRId64 " flips and "
|
|
993
|
+
"%" PRId64 " ticks",
|
|
994
|
+
minimum, flips, walker.ticks);
|
|
995
|
+
|
|
996
|
+
if (opts.profile >= 2) {
|
|
997
|
+
PHASE ("walk", stats.walk.count, "%.2f million ticks per second",
|
|
998
|
+
1e-6 *
|
|
999
|
+
relative (walker.ticks, time () - profiles.walk.started));
|
|
1000
|
+
|
|
1001
|
+
PHASE ("walk", stats.walk.count, "%.2f thousand flips per second",
|
|
1002
|
+
relative (1e-3 * flips, time () - profiles.walk.started));
|
|
1003
|
+
|
|
1004
|
+
} else {
|
|
1005
|
+
PHASE ("walk", stats.walk.count, "%.2f ticks", 1e-6 * walker.ticks);
|
|
1006
|
+
|
|
1007
|
+
PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips);
|
|
1008
|
+
}
|
|
1009
|
+
#endif
|
|
1010
|
+
|
|
1011
|
+
if (minimum > 0) {
|
|
1012
|
+
LOG ("minimum %" PRId64 " non-zero thus potentially continue",
|
|
1013
|
+
minimum);
|
|
1014
|
+
res = 0;
|
|
1015
|
+
} else {
|
|
1016
|
+
LOG ("minimum is zero thus stop local search");
|
|
1017
|
+
res = 10;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
} else {
|
|
1021
|
+
|
|
1022
|
+
res = 20;
|
|
1023
|
+
|
|
1024
|
+
PHASE ("walk", stats.walk.count,
|
|
1025
|
+
"aborted due to inconsistent assumptions");
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
for (auto idx : vars)
|
|
1029
|
+
if (active (idx))
|
|
1030
|
+
set_val (idx, 0);
|
|
1031
|
+
|
|
1032
|
+
assert (level == 2);
|
|
1033
|
+
level = 0;
|
|
1034
|
+
|
|
1035
|
+
clear_watches ();
|
|
1036
|
+
connect_watches ();
|
|
1037
|
+
|
|
1038
|
+
#ifndef QUIET
|
|
1039
|
+
if (localsearching) {
|
|
1040
|
+
assert (force_phase_messages);
|
|
1041
|
+
force_phase_messages = false;
|
|
1042
|
+
}
|
|
1043
|
+
#endif
|
|
1044
|
+
stats.ticks.walk += walker.ticks;
|
|
1045
|
+
return res;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
void Internal::walk () {
|
|
1049
|
+
START_INNER_WALK ();
|
|
1050
|
+
|
|
1051
|
+
backtrack ();
|
|
1052
|
+
if (propagated < trail.size () && !propagate ()) {
|
|
1053
|
+
LOG ("empty clause after root level propagation");
|
|
1054
|
+
learn_empty_clause ();
|
|
1055
|
+
STOP_INNER_WALK ();
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
int res = 0;
|
|
1060
|
+
if (opts.warmup)
|
|
1061
|
+
res = warmup ();
|
|
1062
|
+
if (res) {
|
|
1063
|
+
LOG ("stopping walk due to warmup");
|
|
1064
|
+
STOP_INNER_WALK ();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
const int64_t ticks = stats.ticks.search[0] + stats.ticks.search[1];
|
|
1068
|
+
int64_t limit = ticks - last.walk.ticks;
|
|
1069
|
+
VERBOSE (2,
|
|
1070
|
+
"walk scheduling: last %" PRId64 " current %" PRId64
|
|
1071
|
+
" delta %" PRId64,
|
|
1072
|
+
last.walk.ticks, ticks, limit);
|
|
1073
|
+
last.walk.ticks = ticks;
|
|
1074
|
+
limit *= 1e-3 * opts.walkeffort;
|
|
1075
|
+
if (limit < opts.walkmineff)
|
|
1076
|
+
limit = opts.walkmineff;
|
|
1077
|
+
// local search is very cache friendly, so we actually really go over a
|
|
1078
|
+
// lot of ticks
|
|
1079
|
+
if (limit > 1e3 * opts.walkmaxeff) {
|
|
1080
|
+
MSG ("reached maximum efficiency %" PRId64, limit);
|
|
1081
|
+
limit = 1e3 * opts.walkmaxeff;
|
|
1082
|
+
}
|
|
1083
|
+
(void) walk_round (limit, false);
|
|
1084
|
+
STOP_INNER_WALK ();
|
|
1085
|
+
assert (!unsat);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
} // namespace CaDiCaL
|