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