1 /********************* */
2 /*! \file bv_subtheory_algebraic.cpp
3 ** \verbatim
4 ** Top contributors (to current version):
5 ** Liana Hadarean, Aina Niemetz, Tim King
6 ** This file is part of the CVC4 project.
7 ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS
8 ** in the top-level source directory) and their institutional affiliations.
9 ** All rights reserved. See the file COPYING in the top-level source
10 ** directory for licensing information.\endverbatim
11 **
12 ** \brief Algebraic solver.
13 **
14 ** Algebraic solver.
15 **/
16 #include "theory/bv/bv_subtheory_algebraic.h"
17
18 #include <unordered_set>
19
20 #include "expr/node_algorithm.h"
21 #include "options/bv_options.h"
22 #include "smt/smt_statistics_registry.h"
23 #include "smt_util/boolean_simplification.h"
24 #include "theory/bv/bv_quick_check.h"
25 #include "theory/bv/theory_bv.h"
26 #include "theory/bv/theory_bv_utils.h"
27 #include "theory/theory_model.h"
28
29 using namespace CVC4::context;
30 using namespace CVC4::prop;
31 using namespace CVC4::theory::bv::utils;
32 using namespace std;
33
34 namespace CVC4 {
35 namespace theory {
36 namespace bv {
37
38 /* ------------------------------------------------------------------------- */
39
40 namespace {
41
42 /* Collect all variables under a given a node. */
collectVariables(TNode node,utils::NodeSet & vars)43 void collectVariables(TNode node, utils::NodeSet& vars)
44 {
45 std::vector<TNode> stack;
46 std::unordered_set<TNode, TNodeHashFunction> visited;
47
48 stack.push_back(node);
49 while (!stack.empty())
50 {
51 Node n = stack.back();
52 stack.pop_back();
53
54 if (vars.find(n) != vars.end()) continue;
55 if (visited.find(n) != visited.end()) continue;
56 visited.insert(n);
57
58 if (Theory::isLeafOf(n, THEORY_BV) && n.getKind() != kind::CONST_BITVECTOR)
59 {
60 vars.insert(n);
61 continue;
62 }
63 stack.insert(stack.end(), n.begin(), n.end());
64 }
65 }
66
67 };
68
69 /* ------------------------------------------------------------------------- */
70
71 bool hasExpensiveBVOperators(TNode fact);
72 Node mergeExplanations(const std::vector<Node>& expls);
73 Node mergeExplanations(TNode expl1, TNode expl2);
74
75
SubstitutionEx(theory::SubstitutionMap * modelMap)76 SubstitutionEx::SubstitutionEx(theory::SubstitutionMap* modelMap)
77 : d_substitutions()
78 , d_cache()
79 , d_cacheInvalid(true)
80 , d_modelMap(modelMap)
81 {}
82
addSubstitution(TNode from,TNode to,TNode reason)83 bool SubstitutionEx::addSubstitution(TNode from, TNode to, TNode reason) {
84 Debug("bv-substitution") << "SubstitutionEx::addSubstitution: "<< from
85 <<" => "<< to << "\n" << " reason "<<reason << "\n";
86 Assert (from != to);
87 if (d_substitutions.find(from) != d_substitutions.end()) {
88 return false;
89 }
90
91 d_modelMap->addSubstitution(from, to);
92
93 d_cacheInvalid = true;
94 d_substitutions[from] = SubstitutionElement(to, reason);
95 return true;
96 }
97
apply(TNode node)98 Node SubstitutionEx::apply(TNode node) {
99 Debug("bv-substitution") << "SubstitutionEx::apply("<< node <<")\n";
100 if (d_cacheInvalid) {
101 d_cache.clear();
102 d_cacheInvalid = false;
103 }
104
105 SubstitutionsCache::iterator it = d_cache.find(node);
106
107 if (it != d_cache.end()) {
108 Node res = it->second.to;
109 Debug("bv-substitution") << " =>"<< res <<"\n";
110 return res;
111 }
112
113 Node result = internalApply(node);
114 Debug("bv-substitution") << " =>"<< result <<"\n";
115 return result;
116 }
117
internalApply(TNode node)118 Node SubstitutionEx::internalApply(TNode node) {
119 if (d_substitutions.empty())
120 return node;
121
122 vector<SubstitutionStackElement> stack;
123 stack.push_back(SubstitutionStackElement(node));
124
125 while (!stack.empty()) {
126 SubstitutionStackElement head = stack.back();
127 stack.pop_back();
128
129 TNode current = head.node;
130
131 if (hasCache(current)) {
132 continue;
133 }
134
135 // check if it has substitution
136 Substitutions::const_iterator it = d_substitutions.find(current);
137 if (it != d_substitutions.end()) {
138 vector<Node> reasons;
139 TNode to = it->second.to;
140 reasons.push_back(it->second.reason);
141 // check if the thing we subsituted to has substitutions
142 TNode res = internalApply(to);
143 // update reasons
144 reasons.push_back(getReason(to));
145 Node reason = mergeExplanations(reasons);
146 storeCache(current, res, reason);
147 continue;
148 }
149
150 // if no children then just continue
151 if(current.getNumChildren() == 0) {
152 storeCache(current, current, utils::mkTrue());
153 continue;
154 }
155
156 // children already processed
157 if (head.childrenAdded) {
158 NodeBuilder<> nb(current.getKind());
159 std::vector<Node> reasons;
160
161 if (current.getMetaKind() == kind::metakind::PARAMETERIZED) {
162 TNode op = current.getOperator();
163 Assert (hasCache(op));
164 nb << getCache(op);
165 reasons.push_back(getReason(op));
166 }
167 for (unsigned i = 0; i < current.getNumChildren(); ++i) {
168 Assert (hasCache(current[i]));
169 nb << getCache(current[i]);
170 reasons.push_back(getReason(current[i]));
171 }
172 Node result = nb;
173 // if the node is new apply substitutions to it
174 Node subst_result = result;
175 if (result != current) {
176 subst_result = result!= current? internalApply(result) : result;
177 reasons.push_back(getReason(result));
178 }
179 Node reason = mergeExplanations(reasons);
180 storeCache(current, subst_result, reason);
181 continue;
182 } else {
183 // add children to stack
184 stack.push_back(SubstitutionStackElement(current, true));
185 if (current.getMetaKind() == kind::metakind::PARAMETERIZED) {
186 stack.push_back(SubstitutionStackElement(current.getOperator()));
187 }
188 for (unsigned i = 0; i < current.getNumChildren(); ++i) {
189 stack.push_back(SubstitutionStackElement(current[i]));
190 }
191 }
192 }
193
194 Assert(hasCache(node));
195 return getCache(node);
196 }
197
explain(TNode node) const198 Node SubstitutionEx::explain(TNode node) const {
199 if(!hasCache(node)) {
200 return utils::mkTrue();
201 }
202
203 Debug("bv-substitution") << "SubstitutionEx::explain("<< node <<")\n";
204 Node res = getReason(node);
205 Debug("bv-substitution") << " with "<< res <<"\n";
206 return res;
207 }
208
getReason(TNode node) const209 Node SubstitutionEx::getReason(TNode node) const {
210 Assert(hasCache(node));
211 SubstitutionsCache::const_iterator it = d_cache.find(node);
212 return it->second.reason;
213 }
214
hasCache(TNode node) const215 bool SubstitutionEx::hasCache(TNode node) const {
216 return d_cache.find(node) != d_cache.end();
217 }
218
getCache(TNode node) const219 Node SubstitutionEx::getCache(TNode node) const {
220 Assert (hasCache(node));
221 return d_cache.find(node)->second.to;
222 }
223
storeCache(TNode from,TNode to,Node reason)224 void SubstitutionEx::storeCache(TNode from, TNode to, Node reason) {
225 // Debug("bv-substitution") << "SubstitutionEx::storeCache(" << from <<", " << to <<", "<< reason<<")\n";
226 Assert (!hasCache(from));
227 d_cache[from] = SubstitutionElement(to, reason);
228 }
229
AlgebraicSolver(context::Context * c,TheoryBV * bv)230 AlgebraicSolver::AlgebraicSolver(context::Context* c, TheoryBV* bv)
231 : SubtheorySolver(c, bv),
232 d_modelMap(),
233 d_quickSolver(new BVQuickCheck("theory::bv::algebraic", bv)),
234 d_isComplete(c, false),
235 d_isDifficult(c, false),
236 d_budget(options::bitvectorAlgebraicBudget()),
237 d_explanations(),
238 d_inputAssertions(),
239 d_ids(),
240 d_numSolved(0),
241 d_numCalls(0),
242 d_quickXplain(),
243 d_statistics()
244 {
245 if (options::bitvectorQuickXplain())
246 {
247 d_quickXplain.reset(
248 new QuickXPlain("theory::bv::algebraic", d_quickSolver.get()));
249 }
250 }
251
~AlgebraicSolver()252 AlgebraicSolver::~AlgebraicSolver() {}
253
check(Theory::Effort e)254 bool AlgebraicSolver::check(Theory::Effort e)
255 {
256 Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY);
257
258 if (!Theory::fullEffort(e)) { return true; }
259 if (!useHeuristic()) { return true; }
260
261 TimerStat::CodeTimer algebraicTimer(d_statistics.d_solveTime);
262 Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check (" << e << ")\n";
263 ++(d_numCalls);
264 ++(d_statistics.d_numCallstoCheck);
265
266 d_explanations.clear();
267 d_ids.clear();
268 d_inputAssertions.clear();
269
270 std::vector<WorklistElement> worklist;
271
272 uint64_t original_bb_cost = 0;
273
274 NodeManager* nm = NodeManager::currentNM();
275 NodeSet seen_assertions;
276 // Processing assertions from scratch
277 for (AssertionQueue::const_iterator it = assertionsBegin(); it != assertionsEnd(); ++it) {
278 Debug("bv-subtheory-algebraic") << " " << *it << "\n";
279 TNode assertion = *it;
280 unsigned id = worklist.size();
281 d_ids[assertion] = id;
282 worklist.push_back(WorklistElement(assertion, id));
283 d_inputAssertions.insert(assertion);
284 storeExplanation(assertion);
285
286 uint64_t assertion_size = d_quickSolver->computeAtomWeight(assertion, seen_assertions);
287 Assert (original_bb_cost <= original_bb_cost + assertion_size);
288 original_bb_cost+= assertion_size;
289 }
290
291 for (unsigned i = 0; i < worklist.size(); ++i) {
292 d_ids[worklist[i].node] = worklist[i].id;
293 }
294
295 Debug("bv-subtheory-algebraic") << "Assertions " << worklist.size() <<" : \n";
296
297 Assert (d_explanations.size() == worklist.size());
298
299 d_modelMap.reset(new SubstitutionMap(d_context));
300 SubstitutionEx subst(d_modelMap.get());
301
302 // first round of substitutions
303 processAssertions(worklist, subst);
304
305 if (!d_isDifficult.get()) {
306 // skolemize all possible extracts
307 ExtractSkolemizer skolemizer(d_modelMap.get());
308 skolemizer.skolemize(worklist);
309 // second round of substitutions
310 processAssertions(worklist, subst);
311 }
312
313 NodeSet subst_seen;
314 uint64_t subst_bb_cost = 0;
315
316 unsigned r = 0;
317 unsigned w = 0;
318
319 for (; r < worklist.size(); ++r) {
320
321 TNode fact = worklist[r].node;
322 unsigned id = worklist[r].id;
323
324 if (Dump.isOn("bv-algebraic")) {
325 Node expl = d_explanations[id];
326 Node query = utils::mkNot(nm->mkNode(kind::IMPLIES, expl, fact));
327 Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation");
328 Dump("bv-algebraic") << PushCommand();
329 Dump("bv-algebraic") << AssertCommand(query.toExpr());
330 Dump("bv-algebraic") << CheckSatCommand();
331 Dump("bv-algebraic") << PopCommand();
332 }
333
334 if (fact.isConst() &&
335 fact.getConst<bool>() == true) {
336 continue;
337 }
338
339 if (fact.isConst() &&
340 fact.getConst<bool>() == false) {
341 // we have a conflict
342 Node conflict = BooleanSimplification::simplify(d_explanations[id]);
343 d_bv->setConflict(conflict);
344 d_isComplete.set(true);
345 Debug("bv-subtheory-algebraic") << " UNSAT: assertion simplfies to false with conflict: "<< conflict << "\n";
346
347 if (Dump.isOn("bv-algebraic")) {
348 Dump("bv-algebraic") << EchoCommand("TheoryBV::AlgebraicSolver::conflict");
349 Dump("bv-algebraic") << PushCommand();
350 Dump("bv-algebraic") << AssertCommand(conflict.toExpr());
351 Dump("bv-algebraic") << CheckSatCommand();
352 Dump("bv-algebraic") << PopCommand();
353 }
354
355
356 ++(d_statistics.d_numSimplifiesToFalse);
357 ++(d_numSolved);
358 return false;
359 }
360
361 subst_bb_cost+= d_quickSolver->computeAtomWeight(fact, subst_seen);
362 worklist[w] = WorklistElement(fact, id);
363 Node expl = BooleanSimplification::simplify(d_explanations[id]);
364 storeExplanation(id, expl);
365 d_ids[fact] = id;
366 ++w;
367 }
368
369 worklist.resize(w);
370
371
372 if(Debug.isOn("bv-subtheory-algebraic")) {
373 Debug("bv-subtheory-algebraic") << "Assertions post-substitutions " << worklist.size() << ":\n";
374 for (unsigned i = 0; i < worklist.size(); ++i) {
375 Debug("bv-subtheory-algebraic") << " " << worklist[i].node << "\n";
376 }
377 }
378
379
380 // all facts solved to true
381 if (worklist.empty()) {
382 Debug("bv-subtheory-algebraic") << " SAT: everything simplifies to true.\n";
383 ++(d_statistics.d_numSimplifiesToTrue);
384 ++(d_numSolved);
385 return true;
386 }
387
388 double ratio = ((double)subst_bb_cost)/original_bb_cost;
389 if (ratio > 0.5 ||
390 !d_isDifficult.get()) {
391 // give up if problem not reduced enough
392 d_isComplete.set(false);
393 return true;
394 }
395
396 d_quickSolver->clearSolver();
397
398 d_quickSolver->push();
399 std::vector<Node> facts;
400 for (unsigned i = 0; i < worklist.size(); ++i) {
401 facts.push_back(worklist[i].node);
402 }
403 bool ok = quickCheck(facts);
404
405 Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check done " << ok << ".\n";
406 return ok;
407 }
408
quickCheck(std::vector<Node> & facts)409 bool AlgebraicSolver::quickCheck(std::vector<Node>& facts) {
410 SatValue res = d_quickSolver->checkSat(facts, d_budget);
411
412 if (res == SAT_VALUE_UNKNOWN) {
413 d_isComplete.set(false);
414 Debug("bv-subtheory-algebraic") << " Unknown.\n";
415 ++(d_statistics.d_numUnknown);
416 return true;
417 }
418
419 if (res == SAT_VALUE_TRUE) {
420 Debug("bv-subtheory-algebraic") << " Sat.\n";
421 ++(d_statistics.d_numSat);
422 ++(d_numSolved);
423 d_isComplete.set(true);
424 return true;
425 }
426
427 Assert (res == SAT_VALUE_FALSE);
428 Assert (d_quickSolver->inConflict());
429 d_isComplete.set(true);
430 Debug("bv-subtheory-algebraic") << " Unsat.\n";
431 ++(d_numSolved);
432 ++(d_statistics.d_numUnsat);
433
434
435 Node conflict = d_quickSolver->getConflict();
436 Debug("bv-subtheory-algebraic") << " Conflict: " << conflict << "\n";
437
438 // singleton conflict
439 if (conflict.getKind() != kind::AND) {
440 Assert (d_ids.find(conflict) != d_ids.end());
441 unsigned id = d_ids[conflict];
442 Assert (id < d_explanations.size());
443 Node theory_confl = d_explanations[id];
444 d_bv->setConflict(theory_confl);
445 return false;
446 }
447
448 Assert (conflict.getKind() == kind::AND);
449 if (options::bitvectorQuickXplain()) {
450 d_quickSolver->popToZero();
451 Debug("bv-quick-xplain") << "AlgebraicSolver::quickCheck original conflict size " << conflict.getNumChildren() << "\n";
452 conflict = d_quickXplain->minimizeConflict(conflict);
453 Debug("bv-quick-xplain") << "AlgebraicSolver::quickCheck minimized conflict size " << conflict.getNumChildren() << "\n";
454 }
455
456 vector<TNode> theory_confl;
457 for (unsigned i = 0; i < conflict.getNumChildren(); ++i) {
458 TNode c = conflict[i];
459
460 Assert (d_ids.find(c) != d_ids.end());
461 unsigned c_id = d_ids[c];
462 Assert (c_id < d_explanations.size());
463 TNode c_expl = d_explanations[c_id];
464 theory_confl.push_back(c_expl);
465 }
466
467 Node confl = BooleanSimplification::simplify(utils::mkAnd(theory_confl));
468
469 Debug("bv-subtheory-algebraic") << " Out Conflict: " << confl << "\n";
470 setConflict(confl);
471 return false;
472 }
473
setConflict(TNode conflict)474 void AlgebraicSolver::setConflict(TNode conflict) {
475 Node final_conflict = conflict;
476 if (options::bitvectorQuickXplain() &&
477 conflict.getKind() == kind::AND &&
478 conflict.getNumChildren() > 4) {
479 final_conflict = d_quickXplain->minimizeConflict(conflict);
480 }
481 d_bv->setConflict(final_conflict);
482 }
483
solve(TNode fact,TNode reason,SubstitutionEx & subst)484 bool AlgebraicSolver::solve(TNode fact, TNode reason, SubstitutionEx& subst) {
485 if (fact.getKind() != kind::EQUAL) return false;
486
487 NodeManager* nm = NodeManager::currentNM();
488 TNode left = fact[0];
489 TNode right = fact[1];
490
491 if (left.isVar() && !expr::hasSubterm(right, left))
492 {
493 bool changed = subst.addSubstitution(left, right, reason);
494 return changed;
495 }
496 if (right.isVar() && !expr::hasSubterm(left, right))
497 {
498 bool changed = subst.addSubstitution(right, left, reason);
499 return changed;
500 }
501
502 // xor simplification
503 if (right.getKind() == kind::BITVECTOR_XOR &&
504 left.getKind() == kind::BITVECTOR_XOR) {
505 TNode var = left[0];
506 if (var.getMetaKind() != kind::metakind::VARIABLE)
507 return false;
508
509 // simplify xor with same variable on both sides
510 if (expr::hasSubterm(right, var))
511 {
512 std::vector<Node> right_children;
513 for (unsigned i = 0; i < right.getNumChildren(); ++i) {
514 if (right[i] != var)
515 right_children.push_back(right[i]);
516 }
517 Assert (right_children.size());
518 Node new_right = utils::mkNaryNode(kind::BITVECTOR_XOR, right_children);
519 std::vector<Node> left_children;
520 for (unsigned i = 1; i < left.getNumChildren(); ++i) {
521 left_children.push_back(left[i]);
522 }
523 Node new_left = utils::mkNaryNode(kind::BITVECTOR_XOR, left_children);
524 Node new_fact = nm->mkNode(kind::EQUAL, new_left, new_right);
525 bool changed = subst.addSubstitution(fact, new_fact, reason);
526 return changed;
527 }
528
529 NodeBuilder<> nb(kind::BITVECTOR_XOR);
530 for (unsigned i = 1; i < left.getNumChildren(); ++i) {
531 nb << left[i];
532 }
533 Node inverse = left.getNumChildren() == 2? (Node)left[1] : (Node)nb;
534 Node new_right = nm->mkNode(kind::BITVECTOR_XOR, right, inverse);
535 bool changed = subst.addSubstitution(var, new_right, reason);
536
537 if (Dump.isOn("bv-algebraic")) {
538 Node query = utils::mkNot(nm->mkNode(
539 kind::EQUAL, fact, nm->mkNode(kind::EQUAL, var, new_right)));
540 Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation");
541 Dump("bv-algebraic") << PushCommand();
542 Dump("bv-algebraic") << AssertCommand(query.toExpr());
543 Dump("bv-algebraic") << CheckSatCommand();
544 Dump("bv-algebraic") << PopCommand();
545 }
546
547
548 return changed;
549 }
550
551 // (a xor t = a) <=> (t = 0)
552 if (left.getKind() == kind::BITVECTOR_XOR
553 && right.getMetaKind() == kind::metakind::VARIABLE
554 && expr::hasSubterm(left, right))
555 {
556 TNode var = right;
557 Node new_left = nm->mkNode(kind::BITVECTOR_XOR, var, left);
558 Node zero = utils::mkConst(utils::getSize(var), 0u);
559 Node new_fact = nm->mkNode(kind::EQUAL, zero, new_left);
560 bool changed = subst.addSubstitution(fact, new_fact, reason);
561 return changed;
562 }
563
564 if (right.getKind() == kind::BITVECTOR_XOR
565 && left.getMetaKind() == kind::metakind::VARIABLE
566 && expr::hasSubterm(right, left))
567 {
568 TNode var = left;
569 Node new_right = nm->mkNode(kind::BITVECTOR_XOR, var, right);
570 Node zero = utils::mkConst(utils::getSize(var), 0u);
571 Node new_fact = nm->mkNode(kind::EQUAL, zero, new_right);
572 bool changed = subst.addSubstitution(fact, new_fact, reason);
573 return changed;
574 }
575
576 // (a xor b = 0) <=> (a = b)
577 if (left.getKind() == kind::BITVECTOR_XOR &&
578 left.getNumChildren() == 2 &&
579 right.getKind() == kind::CONST_BITVECTOR &&
580 right.getConst<BitVector>() == BitVector(utils::getSize(left), 0u)) {
581 Node new_fact = nm->mkNode(kind::EQUAL, left[0], left[1]);
582 bool changed = subst.addSubstitution(fact, new_fact, reason);
583 return changed;
584 }
585
586
587 return false;
588 }
589
isSubstitutableIn(TNode node,TNode in)590 bool AlgebraicSolver::isSubstitutableIn(TNode node, TNode in)
591 {
592 if (node.getMetaKind() == kind::metakind::VARIABLE
593 && !expr::hasSubterm(in, node))
594 return true;
595 return false;
596 }
597
processAssertions(std::vector<WorklistElement> & worklist,SubstitutionEx & subst)598 void AlgebraicSolver::processAssertions(std::vector<WorklistElement>& worklist, SubstitutionEx& subst) {
599 NodeManager* nm = NodeManager::currentNM();
600 bool changed = true;
601 while(changed) {
602 // d_bv->spendResource();
603 changed = false;
604 for (unsigned i = 0; i < worklist.size(); ++i) {
605 // apply current substitutions
606 Node current = subst.apply(worklist[i].node);
607 unsigned current_id = worklist[i].id;
608 Node subst_expl = subst.explain(worklist[i].node);
609 worklist[i] = WorklistElement(Rewriter::rewrite(current), current_id);
610 // explanation for this assertion
611 Node old_expl = d_explanations[current_id];
612 Node new_expl = mergeExplanations(subst_expl, old_expl);
613 storeExplanation(current_id, new_expl);
614
615 // use the new substitution to solve
616 if(solve(worklist[i].node, new_expl, subst)) {
617 changed = true;
618 }
619 }
620
621 // check for concat slicings
622 for (unsigned i = 0; i < worklist.size(); ++i) {
623 TNode fact = worklist[i].node;
624 unsigned current_id = worklist[i].id;
625
626 if (fact.getKind() != kind::EQUAL) {
627 continue;
628 }
629
630 TNode left = fact[0];
631 TNode right = fact[1];
632 if (left.getKind() != kind::BITVECTOR_CONCAT ||
633 right.getKind() != kind::BITVECTOR_CONCAT ||
634 left.getNumChildren() != right.getNumChildren()) {
635 continue;
636 }
637
638 bool can_slice = true;
639 for (unsigned j = 0; j < left.getNumChildren(); ++j) {
640 if (utils::getSize(left[j]) != utils::getSize(right[j]))
641 can_slice = false;
642 }
643
644 if (!can_slice) {
645 continue;
646 }
647
648 for (unsigned j = 0; j < left.getNumChildren(); ++j) {
649 Node eq_j = nm->mkNode(kind::EQUAL, left[j], right[j]);
650 unsigned id = d_explanations.size();
651 TNode expl = d_explanations[current_id];
652 storeExplanation(expl);
653 worklist.push_back(WorklistElement(eq_j, id));
654 d_ids[eq_j] = id;
655 }
656 worklist[i] = WorklistElement(utils::mkTrue(), worklist[i].id);
657 changed = true;
658 }
659 Assert (d_explanations.size() == worklist.size());
660 }
661 }
662
storeExplanation(unsigned id,TNode explanation)663 void AlgebraicSolver::storeExplanation(unsigned id, TNode explanation) {
664 Assert (checkExplanation(explanation));
665 d_explanations[id] = explanation;
666 }
667
storeExplanation(TNode explanation)668 void AlgebraicSolver::storeExplanation(TNode explanation) {
669 Assert (checkExplanation(explanation));
670 d_explanations.push_back(explanation);
671 }
672
checkExplanation(TNode explanation)673 bool AlgebraicSolver::checkExplanation(TNode explanation) {
674 Node simplified_explanation = explanation; //BooleanSimplification::simplify(explanation);
675 if (simplified_explanation.getKind() != kind::AND) {
676 return d_inputAssertions.find(simplified_explanation) != d_inputAssertions.end();
677 }
678 for (unsigned i = 0; i < simplified_explanation.getNumChildren(); ++i) {
679 if (d_inputAssertions.find(simplified_explanation[i]) == d_inputAssertions.end()) {
680 return false;
681 }
682 }
683 return true;
684 }
685
686
isComplete()687 bool AlgebraicSolver::isComplete() {
688 return d_isComplete.get();
689 }
690
useHeuristic()691 bool AlgebraicSolver::useHeuristic() {
692 if (d_numCalls == 0)
693 return true;
694
695 double success_rate = double(d_numSolved)/double(d_numCalls);
696 d_statistics.d_useHeuristic.setData(success_rate);
697 return success_rate > 0.8;
698 }
699
700
assertFact(TNode fact)701 void AlgebraicSolver::assertFact(TNode fact) {
702 d_assertionQueue.push_back(fact);
703 d_isComplete.set(false);
704 if (!d_isDifficult.get()) {
705 d_isDifficult.set(hasExpensiveBVOperators(fact));
706 }
707 }
708
getEqualityStatus(TNode a,TNode b)709 EqualityStatus AlgebraicSolver::getEqualityStatus(TNode a, TNode b) {
710 return EQUALITY_UNKNOWN;
711 }
712
collectModelInfo(TheoryModel * model,bool fullModel)713 bool AlgebraicSolver::collectModelInfo(TheoryModel* model, bool fullModel)
714 {
715 Debug("bitvector-model") << "AlgebraicSolver::collectModelInfo\n";
716 AlwaysAssert (!d_quickSolver->inConflict());
717 set<Node> termSet;
718 d_bv->computeRelevantTerms(termSet);
719
720 // collect relevant terms that the bv theory abstracts to variables
721 // (variables and parametric terms such as select apply_uf)
722 std::vector<TNode> variables;
723 std::vector<Node> values;
724 for (set<Node>::const_iterator it = termSet.begin(); it != termSet.end(); ++it) {
725 TNode term = *it;
726 if (term.getType().isBitVector() &&
727 (term.getMetaKind() == kind::metakind::VARIABLE ||
728 Theory::theoryOf(term) != THEORY_BV)) {
729 variables.push_back(term);
730 values.push_back(term);
731 }
732 }
733
734 NodeSet leaf_vars;
735 Debug("bitvector-model") << "Substitutions:\n";
736 for (unsigned i = 0; i < variables.size(); ++i) {
737 TNode current = variables[i];
738 TNode subst = Rewriter::rewrite(d_modelMap->apply(current));
739 Debug("bitvector-model") << " " << current << " => " << subst << "\n";
740 values[i] = subst;
741 collectVariables(subst, leaf_vars);
742 }
743
744 Debug("bitvector-model") << "Model:\n";
745
746 for (NodeSet::const_iterator it = leaf_vars.begin(); it != leaf_vars.end(); ++it) {
747 TNode var = *it;
748 Node value = d_quickSolver->getVarValue(var, true);
749 Assert (!value.isNull() || !fullModel);
750
751 // may be a shared term that did not appear in the current assertions
752 // AJR: need to check whether already in map for cases where collectModelInfo is called multiple times in the same context
753 if (!value.isNull() && !d_modelMap->hasSubstitution(var)) {
754 Debug("bitvector-model") << " " << var << " => " << value << "\n";
755 Assert (value.getKind() == kind::CONST_BITVECTOR);
756 d_modelMap->addSubstitution(var, value);
757 }
758 }
759
760 Debug("bitvector-model") << "Final Model:\n";
761 for (unsigned i = 0; i < variables.size(); ++i) {
762 TNode current = values[i];
763 TNode subst = Rewriter::rewrite(d_modelMap->apply(current));
764 Debug("bitvector-model") << "AlgebraicSolver: " << variables[i] << " => " << subst << "\n";
765 // Doesn't have to be constant as it may be irrelevant
766 Assert (subst.getKind() == kind::CONST_BITVECTOR);
767 if (!model->assertEquality(variables[i], subst, true))
768 {
769 return false;
770 }
771 }
772 return true;
773 }
774
getModelValue(TNode node)775 Node AlgebraicSolver::getModelValue(TNode node) {
776 return Node::null();
777 }
778
Statistics()779 AlgebraicSolver::Statistics::Statistics()
780 : d_numCallstoCheck("theory::bv::algebraic::NumCallsToCheck", 0)
781 , d_numSimplifiesToTrue("theory::bv::algebraic::NumSimplifiesToTrue", 0)
782 , d_numSimplifiesToFalse("theory::bv::algebraic::NumSimplifiesToFalse", 0)
783 , d_numUnsat("theory::bv::algebraic::NumUnsat", 0)
784 , d_numSat("theory::bv::algebraic::NumSat", 0)
785 , d_numUnknown("theory::bv::algebraic::NumUnknown", 0)
786 , d_solveTime("theory::bv::algebraic::SolveTime")
787 , d_useHeuristic("theory::bv::algebraic::UseHeuristic", 0.2)
788 {
789 smtStatisticsRegistry()->registerStat(&d_numCallstoCheck);
790 smtStatisticsRegistry()->registerStat(&d_numSimplifiesToTrue);
791 smtStatisticsRegistry()->registerStat(&d_numSimplifiesToFalse);
792 smtStatisticsRegistry()->registerStat(&d_numUnsat);
793 smtStatisticsRegistry()->registerStat(&d_numSat);
794 smtStatisticsRegistry()->registerStat(&d_numUnknown);
795 smtStatisticsRegistry()->registerStat(&d_solveTime);
796 smtStatisticsRegistry()->registerStat(&d_useHeuristic);
797 }
798
~Statistics()799 AlgebraicSolver::Statistics::~Statistics() {
800 smtStatisticsRegistry()->unregisterStat(&d_numCallstoCheck);
801 smtStatisticsRegistry()->unregisterStat(&d_numSimplifiesToTrue);
802 smtStatisticsRegistry()->unregisterStat(&d_numSimplifiesToFalse);
803 smtStatisticsRegistry()->unregisterStat(&d_numUnsat);
804 smtStatisticsRegistry()->unregisterStat(&d_numSat);
805 smtStatisticsRegistry()->unregisterStat(&d_numUnknown);
806 smtStatisticsRegistry()->unregisterStat(&d_solveTime);
807 smtStatisticsRegistry()->unregisterStat(&d_useHeuristic);
808 }
809
hasExpensiveBVOperatorsRec(TNode fact,TNodeSet & seen)810 bool hasExpensiveBVOperatorsRec(TNode fact, TNodeSet& seen) {
811 if (fact.getKind() == kind::BITVECTOR_MULT ||
812 fact.getKind() == kind::BITVECTOR_UDIV_TOTAL ||
813 fact.getKind() == kind::BITVECTOR_UREM_TOTAL) {
814 return true;
815 }
816
817 if (seen.find(fact) != seen.end()) {
818 return false;
819 }
820
821 if (fact.getNumChildren() == 0) {
822 return false;
823 }
824 for (unsigned i = 0; i < fact.getNumChildren(); ++i) {
825 bool difficult = hasExpensiveBVOperatorsRec(fact[i], seen);
826 if (difficult)
827 return true;
828 }
829 seen.insert(fact);
830 return false;
831 }
832
hasExpensiveBVOperators(TNode fact)833 bool hasExpensiveBVOperators(TNode fact) {
834 TNodeSet seen;
835 return hasExpensiveBVOperatorsRec(fact, seen);
836 }
837
skolemize(std::vector<WorklistElement> & facts)838 void ExtractSkolemizer::skolemize(std::vector<WorklistElement>& facts) {
839 TNodeSet seen;
840 for (unsigned i = 0; i < facts.size(); ++i) {
841 TNode current = facts[i].node;
842 collectExtracts(current, seen);
843 }
844
845 for (VarExtractMap::iterator it = d_varToExtract.begin(); it != d_varToExtract.end(); ++it) {
846 ExtractList& el = it->second;
847 TNode var = it->first;
848 Base& base = el.base;
849
850 unsigned bw = utils::getSize(var);
851 // compute decomposition
852 std::vector<unsigned> cuts;
853 for (unsigned i = 1; i <= bw; ++i) {
854 if (base.isCutPoint(i)) {
855 cuts.push_back(i);
856 }
857 }
858 unsigned previous = 0;
859 unsigned current = 0;
860 std::vector<Node> skolems;
861 for (unsigned i = 0; i < cuts.size(); ++i) {
862 current = cuts[i];
863 Assert (current > 0);
864 int size = current - previous;
865 Assert (size > 0);
866 Node sk = utils::mkVar(size);
867 skolems.push_back(sk);
868 previous = current;
869 }
870 if (current < bw -1) {
871 int size = bw - current;
872 Assert (size > 0);
873 Node sk = utils::mkVar(size);
874 skolems.push_back(sk);
875 }
876 NodeBuilder<> skolem_nb(kind::BITVECTOR_CONCAT);
877
878 for (int i = skolems.size() - 1; i >= 0; --i) {
879 skolem_nb << skolems[i];
880 }
881
882 Node skolem_concat = skolems.size() == 1 ? (Node)skolems[0] : (Node) skolem_nb;
883 Assert (utils::getSize(skolem_concat) == utils::getSize(var));
884 storeSkolem(var, skolem_concat);
885
886 for (unsigned i = 0; i < el.extracts.size(); ++i) {
887 unsigned h = el.extracts[i].high;
888 unsigned l = el.extracts[i].low;
889 Node extract = utils::mkExtract(var, h, l);
890 Node skolem_extract = Rewriter::rewrite(utils::mkExtract(skolem_concat, h, l));
891 Assert (skolem_extract.getMetaKind() == kind::metakind::VARIABLE ||
892 skolem_extract.getKind() == kind::BITVECTOR_CONCAT);
893 storeSkolem(extract, skolem_extract);
894 }
895 }
896
897 for (unsigned i = 0; i < facts.size(); ++i) {
898 facts[i] = WorklistElement(skolemize(facts[i].node), facts[i].id);
899 }
900 }
901
mkSkolem(Node node)902 Node ExtractSkolemizer::mkSkolem(Node node) {
903 Assert (node.getKind() == kind::BITVECTOR_EXTRACT &&
904 node[0].getMetaKind() == kind::metakind::VARIABLE);
905 Assert (!d_skolemSubst.hasSubstitution(node));
906 return utils::mkVar(utils::getSize(node));
907 }
908
unSkolemize(std::vector<WorklistElement> & facts)909 void ExtractSkolemizer::unSkolemize(std::vector<WorklistElement>& facts) {
910 for (unsigned i = 0; i < facts.size(); ++i) {
911 facts[i] = WorklistElement(unSkolemize(facts[i].node), facts[i].id);
912 }
913 }
914
storeSkolem(TNode node,TNode skolem)915 void ExtractSkolemizer::storeSkolem(TNode node, TNode skolem) {
916 d_skolemSubst.addSubstitution(node, skolem);
917 d_modelMap->addSubstitution(node, skolem);
918 d_skolemSubstRev.addSubstitution(skolem, node);
919 }
920
unSkolemize(TNode node)921 Node ExtractSkolemizer::unSkolemize(TNode node) {
922 return d_skolemSubstRev.apply(node);
923 }
924
skolemize(TNode node)925 Node ExtractSkolemizer::skolemize(TNode node) {
926 return d_skolemSubst.apply(node);
927 }
928
addExtract(Extract & e)929 void ExtractSkolemizer::ExtractList::addExtract(Extract& e) {
930 extracts.push_back(e);
931 base.sliceAt(e.low);
932 base.sliceAt(e.high+1);
933 }
934
storeExtract(TNode var,unsigned high,unsigned low)935 void ExtractSkolemizer::storeExtract(TNode var, unsigned high, unsigned low) {
936 Assert (var.getMetaKind() == kind::metakind::VARIABLE);
937 if (d_varToExtract.find(var) == d_varToExtract.end()) {
938 d_varToExtract[var] = ExtractList(utils::getSize(var));
939 }
940 VarExtractMap::iterator it = d_varToExtract.find(var);
941 ExtractList& el = it->second;
942 Extract e(high, low);
943 el.addExtract(e);
944 }
945
collectExtracts(TNode node,TNodeSet & seen)946 void ExtractSkolemizer::collectExtracts(TNode node, TNodeSet& seen) {
947 if (seen.find(node) != seen.end()) {
948 return;
949 }
950
951 if (node.getKind() == kind::BITVECTOR_EXTRACT &&
952 node[0].getMetaKind() == kind::metakind::VARIABLE) {
953 unsigned high = utils::getExtractHigh(node);
954 unsigned low = utils::getExtractLow(node);
955 TNode var = node[0];
956 storeExtract(var, high, low);
957 seen.insert(node);
958 return;
959 }
960
961 if (node.getNumChildren() == 0)
962 return;
963
964 for (unsigned i = 0; i < node.getNumChildren(); ++i) {
965 collectExtracts(node[i], seen);
966 }
967 seen.insert(node);
968 }
969
ExtractSkolemizer(theory::SubstitutionMap * modelMap)970 ExtractSkolemizer::ExtractSkolemizer(theory::SubstitutionMap* modelMap)
971 : d_emptyContext()
972 , d_varToExtract()
973 , d_modelMap(modelMap)
974 , d_skolemSubst(&d_emptyContext)
975 , d_skolemSubstRev(&d_emptyContext)
976 {}
977
~ExtractSkolemizer()978 ExtractSkolemizer::~ExtractSkolemizer() {
979 }
980
mergeExplanations(const std::vector<Node> & expls)981 Node mergeExplanations(const std::vector<Node>& expls) {
982 TNodeSet literals;
983 for (unsigned i = 0; i < expls.size(); ++i) {
984 TNode expl = expls[i];
985 Assert (expl.getType().isBoolean());
986 if (expl.getKind() == kind::AND) {
987 for (unsigned i = 0; i < expl.getNumChildren(); ++i) {
988 TNode child = expl[i];
989 if (child == utils::mkTrue())
990 continue;
991 literals.insert(child);
992 }
993 } else if (expl != utils::mkTrue()) {
994 literals.insert(expl);
995 }
996 }
997
998 if (literals.size() == 0) {
999 return utils::mkTrue();
1000 }else if (literals.size() == 1) {
1001 return *literals.begin();
1002 }
1003
1004 NodeBuilder<> nb(kind::AND);
1005
1006 for (TNodeSet::const_iterator it = literals.begin(); it!= literals.end(); ++it) {
1007 nb << *it;
1008 }
1009 return nb;
1010 }
1011
mergeExplanations(TNode expl1,TNode expl2)1012 Node mergeExplanations(TNode expl1, TNode expl2) {
1013 std::vector<Node> expls;
1014 expls.push_back(expl1);
1015 expls.push_back(expl2);
1016 return mergeExplanations(expls);
1017 }
1018
1019 } /* namespace CVC4::theory::bv */
1020 } /* namespace CVc4::theory */
1021 } /* namespace CVC4 */
1022