1 /******************************************
2 Copyright (c) 2016, Mate Soos
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 ***********************************************/
22
23 #include "xorfinder.h"
24 #include "time_mem.h"
25 #include "solver.h"
26 #include "occsimplifier.h"
27 #include "clauseallocator.h"
28 #include "sqlstats.h"
29 #include "varreplacer.h"
30
31 #include <limits>
32 //#define XOR_DEBUG
33
34 using namespace CMSat;
35 using std::cout;
36 using std::endl;
37
XorFinder(OccSimplifier * _occsimplifier,Solver * _solver)38 XorFinder::XorFinder(OccSimplifier* _occsimplifier, Solver* _solver) :
39 xors(_solver->xorclauses)
40 , unused_xors(_solver->xorclauses_unused)
41 , occsimplifier(_occsimplifier)
42 , solver(_solver)
43 , toClear(_solver->toClear)
44 , seen(_solver->seen)
45 , seen2(_solver->seen2)
46 {
47 tmp_vars_xor_two.reserve(2000);
48 }
49
find_xors_based_on_long_clauses()50 void XorFinder::find_xors_based_on_long_clauses()
51 {
52 #ifdef DEBUG_MARKED_CLAUSE
53 assert(solver->no_marked_clauses());
54 #endif
55
56 vector<Lit> lits;
57 for (vector<ClOffset>::iterator
58 it = occsimplifier->clauses.begin()
59 , end = occsimplifier->clauses.end()
60 ; it != end && xor_find_time_limit > 0
61 ; ++it
62 ) {
63 ClOffset offset = *it;
64 Clause* cl = solver->cl_alloc.ptr(offset);
65 xor_find_time_limit -= 1;
66
67 //Already freed
68 if (cl->freed() || cl->getRemoved() || cl->red()) {
69 continue;
70 }
71
72 //Too large -> too expensive
73 if (cl->size() > solver->conf.maxXorToFind) {
74 continue;
75 }
76
77 //If not tried already, find an XOR with it
78 if (!cl->stats.marked_clause ) {
79 cl->stats.marked_clause = true;
80 assert(!cl->getRemoved());
81
82 size_t needed_per_ws = 1ULL << (cl->size()-2);
83 //let's allow shortened clauses
84 needed_per_ws >>= 1;
85
86 for(const Lit lit: *cl) {
87 if (solver->watches[lit].size() < needed_per_ws) {
88 goto next;
89 }
90 if (solver->watches[~lit].size() < needed_per_ws) {
91 goto next;
92 }
93 }
94
95 lits.resize(cl->size());
96 std::copy(cl->begin(), cl->end(), lits.begin());
97 findXor(lits, offset, cl->abst);
98 next:;
99 }
100 }
101 }
102
clean_equivalent_xors(vector<Xor> & txors)103 void XorFinder::clean_equivalent_xors(vector<Xor>& txors)
104 {
105 if (!txors.empty()) {
106 size_t orig_size = txors.size();
107 for(Xor& x: txors) {
108 std::sort(x.begin(), x.end());
109 }
110 std::sort(txors.begin(), txors.end());
111
112 vector<Xor>::iterator i = txors.begin();
113 vector<Xor>::iterator j = i;
114 i++;
115 uint64_t size = 1;
116 for(vector<Xor>::iterator end = txors.end(); i != end; i++) {
117 if (j->vars == i->vars && i->rhs == j->rhs) {
118 j->merge_clash(*i, seen);
119 j->detached |= i->detached;
120 } else {
121 j++;
122 *j = *i;
123 size++;
124 }
125 }
126 txors.resize(size);
127
128 if (solver->conf.verbosity) {
129 cout << "c [xor-clean-equiv] removed equivalent xors: "
130 << (orig_size-txors.size()) << " left with: " << txors.size()
131 << endl;
132 }
133 }
134 }
135
find_xors()136 void XorFinder::find_xors()
137 {
138 runStats.clear();
139 runStats.numCalls = 1;
140 grab_mem();
141 if ((solver->conf.xor_var_per_cut + 2) > solver->conf.maxXorToFind) {
142 if (solver->conf.verbosity) {
143 cout << "c WARNING updating max XOR to find to "
144 << (solver->conf.xor_var_per_cut + 2)
145 << " as the current number was lower than the cutting number" << endl;
146 }
147 solver->conf.maxXorToFind = solver->conf.xor_var_per_cut + 2;
148 }
149
150 //Clear flags. This is super-important.
151 for(auto& offs: occsimplifier->clauses) {
152 Clause* cl = solver->cl_alloc.ptr(offs);
153 if (cl->getRemoved() || cl->freed()) {
154 continue;
155 }
156
157 cl->set_used_in_xor(false);
158 cl->set_used_in_xor_full(false);
159 }
160 xors.clear();
161 unused_xors.clear();
162
163 double myTime = cpuTime();
164 const int64_t orig_xor_find_time_limit =
165 1000LL*1000LL*solver->conf.xor_finder_time_limitM
166 *solver->conf.global_timeout_multiplier;
167
168 xor_find_time_limit = orig_xor_find_time_limit;
169
170 occsimplifier->sort_occurs_and_set_abst();
171 if (solver->conf.verbosity) {
172 cout << "c [occ-xor] sort occur list T: " << (cpuTime()-myTime) << endl;
173 }
174
175 #ifdef DEBUG_MARKED_CLAUSE
176 assert(solver->no_marked_clauses());
177 #endif
178
179 find_xors_based_on_long_clauses();
180 assert(runStats.foundXors == xors.size());
181
182 //clean them of equivalent XORs
183 clean_equivalent_xors(xors);
184
185 //Cleanup
186 for(ClOffset offset: occsimplifier->clauses) {
187 Clause* cl = solver->cl_alloc.ptr(offset);
188 cl->stats.marked_clause = false;
189 }
190
191 //Print stats
192 const bool time_out = (xor_find_time_limit < 0);
193 const double time_remain = float_div(xor_find_time_limit, orig_xor_find_time_limit);
194 runStats.findTime = cpuTime() - myTime;
195 runStats.time_outs += time_out;
196 solver->sumSearchStats.num_xors_found_last = xors.size();
197 print_found_xors();
198
199 if (solver->conf.verbosity) {
200 runStats.print_short(solver, time_remain);
201 }
202 globalStats += runStats;
203
204 if (solver->sqlStats) {
205 solver->sqlStats->time_passed(
206 solver
207 , "xor-find"
208 , cpuTime() - myTime
209 , time_out
210 , time_remain
211 );
212 }
213 solver->xor_clauses_updated = true;
214
215 #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
216 for(const Xor& x: xors) {
217 for(uint32_t v: x) {
218 assert(solver->varData[v].removed == Removed::none);
219 }
220 }
221 #endif
222 }
223
print_found_xors()224 void XorFinder::print_found_xors()
225 {
226 if (solver->conf.verbosity >= 5) {
227 cout << "c Found XORs: " << endl;
228 for(vector<Xor>::const_iterator
229 it = xors.begin(), end = xors.end()
230 ; it != end
231 ; ++it
232 ) {
233 cout << "c " << *it << endl;
234 }
235 cout << "c -> Total: " << xors.size() << " xors" << endl;
236 }
237 }
238
findXor(vector<Lit> & lits,const ClOffset offset,cl_abst_type abst)239 void XorFinder::findXor(vector<Lit>& lits, const ClOffset offset, cl_abst_type abst)
240 {
241 //Set this clause as the base for the XOR, fill 'seen'
242 xor_find_time_limit -= lits.size()/4+1;
243 poss_xor.setup(lits, offset, abst, occcnt);
244
245 //Run findXorMatch for the 2 smallest watchlists
246 Lit slit = lit_Undef;
247 Lit slit2 = lit_Undef;
248 uint32_t smallest = std::numeric_limits<uint32_t>::max();
249 uint32_t smallest2 = std::numeric_limits<uint32_t>::max();
250 for (size_t i = 0, end = lits.size(); i < end; i++) {
251 const Lit lit = lits[i];
252 uint32_t num = solver->watches[lit].size();
253 num += solver->watches[~lit].size();
254 if (num < smallest) {
255 slit2 = slit;
256 smallest2 = smallest;
257
258 slit = lit;
259 smallest = num;
260 } else if (num < smallest2) {
261 slit2 = lit;
262 smallest2 = num;
263 }
264 }
265 findXorMatch(solver->watches[slit], slit);
266 findXorMatch(solver->watches[~slit], ~slit);
267
268 if (lits.size() <= solver->conf.maxXorToFindSlow) {
269 findXorMatch(solver->watches[slit2], slit2);
270 findXorMatch(solver->watches[~slit2], ~slit2);
271 }
272
273 if (poss_xor.foundAll()) {
274 std::sort(lits.begin(), lits.end());
275 Xor found_xor(lits, poss_xor.getRHS(), vector<uint32_t>());
276 #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
277 for(Lit lit: lits) {
278 assert(solver->varData[lit.var()].removed == Removed::none);
279 }
280 #endif
281
282 add_found_xor(found_xor);
283 assert(poss_xor.get_fully_used().size() == poss_xor.get_offsets().size());
284 for(uint32_t i = 0; i < poss_xor.get_offsets().size() ; i++) {
285 ClOffset offs = poss_xor.get_offsets()[i];
286 bool fully_used = poss_xor.get_fully_used()[i];
287
288 Clause* cl = solver->cl_alloc.ptr(offs);
289 assert(!cl->getRemoved());
290 cl->set_used_in_xor(true);
291 cl->set_used_in_xor_full(fully_used);
292 }
293 }
294 poss_xor.clear_seen(occcnt);
295 }
296
add_found_xor(const Xor & found_xor)297 void XorFinder::add_found_xor(const Xor& found_xor)
298 {
299 xors.push_back(found_xor);
300 runStats.foundXors++;
301 runStats.sumSizeXors += found_xor.size();
302 runStats.maxsize = std::max<uint32_t>(runStats.maxsize, found_xor.size());
303 runStats.minsize = std::min<uint32_t>(runStats.minsize, found_xor.size());
304 }
305
findXorMatch(watch_subarray_const occ,const Lit wlit)306 void XorFinder::findXorMatch(watch_subarray_const occ, const Lit wlit)
307 {
308 xor_find_time_limit -= (int64_t)occ.size()/8+1;
309 for (const Watched& w: occ) {
310 if (w.isIdx()) {
311 continue;
312 }
313 assert(poss_xor.getSize() > 2);
314
315 if (w.isBin()) {
316 #ifdef SLOW_DEBUG
317 assert(occcnt[wlit.var()]);
318 #endif
319
320 if (w.red()) {
321 continue;
322 }
323
324 if (!occcnt[w.lit2().var()]) {
325 goto end;
326 }
327
328 binvec.clear();
329 binvec.resize(2);
330 binvec[0] = w.lit2();
331 binvec[1] = wlit;
332 if (binvec[0] > binvec[1]) {
333 std::swap(binvec[0], binvec[1]);
334 }
335
336 xor_find_time_limit -= 1;
337 poss_xor.add(binvec, std::numeric_limits<ClOffset>::max(), varsMissing);
338 if (poss_xor.foundAll())
339 break;
340 } else {
341 if (w.getBlockedLit().toInt() == lit_Undef.toInt())
342 //Clauses are ordered, lit_Undef means it's larger than maxXorToFind
343 break;
344
345 if (w.getBlockedLit().toInt() == lit_Error.toInt())
346 //lit_Error means it's freed or removed, and it's ordered so no more
347 break;
348
349 if ((w.getBlockedLit().toInt() | poss_xor.getAbst()) != poss_xor.getAbst())
350 continue;
351
352 xor_find_time_limit -= 3;
353 const ClOffset offset = w.get_offset();
354 Clause& cl = *solver->cl_alloc.ptr(offset);
355 if (cl.freed() || cl.getRemoved() || cl.red()) {
356 //Clauses are ordered!!
357 break;
358 }
359
360 //Allow the clause to be smaller or equal in size
361 if (cl.size() > poss_xor.getSize()) {
362 //clauses are ordered!!
363 break;
364 }
365
366 //For longer clauses, don't the the fancy algo that can
367 //deal with incomplete XORs
368 if (cl.size() != poss_xor.getSize()
369 && poss_xor.getSize() > solver->conf.maxXorToFindSlow
370 ) {
371 break;
372 }
373
374 //Doesn't contain variables not in the original clause
375 #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
376 assert(cl.abst == calcAbstraction(cl));
377 #endif
378 if ((cl.abst | poss_xor.getAbst()) != poss_xor.getAbst())
379 continue;
380
381 //Check RHS, vars inside
382 bool rhs = true;
383 for (const Lit cl_lit :cl) {
384 //early-abort, contains literals not in original clause
385 if (!occcnt[cl_lit.var()])
386 goto end;
387
388 rhs ^= cl_lit.sign();
389 }
390 //either the invertedness has to match, or the size must be smaller
391 if (rhs != poss_xor.getRHS() && cl.size() == poss_xor.getSize())
392 continue;
393
394 //If the size of this clause is the same of the base clause, then
395 //there is no point in using this clause as a base for another XOR
396 //because exactly the same things will be found.
397 if (cl.size() == poss_xor.getSize()) {
398 cl.stats.marked_clause = true;
399 }
400
401 xor_find_time_limit -= cl.size()/4+1;
402 poss_xor.add(cl, offset, varsMissing);
403 if (poss_xor.foundAll())
404 break;
405 }
406 end:;
407 }
408 }
409
remove_xors_without_connecting_vars(const vector<Xor> & this_xors)410 vector<Xor> XorFinder::remove_xors_without_connecting_vars(const vector<Xor>& this_xors)
411 {
412 if (this_xors.empty())
413 return this_xors;
414
415 double myTime = cpuTime();
416 vector<Xor> ret;
417 assert(toClear.empty());
418
419 //Fill "seen" with vars used
420 uint32_t non_empty = 0;
421 for(const Xor& x: this_xors) {
422 if (x.size() != 0) {
423 non_empty++;
424 }
425
426 for(uint32_t v: x) {
427 if (solver->seen[v] == 0) {
428 toClear.push_back(Lit(v, false));
429 }
430
431 if (solver->seen[v] < 2) {
432 solver->seen[v]++;
433 }
434 }
435 }
436
437 //has at least 1 var with occur of 2
438 for(const Xor& x: this_xors) {
439 if (xor_has_interesting_var(x) || x.detached) {
440 #ifdef VERBOSE_DEBUG
441 cout << "XOR has connecting var: " << x << endl;
442 #endif
443 ret.push_back(x);
444 } else {
445 #ifdef VERBOSE_DEBUG
446 cout << "XOR has no connecting var: " << x << endl;
447 #endif
448 unused_xors.push_back(x);
449 }
450 }
451
452 //clear "seen"
453 for(Lit l: toClear) {
454 solver->seen[l.var()] = 0;
455 }
456 toClear.clear();
457
458 double time_used = cpuTime() - myTime;
459 if (solver->conf.verbosity) {
460 cout << "c [xor-rem-unconnected] left with " << ret.size()
461 << " xors from " << non_empty << " non-empty xors"
462 << solver->conf.print_times(time_used)
463 << endl;
464 }
465 if (solver->sqlStats) {
466 solver->sqlStats->time_passed_min(
467 solver
468 , "xor-rem-no-connecting-vars"
469 , time_used
470 );
471 }
472
473 return ret;
474 }
475
xor_together_xors(vector<Xor> & this_xors)476 bool XorFinder::xor_together_xors(vector<Xor>& this_xors)
477 {
478 if (occcnt.size() != solver->nVars())
479 grab_mem();
480
481 if (this_xors.empty())
482 return solver->okay();
483
484 #ifdef SLOW_DEBUG
485 for(auto x: occcnt) {
486 assert(x == 0);
487 }
488 #endif
489
490 if (solver->conf.verbosity >= 5) {
491 cout << "c XOR-ing together XORs. Starting with: " << endl;
492 for(const auto& x: this_xors) {
493 cout << "c XOR before xor-ing together: " << x << endl;
494 }
495 }
496
497 assert(solver->okay());
498 assert(solver->decisionLevel() == 0);
499 assert(solver->watches.get_smudged_list().empty());
500 const size_t origsize = this_xors.size();
501
502 uint32_t xored = 0;
503 const double myTime = cpuTime();
504 assert(toClear.empty());
505
506 //Link in xors into watchlist
507 for(size_t i = 0; i < this_xors.size(); i++) {
508 const Xor& x = this_xors[i];
509 for(uint32_t v: x) {
510 if (occcnt[v] == 0) {
511 toClear.push_back(Lit(v, false));
512 }
513 occcnt[v]++;
514
515 Lit l(v, false);
516 assert(solver->watches.size() > l.toInt());
517 solver->watches[l].push(Watched(i)); //Idx watch
518 solver->watches.smudge(l);
519 }
520 }
521
522 //Don't XOR together over the sampling vars
523 //or variables that are in regular clauses
524 vector<uint32_t> to_clear_2;
525 if (solver->conf.sampling_vars) {
526 for(uint32_t outside_var: *solver->conf.sampling_vars) {
527 uint32_t outer_var = solver->map_to_with_bva(outside_var);
528 outer_var = solver->varReplacer->get_var_replaced_with_outer(outer_var);
529 uint32_t int_var = solver->map_outer_to_inter(outer_var);
530 if (int_var < solver->nVars()) {
531 if (!seen2[int_var]) {
532 seen2[int_var] = 1;
533 to_clear_2.push_back(int_var);
534 //cout << "sampling var: " << int_var+1 << endl;
535 }
536 }
537 }
538 }
539
540 for(const auto& ws: solver->watches) {
541 for(const auto& w: ws) {
542 if (w.isBin() && !w.red()) {
543 uint32_t v = w.lit2().var();
544 if (!seen2[v]) {
545 seen2[v] = 1;
546 to_clear_2.push_back(v);
547 }
548 }
549 }
550 }
551
552 for(const auto& offs: solver->longIrredCls) {
553 Clause* cl = solver->cl_alloc.ptr(offs);
554 if (cl->red() || cl->used_in_xor()) {
555 continue;
556 }
557 for(Lit l: *cl) {
558 if (!seen2[l.var()]) {
559 seen2[l.var()] = 1;
560 to_clear_2.push_back(l.var());
561 //cout << "Not XORing together over var: " << l.var()+1 << endl;
562 }
563 }
564 }
565
566 //until fixedpoint
567 bool changed = true;
568 while(changed) {
569 changed = false;
570 interesting.clear();
571 for(const Lit l: toClear) {
572 if (occcnt[l.var()] == 2 && !seen2[l.var()]) {
573 interesting.push_back(l.var());
574 }
575 }
576
577 while(!interesting.empty()) {
578 #ifdef SLOW_DEBUG
579 {
580 vector<uint32_t> check;
581 check.resize(solver->nVars(), 0);
582 for(size_t i = 0; i < this_xors.size(); i++) {
583 const Xor& x = this_xors[i];
584 for(uint32_t v: x) {
585 check[v]++;
586 }
587 }
588 for(size_t i = 0; i < solver->nVars(); i++) {
589 assert(check[i] == occcnt[i]);
590 }
591 }
592 #endif
593
594 //Pop and check if it can be XOR-ed together
595 const uint32_t v = interesting.back();
596 interesting.resize(interesting.size()-1);
597 if (occcnt[v] != 2)
598 continue;
599
600 size_t idxes[2];
601 unsigned at = 0;
602 size_t i2 = 0;
603 assert(solver->watches.size() > Lit(v, false).toInt());
604 watch_subarray ws = solver->watches[Lit(v, false)];
605
606 //Remove the 2 indexes from the watchlist
607 for(size_t i = 0; i < ws.size(); i++) {
608 const Watched& w = ws[i];
609 if (!w.isIdx()) {
610 ws[i2++] = ws[i];
611 } else if (!this_xors[w.get_idx()].empty()) {
612 assert(at < 2);
613 idxes[at] = w.get_idx();
614 at++;
615 }
616 }
617 assert(at == 2);
618 ws.resize(i2);
619
620 Xor& x0 = this_xors[idxes[0]];
621 Xor& x1 = this_xors[idxes[1]];
622 uint32_t clash_var;
623 uint32_t clash_num = xor_two(&x0, &x1, clash_var);
624
625 //If they are equivalent
626 if (x0.size() == x1.size()
627 && x0.rhs == x1.rhs
628 && clash_num == x0.size()
629 ) {
630 #ifdef VERBOSE_DEBUG
631 cout << "x1: " << x0 << " -- at idx: " << idxes[0] << endl;
632 cout << "x2: " << x1 << " -- at idx: " << idxes[1] << endl;
633 cout << "equivalent. " << endl;
634 #endif
635
636 //Update clash values & detached values
637 x1.merge_clash(x0, seen);
638 x1.detached |= x0.detached;
639
640 #ifdef VERBOSE_DEBUG
641 cout << "after merge: " << x1 << " -- at idx: " << idxes[1] << endl;
642 #endif
643
644 //Equivalent, so delete one
645 x0 = Xor();
646
647 //Re-attach the other, remove the occur of the one we deleted
648 solver->watches[Lit(v, false)].push(Watched(idxes[1]));
649
650 for(uint32_t v2: x1) {
651 Lit l(v2, false);
652 assert(occcnt[l.var()] >= 2);
653 occcnt[l.var()]--;
654 if (occcnt[l.var()] == 2 && !seen2[l.var()]) {
655 interesting.push_back(l.var());
656 }
657 }
658 } else if (clash_num > 1 || x0.detached || x1.detached) {
659 //add back to ws, can't do much
660 ws.push(Watched(idxes[0]));
661 ws.push(Watched(idxes[1]));
662 continue;
663 } else {
664 occcnt[v] -= 2;
665 assert(occcnt[v] == 0);
666
667 Xor x_new(tmp_vars_xor_two, x0.rhs ^ x1.rhs, clash_var);
668 x_new.merge_clash(x0, seen);
669 x_new.merge_clash(x1, seen);
670 #ifdef VERBOSE_DEBUG
671 cout << "x1: " << x0 << " -- at idx: " << idxes[0] << endl;
672 cout << "x2: " << x1 << " -- at idx: " << idxes[1] << endl;
673 cout << "clashed on var: " << clash_var+1 << endl;
674 cout << "final: " << x_new << " -- at idx: " << this_xors.size() << endl;
675 #endif
676 changed = true;
677 this_xors.push_back(x_new);
678 for(uint32_t v2: x_new) {
679 Lit l(v2, false);
680 solver->watches[l].push(Watched(this_xors.size()-1));
681 assert(occcnt[l.var()] >= 1);
682 if (occcnt[l.var()] == 2 && !seen2[l.var()]) {
683 interesting.push_back(l.var());
684 }
685 }
686 this_xors[idxes[0]] = Xor();
687 this_xors[idxes[1]] = Xor();
688 xored++;
689 }
690 }
691 }
692
693 if (solver->conf.verbosity >= 5) {
694 cout << "c Finished XOR-ing together XORs. " << endl;
695 size_t at = 0;
696 for(const auto& x: this_xors) {
697 cout << "c XOR after xor-ing together: " << x << " -- at idx: " << at << endl;
698 at++;
699 }
700 }
701
702 for(const Lit l: toClear) {
703 occcnt[l.var()] = 0;
704 }
705 toClear.clear();
706
707 //Clear seen2
708 for(const auto& x: to_clear_2) {
709 seen2[x] = 0;
710 }
711
712 solver->clean_occur_from_idx_types_only_smudged();
713 clean_xors_from_empty(this_xors);
714 double recur_time = cpuTime() - myTime;
715 if (solver->conf.verbosity) {
716 cout
717 << "c [xor-together] xored together: " << xored
718 << " orig xors: " << origsize
719 << " new xors: " << this_xors.size()
720 << solver->conf.print_times(recur_time)
721 << endl;
722 }
723
724
725 if (solver->sqlStats) {
726 solver->sqlStats->time_passed_min(
727 solver
728 , "xor-xor-together"
729 , recur_time
730 );
731 }
732
733 #if defined(SLOW_DEBUG) || defined(XOR_DEBUG)
734 //Make sure none is 2.
735 assert(toClear.empty());
736 for(const Xor& x: this_xors) {
737 for(uint32_t v: x) {
738 if (occcnt[v] == 0) {
739 toClear.push_back(Lit(v, false));
740 }
741
742 //Don't roll around
743 occcnt[v]++;
744 }
745 }
746
747 for(const Lit c: toClear) {
748 /*This is now possible because we don't XOR them together
749 in case they clash on more than 1 variable */
750 //assert(occcnt[c.var()] != 2);
751
752 occcnt[c.var()] = 0;
753 }
754 toClear.clear();
755 #endif
756
757 return solver->okay();
758 }
759
clean_xors_from_empty(vector<Xor> & thisxors)760 void XorFinder::clean_xors_from_empty(vector<Xor>& thisxors)
761 {
762 size_t j = 0;
763 for(size_t i = 0;i < thisxors.size(); i++) {
764 Xor& x = thisxors[i];
765 if (x.size() == 0
766 && x.rhs == false
767 ) {
768 if (!x.clash_vars.empty()) {
769 unused_xors.push_back(x);
770 }
771 } else {
772 if (solver->conf.verbosity >= 4) {
773 cout << "c xor after clean: " << thisxors[i] << endl;
774 }
775 thisxors[j++] = thisxors[i];
776 }
777 }
778 thisxors.resize(j);
779 }
780
add_new_truths_from_xors(vector<Xor> & this_xors,vector<Lit> * out_changed_occur)781 bool XorFinder::add_new_truths_from_xors(vector<Xor>& this_xors, vector<Lit>* out_changed_occur)
782 {
783 size_t origTrailSize = solver->trail_size();
784 size_t origBins = solver->binTri.redBins;
785 double myTime = cpuTime();
786
787 assert(solver->ok);
788 size_t i2 = 0;
789 for(size_t i = 0;i < this_xors.size(); i++) {
790 Xor& x = this_xors[i];
791 solver->clean_xor_vars_no_prop(x.get_vars(), x.rhs);
792 if (x.size() > 2) {
793 this_xors[i2] = this_xors[i];
794 i2++;
795 continue;
796 }
797
798 switch(x.size() ) {
799 case 0: {
800 if (x.rhs == true) {
801 solver->ok = false;
802 return false;
803 }
804 break;
805 }
806
807 case 1: {
808 Lit lit = Lit(x[0], !x.rhs);
809 if (solver->value(lit) == l_False) {
810 solver->ok = false;
811 } else if (solver->value(lit) == l_Undef) {
812 solver->enqueue(lit);
813 if (out_changed_occur)
814 solver->ok = solver->propagate_occur();
815 else
816 solver->ok = solver->propagate<true>().isNULL();
817 }
818 if (!solver->ok) {
819 goto end;
820 }
821 break;
822 }
823
824 case 2: {
825 //RHS == 1 means both same is not allowed
826 vector<Lit> lits{Lit(x[0], false), Lit(x[1], true^x.rhs)};
827 solver->add_clause_int(lits, true, ClauseStats(), out_changed_occur != NULL);
828 if (!solver->ok) {
829 goto end;
830 }
831 lits = {Lit(x[0], true), Lit(x[1], false^x.rhs)};
832 solver->add_clause_int(lits, true, ClauseStats(), out_changed_occur != NULL);
833 if (!solver->ok) {
834 goto end;
835 }
836 if (out_changed_occur) {
837 solver->ok = solver->propagate_occur();
838 } else {
839 solver->ok = solver->propagate<true>().isNULL();
840 }
841
842 if (out_changed_occur) {
843 out_changed_occur->push_back(Lit(x[0], false));
844 out_changed_occur->push_back(Lit(x[1], false));
845 }
846 break;
847 }
848
849 default: {
850 assert(false && "Not possible");
851 }
852 }
853 }
854 this_xors.resize(i2);
855 end:
856
857 double add_time = cpuTime() - myTime;
858 uint32_t num_bins_added = solver->binTri.redBins - origBins;
859 uint32_t num_units_added = solver->trail_size() - origTrailSize;
860
861 if (solver->conf.verbosity) {
862 cout
863 << "c [xor-add-lem] added unit " << num_units_added
864 << " bin " << num_bins_added
865 << solver->conf.print_times(add_time)
866 << endl;
867 }
868
869
870 if (solver->sqlStats) {
871 solver->sqlStats->time_passed_min(
872 solver
873 , "xor-add-new-bin-unit"
874 , add_time
875 );
876 }
877
878 return solver->okay();
879 }
880
xor_two(Xor const * x1_p,Xor const * x2_p,uint32_t & clash_var)881 uint32_t XorFinder::xor_two(Xor const* x1_p, Xor const* x2_p, uint32_t& clash_var)
882 {
883 tmp_vars_xor_two.clear();
884 if (x1_p->size() > x2_p->size()) {
885 std::swap(x1_p, x2_p);
886 }
887 const Xor& x1 = *x1_p;
888 const Xor& x2 = *x2_p;
889
890 uint32_t clash_num = 0;
891 for(uint32_t v: x1) {
892 assert(seen[v] == 0);
893 seen[v] = 1;
894 }
895
896 uint32_t i_x2;
897 bool early_abort = false;
898 for(i_x2 = 0; i_x2 < x2.size(); i_x2++) {
899 uint32_t v = x2[i_x2];
900 assert(seen[v] != 2);
901 if (seen[v] == 0) {
902 tmp_vars_xor_two.push_back(v);
903 } else {
904 clash_var = v;
905 if (clash_num > 0 &&
906 clash_num != i_x2 //not equivalent by chance
907 ) {
908 //early abort, it's never gonna be good
909 clash_num++;
910 early_abort = true;
911 break;
912 }
913 clash_num++;
914 }
915 seen[v] = 2;
916 }
917
918 if (!early_abort) {
919 #ifdef SLOW_DEBUG
920 uint32_t other_clash = 0;
921 #endif
922 for(uint32_t v: x1) {
923 if (seen[v] != 2) {
924 tmp_vars_xor_two.push_back(v);
925 } else {
926 #ifdef SLOW_DEBUG
927 other_clash++;
928 #endif
929 }
930 seen[v] = 0;
931 }
932 #ifdef SLOW_DEBUG
933 assert(other_clash == clash_num);
934 #endif
935 } else {
936 for(uint32_t v: x1) {
937 seen[v] = 0;
938 }
939 }
940
941 for(uint32_t i = 0; i < i_x2; i++) {
942 seen[x2[i]] = 0;
943 }
944
945 #ifdef SLOW_DEBUG
946 for(uint32_t v: x1) {
947 assert(seen[v] == 0);
948 }
949 #endif
950
951 return clash_num;
952 }
953
xor_has_interesting_var(const Xor & x)954 bool XorFinder::xor_has_interesting_var(const Xor& x)
955 {
956 for(uint32_t v: x) {
957 if (solver->seen[v] > 1) {
958 return true;
959 }
960 }
961 return false;
962 }
963
mem_used() const964 size_t XorFinder::mem_used() const
965 {
966 size_t mem = 0;
967 mem += xors.capacity()*sizeof(Xor);
968
969 //Temporary
970 mem += tmpClause.capacity()*sizeof(Lit);
971 mem += varsMissing.capacity()*sizeof(uint32_t);
972
973 return mem;
974 }
975
grab_mem()976 void XorFinder::grab_mem()
977 {
978 occcnt.clear();
979 occcnt.resize(solver->nVars(), 0);
980 }
981
print_short(const Solver * solver,double time_remain) const982 void XorFinder::Stats::print_short(const Solver* solver, double time_remain) const
983 {
984 cout
985 << "c [occ-xor] found " << std::setw(6) << foundXors
986 ;
987 if (foundXors > 0) {
988 cout
989 << " avg sz " << std::setw(3) << std::fixed << std::setprecision(1)
990 << float_div(sumSizeXors, foundXors)
991 << " min sz " << std::setw(2) << std::fixed << std::setprecision(1)
992 << minsize
993 << " max sz " << std::setw(2) << std::fixed << std::setprecision(1)
994 << maxsize;
995 }
996 cout
997 << solver->conf.print_times(findTime, time_outs, time_remain)
998 << endl;
999 }
1000
operator +=(const XorFinder::Stats & other)1001 XorFinder::Stats& XorFinder::Stats::operator+=(const XorFinder::Stats& other)
1002 {
1003 //Time
1004 findTime += other.findTime;
1005
1006 //XOR
1007 foundXors += other.foundXors;
1008 sumSizeXors += other.sumSizeXors;
1009
1010 //Usefulness
1011 time_outs += other.time_outs;
1012
1013 return *this;
1014 }
1015