ABC: A System for Sequential Synthesis and Verification
 
Loading...
Searching...
No Matches
cadical_ternary.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// This procedure can be used to produce all possible hyper ternary
12// resolvents from ternary clauses. In contrast to hyper binary resolution
13// we would only try to produce ternary clauses from ternary clauses, i.e.,
14// do not consider quaternary clauses as antecedents. Of course if a binary
15// clause is generated we keep it too. In any case we have to make sure
16// though that we do not add clauses which are already in the formula or are
17// subsumed by a binary clause in the formula. This procedure simulates
18// structural hashing for multiplexer (if-then-else) and binary XOR gates in
19// combination with equivalent literal substitution ('decompose') if run
20// until to completion (which in the current implementation is too costly
21// though and would need to be interleaved more eagerly with equivalent
22// literal substitution). For more information see our CPAIOR'13 paper.
23
24/*------------------------------------------------------------------------*/
25
26// Check whether a binary clause consisting of the permutation of the given
27// literals already exists.
28
33 size_t s = occs (a).size ();
34 size_t t = occs (b).size ();
35 int lit = s < t ? a : b;
36 if (opts.ternaryocclim < (int) occs (lit).size ())
37 return true;
38 for (const auto &c : occs (lit)) {
39 if (c->size != 2)
40 continue;
41 const int *lits = c->literals;
42 if (lits[0] == a && lits[1] == b)
43 return true;
44 if (lits[0] == b && lits[1] == a)
45 return true;
46 }
47 return false;
48}
49
50/*------------------------------------------------------------------------*/
51
52// Check whether a ternary clause consisting of the permutation of the given
53// literals already exists or is subsumed by an existing binary clause.
54
55bool Internal::ternary_find_ternary_clause (int a, int b, int c) {
60 size_t r = occs (a).size ();
61 size_t s = occs (b).size ();
62 size_t t = occs (c).size ();
63 int lit;
64 if (r < s)
65 lit = (t < r) ? c : a;
66 else
67 lit = (t < s) ? c : b;
68 if (opts.ternaryocclim < (int) occs (lit).size ())
69 return true;
70 for (const auto &d : occs (lit)) {
71 const int *lits = d->literals;
72 if (d->size == 2) {
73 if (lits[0] == a && lits[1] == b)
74 return true;
75 if (lits[0] == b && lits[1] == a)
76 return true;
77 if (lits[0] == a && lits[1] == c)
78 return true;
79 if (lits[0] == c && lits[1] == a)
80 return true;
81 if (lits[0] == b && lits[1] == c)
82 return true;
83 if (lits[0] == c && lits[1] == b)
84 return true;
85 } else {
86 CADICAL_assert (d->size == 3);
87 if (lits[0] == a && lits[1] == b && lits[2] == c)
88 return true;
89 if (lits[0] == a && lits[1] == c && lits[2] == b)
90 return true;
91 if (lits[0] == b && lits[1] == a && lits[2] == c)
92 return true;
93 if (lits[0] == b && lits[1] == c && lits[2] == a)
94 return true;
95 if (lits[0] == c && lits[1] == a && lits[2] == b)
96 return true;
97 if (lits[0] == c && lits[1] == b && lits[2] == a)
98 return true;
99 }
100 }
101 return false;
102}
103
104/*------------------------------------------------------------------------*/
105
106// Try to resolve the two ternary clauses on the given pivot (assumed to
107// occur positively in the first clause, negatively in the second). If the
108// resolvent has four literals, is tautological, already exists or in the
109// case of a ternary resolvent is subsumed by an existing binary clause then
110// 'false' is returned. The global 'clause' contains the resolvent and
111// needs to be cleared in any case.
112
114 LOG ("hyper binary resolving on pivot %d", pivot);
115 LOG (c, "1st antecedent");
116 LOG (d, "2nd antecedent");
117 stats.ternres++;
118 CADICAL_assert (c->size == 3);
119 CADICAL_assert (d->size == 3);
120 CADICAL_assert (clause.empty ());
121 for (const auto &lit : *c)
122 if (lit != pivot)
123 clause.push_back (lit);
124 for (const auto &lit : *d) {
125 if (lit == -pivot)
126 continue;
127 if (lit == clause[0])
128 continue;
129 if (lit == -clause[0])
130 return false;
131 if (lit == clause[1])
132 continue;
133 if (lit == -clause[1])
134 return false;
135 clause.push_back (lit);
136 }
137 size_t size = clause.size ();
138 if (size > 3)
139 return false;
140 if (size == 2 && ternary_find_binary_clause (clause[0], clause[1]))
141 return false;
142 if (size == 3 &&
144 return false;
145 return true;
146}
147
148/*------------------------------------------------------------------------*/
149
150// Produce all ternary resolvents on literal 'pivot' and increment the
151// 'steps' counter by the number of clauses containing 'pivot' which are
152// used during this process. The reason for choosing this metric to measure
153// the effort spent in 'ternary' is that it should be similar to one
154// propagation step during search.
155
156void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) {
157 LOG ("starting hyper ternary resolutions on pivot %d", pivot);
158 steps -= 1 + cache_lines (occs (pivot).size (), sizeof (Clause *));
159 for (const auto &c : occs (pivot)) {
160 if (steps < 0)
161 break;
162 if (htrs < 0)
163 break;
164 if (c->garbage)
165 continue;
166 if (c->size != 3) {
167 CADICAL_assert (c->size == 2);
168 continue;
169 }
170 if (--steps < 0)
171 break;
172 bool assigned = false;
173 for (const auto &lit : *c)
174 if (val (lit)) {
175 assigned = true;
176 break;
177 }
178 if (assigned)
179 continue;
180 steps -= 1 + cache_lines (occs (-pivot).size (), sizeof (Clause *));
181 for (const auto &d : occs (-pivot)) {
182 if (htrs < 0)
183 break;
184 if (--steps < 0)
185 break;
186 if (d->garbage)
187 continue;
188 if (d->size != 3) {
189 CADICAL_assert (d->size == 2);
190 continue;
191 }
192 for (const auto &lit : *d)
193 if (val (lit)) {
194 assigned = true;
195 break;
196 }
197 if (assigned)
198 continue;
199 CADICAL_assert (clause.empty ());
200 htrs--;
201 if (hyper_ternary_resolve (c, pivot, d)) {
202 size_t size = clause.size ();
203 bool red = (size == 3 || (c->redundant && d->redundant));
204 if (lrat) {
205 CADICAL_assert (lrat_chain.empty ());
206 lrat_chain.push_back (c->id);
207 lrat_chain.push_back (d->id);
208 }
210 if (red)
211 r->hyper = true;
212 lrat_chain.clear ();
213 clause.clear ();
214 LOG (r, "hyper ternary resolved");
215 stats.htrs++;
216 for (const auto &lit : *r)
217 occs (lit).push_back (r);
218 if (size == 2) {
219 LOG ("hyper ternary resolvent subsumes both antecedents");
220 mark_garbage (c);
221 mark_garbage (d);
222 stats.htrs2++;
223 break;
224 } else {
225 CADICAL_assert (r->size == 3);
226 stats.htrs3++;
227 }
228 } else {
229 LOG (clause, "ignoring size %zd resolvent", clause.size ());
230 clause.clear ();
231 }
232 }
233 }
234}
235
236/*------------------------------------------------------------------------*/
237
238// Same as 'ternary_lit' but pick the phase of the variable based on the
239// number of positive and negative occurrence.
240
241void Internal::ternary_idx (int idx, int64_t &steps, int64_t &htrs) {
242 CADICAL_assert (0 < idx);
243 CADICAL_assert (idx <= max_var);
244 steps -= 3;
245 if (!active (idx))
246 return;
247 if (!flags (idx).ternary)
248 return;
249 int pos = occs (idx).size ();
250 int neg = occs (-idx).size ();
251 if (pos <= opts.ternaryocclim && neg <= opts.ternaryocclim) {
252 LOG ("index %d has %zd positive and %zd negative occurrences", idx,
253 occs (idx).size (), occs (-idx).size ());
254 int pivot = (neg < pos ? -idx : idx);
255 ternary_lit (pivot, steps, htrs);
256 }
257 flags (idx).ternary = false;
258}
259
260/*------------------------------------------------------------------------*/
261
262// One round of ternary resolution over all variables. As in 'block' and
263// 'elim' we maintain a persistent global flag 'ternary' for each variable,
264// which records, whether a ternary clause containing it was added. Then we
265// can focus on those variables for which we have not tried ternary
266// resolution before and nothing changed for them since then. This works
267// across multiple calls to 'ternary' as well as 'ternary_round' since this
268// 'ternary' variable flag is updated during adding (ternary) resolvents.
269// This function goes over each variable just once.
270
271bool Internal::ternary_round (int64_t &steps_limit, int64_t &htrs_limit) {
272
274
275#ifndef CADICAL_QUIET
276 int64_t bincon = 0;
277 int64_t terncon = 0;
278#endif
279
280 init_occs ();
281
282 steps_limit -= 1 + cache_lines (clauses.size (), sizeof (Clause *));
283 for (const auto &c : clauses) {
284 steps_limit--;
285 if (c->garbage)
286 continue;
287 if (c->size > 3)
288 continue;
289 bool assigned = false, marked = false;
290 for (const auto &lit : *c) {
291 if (val (lit)) {
292 assigned = true;
293 break;
294 }
295 if (flags (lit).ternary)
296 marked = true;
297 }
298 if (assigned)
299 continue;
300 if (c->size == 2) {
301#ifndef CADICAL_QUIET
302 bincon++;
303#endif
304 } else {
305 CADICAL_assert (c->size == 3);
306 if (!marked)
307 continue;
308#ifndef CADICAL_QUIET
309 terncon++;
310#endif
311 }
312
313 for (const auto &lit : *c)
314 occs (lit).push_back (c);
315 }
316
317 PHASE ("ternary", stats.ternary,
318 "connected %" PRId64 " ternary %.0f%% "
319 "and %" PRId64 " binary clauses %.0f%%",
320 terncon, percent (terncon, clauses.size ()), bincon,
321 percent (bincon, clauses.size ()));
322
323 // Try ternary resolution on all variables once.
324 //
325 for (auto idx : vars) {
327 break;
328 if (steps_limit < 0)
329 break;
330 if (htrs_limit < 0)
331 break;
332 ternary_idx (idx, steps_limit, htrs_limit);
333 }
334
335 // Gather some statistics for the verbose messages below and also
336 // determine whether new variables have been marked and it would make
337 // sense to run another round of ternary resolution over those variables.
338 //
339 int remain = 0;
340 for (auto idx : vars) {
341 if (!active (idx))
342 continue;
343 if (!flags (idx).ternary)
344 continue;
345 remain++;
346 }
347 if (remain)
348 PHASE ("ternary", stats.ternary, "%d variables remain %.0f%%", remain,
349 percent (remain, max_var));
350 else
351 PHASE ("ternary", stats.ternary, "completed hyper ternary resolution");
352
353 reset_occs ();
355
356 return remain; // Are there variables that should be tried again?
357}
358
359/*------------------------------------------------------------------------*/
360
362
363 if (!opts.ternary)
364 return false;
365 if (unsat)
366 return false;
368 return false;
369
370 // No new ternary clauses added since last time?
371 //
372 if (last.ternary.marked == stats.mark.ternary)
373 return false;
374
376
378 stats.ternary++;
379
381
383 if (watching ())
384 reset_watches ();
385
386 // The number of clauses derived through ternary resolution can grow
387 // substantially, particularly for random formulas. Thus we limit the
388 // number of added clauses too (actually the number of 'htrs').
389 //
390 int64_t htrs_limit = stats.current.redundant + stats.current.irredundant;
391 htrs_limit *= opts.ternarymaxadd;
392 htrs_limit /= 100;
393
394 // approximation of ternary ticks.
395 // TODO: count with ternary.ticks directly.
396 int64_t steps_limit = stats.ticks.ternary - limit;
397 stats.ticks.ternary = limit;
398
399 // With 'stats.ternary' we actually count the number of calls to
400 // 'ternary_round' and not the number of calls to 'ternary'. But before
401 // the first round we want to show the limit on the number of steps and
402 // thus we increase counter for the first round here and skip increasing
403 // it in the loop below.
404 //
405 PHASE ("ternary", stats.ternary,
406 "will run a maximum of %d rounds "
407 "limited to %" PRId64 " steps and %" PRId64 " clauses",
408 opts.ternaryrounds, steps_limit, htrs_limit);
409
410 bool resolved_binary_clause = false;
411 bool completed = false;
412
413 for (int round = 0;
414 !terminated_asynchronously () && round < opts.ternaryrounds;
415 round++) {
416 if (htrs_limit < 0)
417 break;
418 if (steps_limit < 0)
419 break;
420 if (round)
421 stats.ternary++;
422 int old_htrs2 = stats.htrs2;
423 int old_htrs3 = stats.htrs3;
424 completed = ternary_round (steps_limit, htrs_limit);
425 int delta_htrs2 = stats.htrs2 - old_htrs2;
426 int delta_htrs3 = stats.htrs3 - old_htrs3;
427 PHASE ("ternary", stats.ternary,
428 "derived %d ternary and %d binary resolvents", delta_htrs3,
429 delta_htrs2);
430 report ('3', !opts.reportall && !(delta_htrs2 + delta_htrs2));
431 if (delta_htrs2)
432 resolved_binary_clause = true;
433 if (!delta_htrs3)
434 break;
435 }
436
439 init_watches ();
441 if (!propagate ()) {
442 LOG ("propagation after connecting watches results in inconsistency");
444 }
445
446 if (completed)
447 last.ternary.marked = stats.mark.ternary;
448
450
451 return resolved_binary_clause;
452}
453
454} // namespace CaDiCaL
455
#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 SET_EFFORT_LIMIT(LIMIT, NAME, THRESHHOLD)
Definition limit.hpp:131
#define PHASE(...)
Definition message.hpp:52
const char * flags()
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
int64_t cache_lines(size_t bytes)
Definition internal.hpp:722
void mark_garbage(Clause *)
vector< int64_t > lrat_chain
Definition internal.hpp:210
void report(char type, int verbose_level=0)
Flags & flags(int lit)
Definition internal.hpp:454
bool terminated_asynchronously(int factor=1)
const Sange lits
Definition internal.hpp:325
bool watching() const
Definition internal.hpp:462
signed char val(int lit) const
bool limit(const char *name, int)
signed char marked(int lit) const
Definition internal.hpp:478
bool ternary_find_binary_clause(int, int)
bool ternary_find_ternary_clause(int, int, int)
bool active(int lit)
Definition internal.hpp:360
bool ternary_round(int64_t &steps, int64_t &htrs)
vector< Clause * > clauses
Definition internal.hpp:283
void ternary_idx(int idx, int64_t &steps, int64_t &htrs)
const Range vars
Definition internal.hpp:324
Occs & occs(int lit)
Definition internal.hpp:465
bool occurring() const
Definition internal.hpp:461
Clause * new_hyper_ternary_resolved_clause(bool red)
bool hyper_ternary_resolve(Clause *, int, Clause *)
void ternary_lit(int pivot, int64_t &steps, int64_t &htrs)
vector< int > clause
Definition internal.hpp:260
void connect_watches(bool irredundant_only=false)
unsigned size
Definition vector.h:35