1 /******************************************
2 Copyright (c) 2019, 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 "cms_breakid.h"
24 #include "solver.h"
25 #include "clausecleaner.h"
26 #include "breakid/breakid.hpp"
27 #include "varupdatehelper.h"
28 #include "varreplacer.h"
29 #include "occsimplifier.h"
30 #include "subsumeimplicit.h"
31 #include "sqlstats.h"
32 #include "completedetachreattacher.h"
33 
34 using namespace CMSat;
35 
BreakID(Solver * _solver)36 BreakID::BreakID(Solver* _solver):
37     solver(_solver)
38 {
39 }
40 
updateVars(const vector<uint32_t> & outerToInter,const vector<uint32_t> &)41 void BreakID::updateVars(
42     const vector<uint32_t>& outerToInter
43     , const vector<uint32_t>& /*interToOuter*/)
44 {
45     if (symm_var != var_Undef) {
46         symm_var = getUpdatedVar(symm_var, outerToInter);
47     }
48 }
49 
50 template<class T>
add_this_clause(const T & cl)51 BreakID::add_cl_ret BreakID::add_this_clause(const T& cl)
52 {
53     uint32_t sz = 0;
54     bool sat = false;
55     brkid_lits.clear();
56     for(size_t i3 = 0; i3 < cl.size(); i3++) {
57         Lit lit = cl[i3];
58         assert(solver->varData[lit.var()].removed == Removed::none);
59         lbool val = l_Undef;
60         if (solver->value(lit) != l_Undef) {
61             val = solver->value(lit);
62         } else {
63             val = solver->lit_inside_assumptions(lit);
64         }
65 
66         if (val == l_True) {
67             //clause is SAT, skip!
68             sat = true;
69             continue;
70         } else if (val == l_False) {
71             continue;
72         }
73         brkid_lits.push_back(lit);
74         sz++;
75     }
76     if (sat) {
77         return add_cl_ret::skipped_cl;
78     }
79     if (sz == 0) {
80         //it's unsat because of assumptions
81         if (solver->conf.verbosity) {
82             cout << "c [breakid] UNSAT because of assumptions in clause: " << cl << endl;
83         }
84         return add_cl_ret::unsat;
85     }
86 
87     num_lits_in_graph += brkid_lits.size();
88     breakid->add_clause((BID::BLit*)brkid_lits.data(), brkid_lits.size());
89     brkid_lits.clear();
90 
91     return add_cl_ret::added_cl;
92 }
93 
94 struct EqCls {
EqClsEqCls95     EqCls(ClauseAllocator& _alloc) :
96         cl_alloc(_alloc)
97     {}
98 
operator ()EqCls99     bool operator()(ClOffset off1, ClOffset off2) {
100         Clause* cl1 = cl_alloc.ptr(off1);
101         Clause* cl2 = cl_alloc.ptr(off2);
102 
103         if (cl1->stats.hash_val != cl2->stats.hash_val) {
104             return cl1->stats.hash_val < cl2->stats.hash_val;
105         }
106 
107         if (cl1->size() != cl2->size()) {
108             return cl1->size() < cl2->size();
109         }
110 
111         //same hash, same size
112         for(uint32_t i = 0; i < cl1->size(); i++) {
113             if (cl1->getData()[i] != cl2->getData()[i]) {
114                 return (cl1->getData()[i] < cl2->getData()[i]);
115             }
116         }
117 
118         //they are equivalent
119         return false;
120     }
121 
122     ClauseAllocator& cl_alloc;
123 };
124 
equiv(Clause * cl1,Clause * cl2)125 static bool equiv(Clause* cl1, Clause* cl2) {
126     if (cl1->stats.hash_val != cl2->stats.hash_val) {
127         return false;
128     }
129 
130     if (cl1->size() != cl2->size()) {
131         return false;
132     }
133 
134     for(uint32_t i = 0; i < cl1->size(); i++) {
135         if (cl1->getData()[i] != cl2->getData()[i]) {
136             return false;
137         }
138     }
139 
140     return true;
141 }
142 
set_up_time_lim()143 void BreakID::set_up_time_lim()
144 {
145     set_time_lim = solver->conf.breakid_time_limit_K;
146     if (solver->nVars() < 5000) {
147         set_time_lim*=2;
148     }
149     if (num_lits_in_graph < 100000) {
150         set_time_lim*=2;
151     }
152 
153     set_time_lim *= 1000LL;
154     if (solver->conf.verbosity) {
155         cout << "c [breakid] set time lim: " << set_time_lim << endl;
156     }
157 
158     breakid->set_steps_lim(set_time_lim);
159 }
160 
add_clauses()161 bool BreakID::add_clauses()
162 {
163     //Add binary clauses
164     vector<Lit> this_clause;
165     for(size_t i2 = 0; i2 < solver->nVars()*2; i2++) {
166         Lit lit = Lit::toLit(i2);
167         for(const Watched& w: solver->watches[lit]) {
168             if (w.isBin() && !w.red() && lit < w.lit2()) {
169                 this_clause.clear();
170                 this_clause.push_back(lit);
171                 this_clause.push_back(w.lit2());
172 
173                 if (add_this_clause(this_clause) == add_cl_ret::unsat) {
174                     return false;
175                 }
176             }
177         }
178     }
179 
180     //Add long clauses
181     for(ClOffset offs: dedup_cls) {
182         const Clause* cl = solver->cl_alloc.ptr(offs);
183         assert(!cl->freed());
184         assert(!cl->getRemoved());
185 
186         if (add_this_clause(*cl) == add_cl_ret::unsat) {
187             return false;
188         }
189     }
190 
191     return true;
192 }
193 
194 ///returns whether we actually ran
doit()195 bool BreakID::doit()
196 {
197     assert(solver->okay());
198     assert(solver->decisionLevel() == 0);
199     num_lits_in_graph = 0;
200 
201     if (!solver->conf.doStrSubImplicit) {
202         if (solver->conf.verbosity) {
203             cout
204             << "c [breakid] cannot run BreakID without implicit submsumption, "
205             << "it would find too many (bad) symmetries"
206             << endl;
207         }
208         return false;
209     }
210 
211     if (solver->check_assumptions_contradict_foced_assignment()) {
212         if (solver->conf.verbosity) {
213             cout
214             << "c [breakid] forced assignements contradicted by assumptions, cannot run"
215             << endl;
216         }
217         return false;
218     }
219 
220     if (!check_limits()) {
221         return false;
222     }
223 
224     solver->clauseCleaner->remove_and_clean_all();
225     solver->subsumeImplicit->subsume_implicit(false, "-breakid");
226 
227     CompleteDetachReatacher reattacher(solver);
228     reattacher.detach_nonbins_nontris();
229 
230     if (!remove_duplicates()) {
231         return false;
232     }
233 
234     double myTime = cpuTime();
235     assert(breakid == NULL);
236     breakid = new BID::BreakID;
237     breakid->set_verbosity(0);
238     breakid->set_useMatrixDetection(solver->conf.breakid_matrix_detect);
239     breakid->set_symBreakingFormLength(solver->conf.breakid_max_constr_per_permut);
240     breakid->start_dynamic_cnf(solver->nVars());
241     if (solver->conf.verbosity) {
242         cout << "c [breakid] version " << breakid->get_sha1_version() << endl;
243     }
244 
245     if (!add_clauses()) {
246         delete breakid;
247         breakid = NULL;
248 
249         bool ok = reattacher.reattachLongs();
250         assert(ok);
251         return false;
252     }
253     set_up_time_lim();
254 
255     // Detect symmetries, detect subgroups
256     breakid->end_dynamic_cnf();
257     if (solver->conf.verbosity) {
258         cout << "c [breakid] Generators: " << breakid->get_num_generators() << endl;
259     }
260     if (solver->conf.verbosity > 3) {
261         breakid->print_generators(std::cout);
262     }
263 
264     if (solver->conf.verbosity >= 2) {
265         cout << "c [breakid] Detecting subgroups..." << endl;
266     }
267     breakid->detect_subgroups();
268 
269     if (solver->conf.verbosity > 3) {
270         breakid->print_subgroups(cout);
271     }
272 
273     // Break symmetries
274     breakid->break_symm();
275 
276     //reattach clauses
277     bool ok = reattacher.reattachLongs();
278     assert(ok);
279 
280     if (breakid->get_num_break_cls() != 0) {
281         break_symms_in_cms();
282     }
283 
284     get_outer_permutations();
285 
286 
287     // Finish up
288     double time_used = cpuTime() - myTime;
289     int64_t remain = breakid->get_steps_remain();
290     bool time_out = remain <= 0;
291     double time_remain = float_div(remain, set_time_lim);
292     if (solver->conf.verbosity) {
293         cout << "c [breakid] finished "
294         << solver->conf.print_times(time_used, time_out, time_remain)
295         << endl;
296     }
297     if (solver->sqlStats) {
298         solver->sqlStats->time_passed(
299             solver
300             , "breakid"
301             , time_used
302             , time_out
303             , time_remain
304         );
305     }
306 
307     delete breakid;
308     breakid = NULL;
309 
310     return false;
311 }
312 
get_outer_permutations()313 void BreakID::get_outer_permutations()
314 {
315     vector<unordered_map<BID::BLit, BID::BLit>> perms_inter;
316     breakid->get_perms(&perms_inter);
317     for(const auto& p: perms_inter) {
318         unordered_map<Lit, Lit> outer;
319         for(const auto& mymap: p) {
320             Lit from = Lit::toLit(mymap.first.toInt());
321             Lit to = Lit::toLit(mymap.second.toInt());
322 
323             from = solver->map_inter_to_outer(from);
324             to = solver->map_inter_to_outer(to);
325 
326             outer[from] = to;
327         }
328         perms_outer.push_back(outer);
329     }
330 }
331 
check_limits()332 bool BreakID::check_limits()
333 {
334     uint64_t tot_num_cls = solver->longIrredCls.size()+solver->binTri.irredBins;
335     uint64_t tot_num_lits = solver->litStats.irredLits + solver->binTri.irredBins*2;
336     if (solver->nVars() > solver->conf.breakid_vars_limit_K*1000ULL) {
337         if (solver->conf.verbosity) {
338             cout
339             << "c [breakid] max var limit exceeded, not running."
340             << " Num vars: " << print_value_kilo_mega(solver->nVars(), false)
341             << endl;
342         }
343         return false;
344     }
345 
346     if (tot_num_cls > solver->conf.breakid_cls_limit_K*1000ULL) {
347         if (solver->conf.verbosity) {
348             cout
349             << "c [breakid] max clause limit exceeded, not running."
350             << " Num clauses: " << print_value_kilo_mega(tot_num_cls, false)
351             << endl;
352         }
353         return false;
354     }
355     if (tot_num_lits > solver->conf.breakid_lits_limit_K*1000ULL) {
356         if (solver->conf.verbosity) {
357             cout
358             << "c [breakid] max literals limit exceeded, not running."
359             << " Num lits: " << print_value_kilo_mega(tot_num_lits, false)
360             << endl;
361         }
362         return false;
363     }
364 
365     return true;
366 }
367 
remove_duplicates()368 bool BreakID::remove_duplicates()
369 {
370     double myTime = cpuTime();
371     dedup_cls.clear();
372 
373     for(ClOffset offs: solver->longIrredCls) {
374         Clause* cl = solver->cl_alloc.ptr(offs);
375         assert(!cl->freed());
376         assert(!cl->getRemoved());
377         assert(!cl->red());
378         std::sort(cl->begin(), cl->end());
379         cl->stats.hash_val = hash_clause(cl->getData(), cl->size());
380         dedup_cls.push_back(offs);
381     }
382 
383     std::sort(dedup_cls.begin(), dedup_cls.end(), EqCls(solver->cl_alloc));
384 
385     size_t old_size = dedup_cls.size();
386     if (dedup_cls.size() > 1 && true) {
387         vector<ClOffset>::iterator prev = dedup_cls.begin();
388         vector<ClOffset>::iterator i = dedup_cls.begin();
389         i++;
390         Clause* prevcl = solver->cl_alloc.ptr(*prev);
391         for(vector<ClOffset>::iterator end = dedup_cls.end(); i != end; i++) {
392             Clause* cl = solver->cl_alloc.ptr(*i);
393             if (!equiv(cl, prevcl)) {
394                 prev++;
395                 *prev = *i;
396                 prevcl = cl;
397             }
398         }
399         prev++;
400         dedup_cls.resize(prev-dedup_cls.begin());
401     }
402 
403     double time_used = cpuTime() - myTime;
404     if (solver->conf.verbosity >= 1) {
405         cout << "c [breakid] tmp-rem-dup cls"
406         << " dupl: " << print_value_kilo_mega(old_size-dedup_cls.size(), false)
407         << solver->conf.print_times(time_used)
408         <<  endl;
409     }
410     if (solver->sqlStats) {
411         solver->sqlStats->time_passed_min(
412             solver
413             , "breakid-rem-dup"
414             , time_used
415         );
416     }
417 
418     return true;
419 }
420 
break_symms_in_cms()421 void BreakID::break_symms_in_cms()
422 {
423     if (solver->conf.verbosity) {
424         cout << "c [breakid] Breaking cls: "<< breakid->get_num_break_cls() << endl;
425         cout << "c [breakid] Aux vars: "<< breakid->get_num_aux_vars() << endl;
426     }
427     for(uint32_t i = 0; i < breakid->get_num_aux_vars(); i++) {
428         solver->new_var(true);
429     }
430     if (solver->conf.breakid_use_assump) {
431         if (symm_var == var_Undef) {
432             solver->new_var(true);
433             symm_var = solver->nVars()-1;
434             solver->add_assumption(Lit(symm_var, true));
435         }
436         assert(solver->varData[symm_var].removed == Removed::none);
437     }
438 
439     auto brk = breakid->get_brk_cls();
440     for (const auto& cl: brk) {
441         vector<Lit>* cl2 = (vector<Lit>*)&cl;
442         if (solver->conf.breakid_use_assump) {
443             cl2->push_back(Lit(symm_var, false));
444         }
445         for(const Lit& l: *cl2) {
446             assert(l.var() < solver->nVars());
447             if (solver->conf.breakid_use_assump) {
448                 assert(solver->value(l) == l_Undef);
449             }
450         }
451         Clause* newcl = solver->add_clause_int(*cl2
452             , false //redundant
453             , ClauseStats() //stats
454             , true //attach
455             , NULL //return simplified
456             , false //DRAT... oops does not work right now
457             , lit_Undef
458         );
459         if (newcl != NULL) {
460             ClOffset offset = solver->cl_alloc.get_offset(newcl);
461             solver->longIrredCls.push_back(offset);
462         }
463     }
464 }
465 
finished_solving()466 void BreakID::finished_solving()
467 {
468     //Nothing actually
469 }
470 
start_new_solving()471 void BreakID::start_new_solving()
472 {
473     assert(solver->decisionLevel() == 0);
474     assert(solver->okay());
475     if (symm_var == var_Undef) {
476         return;
477     }
478 
479     assert(solver->varData[symm_var].removed == Removed::none);
480     assert(solver->value(symm_var) != l_False
481         && "The symm var can never be foreced to FALSE, logic error");
482 
483     //In certain conditions, in particular when the problem is UNSAT
484     //the symmetry assumption var can be forced to TRUE at level 0
485     if (solver->value(symm_var) == l_True) {
486         symm_var = var_Undef;
487         return;
488     }
489 
490     assert(solver->value(symm_var) == l_Undef);
491     solver->enqueue(Lit(symm_var, false));
492     PropBy ret = solver->propagate<false>();
493     assert(ret == PropBy() && "Must not fail on resetting symmetry var");
494     symm_var = var_Undef;
495 }
496 
497 
update_var_after_varreplace()498 void BreakID::update_var_after_varreplace()
499 {
500     if (symm_var != var_Undef) {
501         symm_var = solver->varReplacer->get_var_replaced_with(symm_var);
502     }
503 }
504