1 /********************* */
2 /*! \file bv_subtheory_algebraic.cpp
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-2018 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
12 ** \brief Algebraic solver.
16 #include "theory/bv/bv_subtheory_algebraic.h"
18 #include <unordered_set>
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"
29 using namespace CVC4::context
;
30 using namespace CVC4::prop
;
31 using namespace CVC4::theory::bv::utils
;
38 /* ------------------------------------------------------------------------- */
42 /* Collect all variables under a given a node. */
43 void collectVariables(TNode node
, utils::NodeSet
& vars
)
45 std::vector
<TNode
> stack
;
46 std::unordered_set
<TNode
, TNodeHashFunction
> visited
;
48 stack
.push_back(node
);
49 while (!stack
.empty())
51 Node n
= stack
.back();
54 if (vars
.find(n
) != vars
.end()) continue;
55 if (visited
.find(n
) != visited
.end()) continue;
58 if (Theory::isLeafOf(n
, THEORY_BV
) && n
.getKind() != kind::CONST_BITVECTOR
)
63 stack
.insert(stack
.end(), n
.begin(), n
.end());
69 /* ------------------------------------------------------------------------- */
71 bool hasExpensiveBVOperators(TNode fact
);
72 Node
mergeExplanations(const std::vector
<Node
>& expls
);
73 Node
mergeExplanations(TNode expl1
, TNode expl2
);
76 SubstitutionEx::SubstitutionEx(theory::SubstitutionMap
* modelMap
)
79 , d_cacheInvalid(true)
80 , d_modelMap(modelMap
)
83 bool SubstitutionEx::addSubstitution(TNode from
, TNode to
, TNode reason
) {
84 Debug("bv-substitution") << "SubstitutionEx::addSubstitution: "<< from
85 <<" => "<< to
<< "\n" << " reason "<<reason
<< "\n";
87 if (d_substitutions
.find(from
) != d_substitutions
.end()) {
91 d_modelMap
->addSubstitution(from
, to
);
93 d_cacheInvalid
= true;
94 d_substitutions
[from
] = SubstitutionElement(to
, reason
);
98 Node
SubstitutionEx::apply(TNode node
) {
99 Debug("bv-substitution") << "SubstitutionEx::apply("<< node
<<")\n";
100 if (d_cacheInvalid
) {
102 d_cacheInvalid
= false;
105 SubstitutionsCache::iterator it
= d_cache
.find(node
);
107 if (it
!= d_cache
.end()) {
108 Node res
= it
->second
.to
;
109 Debug("bv-substitution") << " =>"<< res
<<"\n";
113 Node result
= internalApply(node
);
114 Debug("bv-substitution") << " =>"<< result
<<"\n";
118 Node
SubstitutionEx::internalApply(TNode node
) {
119 if (d_substitutions
.empty())
122 vector
<SubstitutionStackElement
> stack
;
123 stack
.push_back(SubstitutionStackElement(node
));
125 while (!stack
.empty()) {
126 SubstitutionStackElement head
= stack
.back();
129 TNode current
= head
.node
;
131 if (hasCache(current
)) {
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
);
144 reasons
.push_back(getReason(to
));
145 Node reason
= mergeExplanations(reasons
);
146 storeCache(current
, res
, reason
);
150 // if no children then just continue
151 if(current
.getNumChildren() == 0) {
152 storeCache(current
, current
, utils::mkTrue());
156 // children already processed
157 if (head
.childrenAdded
) {
158 NodeBuilder
<> nb(current
.getKind());
159 std::vector
<Node
> reasons
;
161 if (current
.getMetaKind() == kind::metakind::PARAMETERIZED
) {
162 TNode op
= current
.getOperator();
163 Assert (hasCache(op
));
165 reasons
.push_back(getReason(op
));
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
]));
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
));
179 Node reason
= mergeExplanations(reasons
);
180 storeCache(current
, subst_result
, reason
);
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()));
188 for (unsigned i
= 0; i
< current
.getNumChildren(); ++i
) {
189 stack
.push_back(SubstitutionStackElement(current
[i
]));
194 Assert(hasCache(node
));
195 return getCache(node
);
198 Node
SubstitutionEx::explain(TNode node
) const {
199 if(!hasCache(node
)) {
200 return utils::mkTrue();
203 Debug("bv-substitution") << "SubstitutionEx::explain("<< node
<<")\n";
204 Node res
= getReason(node
);
205 Debug("bv-substitution") << " with "<< res
<<"\n";
209 Node
SubstitutionEx::getReason(TNode node
) const {
210 Assert(hasCache(node
));
211 SubstitutionsCache::const_iterator it
= d_cache
.find(node
);
212 return it
->second
.reason
;
215 bool SubstitutionEx::hasCache(TNode node
) const {
216 return d_cache
.find(node
) != d_cache
.end();
219 Node
SubstitutionEx::getCache(TNode node
) const {
220 Assert (hasCache(node
));
221 return d_cache
.find(node
)->second
.to
;
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
);
230 AlgebraicSolver::AlgebraicSolver(context::Context
* c
, TheoryBV
* bv
)
231 : SubtheorySolver(c
, bv
)
233 , d_quickSolver(new BVQuickCheck("theory::bv::algebraic", bv
))
234 , d_isComplete(c
, false)
235 , d_isDifficult(c
, false)
236 , d_budget(options::bitvectorAlgebraicBudget())
238 , d_inputAssertions()
242 , d_ctx(new context::Context())
243 , d_quickXplain(options::bitvectorQuickXplain() ? new QuickXPlain("theory::bv::algebraic", d_quickSolver
) : NULL
)
247 AlgebraicSolver::~AlgebraicSolver() {
248 if(d_modelMap
!= NULL
) { delete d_modelMap
; }
249 delete d_quickXplain
;
250 delete d_quickSolver
;
256 bool AlgebraicSolver::check(Theory::Effort e
)
258 Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY
);
260 if (!Theory::fullEffort(e
)) { return true; }
261 if (!useHeuristic()) { return true; }
263 TimerStat::CodeTimer
algebraicTimer(d_statistics
.d_solveTime
);
264 Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check (" << e
<< ")\n";
266 ++(d_statistics
.d_numCallstoCheck
);
268 d_explanations
.clear();
270 d_inputAssertions
.clear();
272 std::vector
<WorklistElement
> worklist
;
274 uint64_t original_bb_cost
= 0;
276 NodeManager
* nm
= NodeManager::currentNM();
277 NodeSet seen_assertions
;
278 // Processing assertions from scratch
279 for (AssertionQueue::const_iterator it
= assertionsBegin(); it
!= assertionsEnd(); ++it
) {
280 Debug("bv-subtheory-algebraic") << " " << *it
<< "\n";
281 TNode assertion
= *it
;
282 unsigned id
= worklist
.size();
283 d_ids
[assertion
] = id
;
284 worklist
.push_back(WorklistElement(assertion
, id
));
285 d_inputAssertions
.insert(assertion
);
286 storeExplanation(assertion
);
288 uint64_t assertion_size
= d_quickSolver
->computeAtomWeight(assertion
, seen_assertions
);
289 Assert (original_bb_cost
<= original_bb_cost
+ assertion_size
);
290 original_bb_cost
+= assertion_size
;
293 for (unsigned i
= 0; i
< worklist
.size(); ++i
) {
294 d_ids
[worklist
[i
].node
] = worklist
[i
].id
;
297 Debug("bv-subtheory-algebraic") << "Assertions " << worklist
.size() <<" : \n";
299 Assert (d_explanations
.size() == worklist
.size());
302 d_modelMap
= new SubstitutionMap(d_context
);
303 SubstitutionEx
subst(d_modelMap
);
305 // first round of substitutions
306 processAssertions(worklist
, subst
);
308 if (!d_isDifficult
.get()) {
309 // skolemize all possible extracts
310 ExtractSkolemizer
skolemizer(d_modelMap
);
311 skolemizer
.skolemize(worklist
);
312 // second round of substitutions
313 processAssertions(worklist
, subst
);
317 uint64_t subst_bb_cost
= 0;
322 for (; r
< worklist
.size(); ++r
) {
324 TNode fact
= worklist
[r
].node
;
325 unsigned id
= worklist
[r
].id
;
327 if (Dump
.isOn("bv-algebraic")) {
328 Node expl
= d_explanations
[id
];
329 Node query
= utils::mkNot(nm
->mkNode(kind::IMPLIES
, expl
, fact
));
330 Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation");
331 Dump("bv-algebraic") << PushCommand();
332 Dump("bv-algebraic") << AssertCommand(query
.toExpr());
333 Dump("bv-algebraic") << CheckSatCommand();
334 Dump("bv-algebraic") << PopCommand();
337 if (fact
.isConst() &&
338 fact
.getConst
<bool>() == true) {
342 if (fact
.isConst() &&
343 fact
.getConst
<bool>() == false) {
344 // we have a conflict
345 Node conflict
= BooleanSimplification::simplify(d_explanations
[id
]);
346 d_bv
->setConflict(conflict
);
347 d_isComplete
.set(true);
348 Debug("bv-subtheory-algebraic") << " UNSAT: assertion simplfies to false with conflict: "<< conflict
<< "\n";
350 if (Dump
.isOn("bv-algebraic")) {
351 Dump("bv-algebraic") << EchoCommand("TheoryBV::AlgebraicSolver::conflict");
352 Dump("bv-algebraic") << PushCommand();
353 Dump("bv-algebraic") << AssertCommand(conflict
.toExpr());
354 Dump("bv-algebraic") << CheckSatCommand();
355 Dump("bv-algebraic") << PopCommand();
359 ++(d_statistics
.d_numSimplifiesToFalse
);
364 subst_bb_cost
+= d_quickSolver
->computeAtomWeight(fact
, subst_seen
);
365 worklist
[w
] = WorklistElement(fact
, id
);
366 Node expl
= BooleanSimplification::simplify(d_explanations
[id
]);
367 storeExplanation(id
, expl
);
375 if(Debug
.isOn("bv-subtheory-algebraic")) {
376 Debug("bv-subtheory-algebraic") << "Assertions post-substitutions " << worklist
.size() << ":\n";
377 for (unsigned i
= 0; i
< worklist
.size(); ++i
) {
378 Debug("bv-subtheory-algebraic") << " " << worklist
[i
].node
<< "\n";
383 // all facts solved to true
384 if (worklist
.empty()) {
385 Debug("bv-subtheory-algebraic") << " SAT: everything simplifies to true.\n";
386 ++(d_statistics
.d_numSimplifiesToTrue
);
391 double ratio
= ((double)subst_bb_cost
)/original_bb_cost
;
393 !d_isDifficult
.get()) {
394 // give up if problem not reduced enough
395 d_isComplete
.set(false);
399 d_quickSolver
->clearSolver();
401 d_quickSolver
->push();
402 std::vector
<Node
> facts
;
403 for (unsigned i
= 0; i
< worklist
.size(); ++i
) {
404 facts
.push_back(worklist
[i
].node
);
406 bool ok
= quickCheck(facts
);
408 Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check done " << ok
<< ".\n";
412 bool AlgebraicSolver::quickCheck(std::vector
<Node
>& facts
) {
413 SatValue res
= d_quickSolver
->checkSat(facts
, d_budget
);
415 if (res
== SAT_VALUE_UNKNOWN
) {
416 d_isComplete
.set(false);
417 Debug("bv-subtheory-algebraic") << " Unknown.\n";
418 ++(d_statistics
.d_numUnknown
);
422 if (res
== SAT_VALUE_TRUE
) {
423 Debug("bv-subtheory-algebraic") << " Sat.\n";
424 ++(d_statistics
.d_numSat
);
426 d_isComplete
.set(true);
430 Assert (res
== SAT_VALUE_FALSE
);
431 Assert (d_quickSolver
->inConflict());
432 d_isComplete
.set(true);
433 Debug("bv-subtheory-algebraic") << " Unsat.\n";
435 ++(d_statistics
.d_numUnsat
);
438 Node conflict
= d_quickSolver
->getConflict();
439 Debug("bv-subtheory-algebraic") << " Conflict: " << conflict
<< "\n";
441 // singleton conflict
442 if (conflict
.getKind() != kind::AND
) {
443 Assert (d_ids
.find(conflict
) != d_ids
.end());
444 unsigned id
= d_ids
[conflict
];
445 Assert (id
< d_explanations
.size());
446 Node theory_confl
= d_explanations
[id
];
447 d_bv
->setConflict(theory_confl
);
451 Assert (conflict
.getKind() == kind::AND
);
452 if (options::bitvectorQuickXplain()) {
453 d_quickSolver
->popToZero();
454 Debug("bv-quick-xplain") << "AlgebraicSolver::quickCheck original conflict size " << conflict
.getNumChildren() << "\n";
455 conflict
= d_quickXplain
->minimizeConflict(conflict
);
456 Debug("bv-quick-xplain") << "AlgebraicSolver::quickCheck minimized conflict size " << conflict
.getNumChildren() << "\n";
459 vector
<TNode
> theory_confl
;
460 for (unsigned i
= 0; i
< conflict
.getNumChildren(); ++i
) {
461 TNode c
= conflict
[i
];
463 Assert (d_ids
.find(c
) != d_ids
.end());
464 unsigned c_id
= d_ids
[c
];
465 Assert (c_id
< d_explanations
.size());
466 TNode c_expl
= d_explanations
[c_id
];
467 theory_confl
.push_back(c_expl
);
470 Node confl
= BooleanSimplification::simplify(utils::mkAnd(theory_confl
));
472 Debug("bv-subtheory-algebraic") << " Out Conflict: " << confl
<< "\n";
477 void AlgebraicSolver::setConflict(TNode conflict
) {
478 Node final_conflict
= conflict
;
479 if (options::bitvectorQuickXplain() &&
480 conflict
.getKind() == kind::AND
&&
481 conflict
.getNumChildren() > 4) {
482 final_conflict
= d_quickXplain
->minimizeConflict(conflict
);
484 d_bv
->setConflict(final_conflict
);
487 bool AlgebraicSolver::solve(TNode fact
, TNode reason
, SubstitutionEx
& subst
) {
488 if (fact
.getKind() != kind::EQUAL
) return false;
490 NodeManager
* nm
= NodeManager::currentNM();
491 TNode left
= fact
[0];
492 TNode right
= fact
[1];
494 if (left
.isVar() && !expr::hasSubterm(right
, left
))
496 bool changed
= subst
.addSubstitution(left
, right
, reason
);
499 if (right
.isVar() && !expr::hasSubterm(left
, right
))
501 bool changed
= subst
.addSubstitution(right
, left
, reason
);
505 // xor simplification
506 if (right
.getKind() == kind::BITVECTOR_XOR
&&
507 left
.getKind() == kind::BITVECTOR_XOR
) {
509 if (var
.getMetaKind() != kind::metakind::VARIABLE
)
512 // simplify xor with same variable on both sides
513 if (expr::hasSubterm(right
, var
))
515 std::vector
<Node
> right_children
;
516 for (unsigned i
= 0; i
< right
.getNumChildren(); ++i
) {
518 right_children
.push_back(right
[i
]);
520 Assert (right_children
.size());
521 Node new_right
= utils::mkNaryNode(kind::BITVECTOR_XOR
, right_children
);
522 std::vector
<Node
> left_children
;
523 for (unsigned i
= 1; i
< left
.getNumChildren(); ++i
) {
524 left_children
.push_back(left
[i
]);
526 Node new_left
= utils::mkNaryNode(kind::BITVECTOR_XOR
, left_children
);
527 Node new_fact
= nm
->mkNode(kind::EQUAL
, new_left
, new_right
);
528 bool changed
= subst
.addSubstitution(fact
, new_fact
, reason
);
532 NodeBuilder
<> nb(kind::BITVECTOR_XOR
);
533 for (unsigned i
= 1; i
< left
.getNumChildren(); ++i
) {
536 Node inverse
= left
.getNumChildren() == 2? (Node
)left
[1] : (Node
)nb
;
537 Node new_right
= nm
->mkNode(kind::BITVECTOR_XOR
, right
, inverse
);
538 bool changed
= subst
.addSubstitution(var
, new_right
, reason
);
540 if (Dump
.isOn("bv-algebraic")) {
541 Node query
= utils::mkNot(nm
->mkNode(
542 kind::EQUAL
, fact
, nm
->mkNode(kind::EQUAL
, var
, new_right
)));
543 Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation");
544 Dump("bv-algebraic") << PushCommand();
545 Dump("bv-algebraic") << AssertCommand(query
.toExpr());
546 Dump("bv-algebraic") << CheckSatCommand();
547 Dump("bv-algebraic") << PopCommand();
554 // (a xor t = a) <=> (t = 0)
555 if (left
.getKind() == kind::BITVECTOR_XOR
556 && right
.getMetaKind() == kind::metakind::VARIABLE
557 && expr::hasSubterm(left
, right
))
560 Node new_left
= nm
->mkNode(kind::BITVECTOR_XOR
, var
, left
);
561 Node zero
= utils::mkConst(utils::getSize(var
), 0u);
562 Node new_fact
= nm
->mkNode(kind::EQUAL
, zero
, new_left
);
563 bool changed
= subst
.addSubstitution(fact
, new_fact
, reason
);
567 if (right
.getKind() == kind::BITVECTOR_XOR
568 && left
.getMetaKind() == kind::metakind::VARIABLE
569 && expr::hasSubterm(right
, left
))
572 Node new_right
= nm
->mkNode(kind::BITVECTOR_XOR
, var
, right
);
573 Node zero
= utils::mkConst(utils::getSize(var
), 0u);
574 Node new_fact
= nm
->mkNode(kind::EQUAL
, zero
, new_right
);
575 bool changed
= subst
.addSubstitution(fact
, new_fact
, reason
);
579 // (a xor b = 0) <=> (a = b)
580 if (left
.getKind() == kind::BITVECTOR_XOR
&&
581 left
.getNumChildren() == 2 &&
582 right
.getKind() == kind::CONST_BITVECTOR
&&
583 right
.getConst
<BitVector
>() == BitVector(utils::getSize(left
), 0u)) {
584 Node new_fact
= nm
->mkNode(kind::EQUAL
, left
[0], left
[1]);
585 bool changed
= subst
.addSubstitution(fact
, new_fact
, reason
);
593 bool AlgebraicSolver::isSubstitutableIn(TNode node
, TNode in
)
595 if (node
.getMetaKind() == kind::metakind::VARIABLE
596 && !expr::hasSubterm(in
, node
))
601 void AlgebraicSolver::processAssertions(std::vector
<WorklistElement
>& worklist
, SubstitutionEx
& subst
) {
602 NodeManager
* nm
= NodeManager::currentNM();
605 // d_bv->spendResource();
607 for (unsigned i
= 0; i
< worklist
.size(); ++i
) {
608 // apply current substitutions
609 Node current
= subst
.apply(worklist
[i
].node
);
610 unsigned current_id
= worklist
[i
].id
;
611 Node subst_expl
= subst
.explain(worklist
[i
].node
);
612 worklist
[i
] = WorklistElement(Rewriter::rewrite(current
), current_id
);
613 // explanation for this assertion
614 Node old_expl
= d_explanations
[current_id
];
615 Node new_expl
= mergeExplanations(subst_expl
, old_expl
);
616 storeExplanation(current_id
, new_expl
);
618 // use the new substitution to solve
619 if(solve(worklist
[i
].node
, new_expl
, subst
)) {
624 // check for concat slicings
625 for (unsigned i
= 0; i
< worklist
.size(); ++i
) {
626 TNode fact
= worklist
[i
].node
;
627 unsigned current_id
= worklist
[i
].id
;
629 if (fact
.getKind() != kind::EQUAL
) {
633 TNode left
= fact
[0];
634 TNode right
= fact
[1];
635 if (left
.getKind() != kind::BITVECTOR_CONCAT
||
636 right
.getKind() != kind::BITVECTOR_CONCAT
||
637 left
.getNumChildren() != right
.getNumChildren()) {
641 bool can_slice
= true;
642 for (unsigned j
= 0; j
< left
.getNumChildren(); ++j
) {
643 if (utils::getSize(left
[j
]) != utils::getSize(right
[j
]))
651 for (unsigned j
= 0; j
< left
.getNumChildren(); ++j
) {
652 Node eq_j
= nm
->mkNode(kind::EQUAL
, left
[j
], right
[j
]);
653 unsigned id
= d_explanations
.size();
654 TNode expl
= d_explanations
[current_id
];
655 storeExplanation(expl
);
656 worklist
.push_back(WorklistElement(eq_j
, id
));
659 worklist
[i
] = WorklistElement(utils::mkTrue(), worklist
[i
].id
);
662 Assert (d_explanations
.size() == worklist
.size());
666 void AlgebraicSolver::storeExplanation(unsigned id
, TNode explanation
) {
667 Assert (checkExplanation(explanation
));
668 d_explanations
[id
] = explanation
;
671 void AlgebraicSolver::storeExplanation(TNode explanation
) {
672 Assert (checkExplanation(explanation
));
673 d_explanations
.push_back(explanation
);
676 bool AlgebraicSolver::checkExplanation(TNode explanation
) {
677 Node simplified_explanation
= explanation
; //BooleanSimplification::simplify(explanation);
678 if (simplified_explanation
.getKind() != kind::AND
) {
679 return d_inputAssertions
.find(simplified_explanation
) != d_inputAssertions
.end();
681 for (unsigned i
= 0; i
< simplified_explanation
.getNumChildren(); ++i
) {
682 if (d_inputAssertions
.find(simplified_explanation
[i
]) == d_inputAssertions
.end()) {
690 bool AlgebraicSolver::isComplete() {
691 return d_isComplete
.get();
694 bool AlgebraicSolver::useHeuristic() {
698 double success_rate
= double(d_numSolved
)/double(d_numCalls
);
699 d_statistics
.d_useHeuristic
.setData(success_rate
);
700 return success_rate
> 0.8;
704 void AlgebraicSolver::assertFact(TNode fact
) {
705 d_assertionQueue
.push_back(fact
);
706 d_isComplete
.set(false);
707 if (!d_isDifficult
.get()) {
708 d_isDifficult
.set(hasExpensiveBVOperators(fact
));
712 EqualityStatus
AlgebraicSolver::getEqualityStatus(TNode a
, TNode b
) {
713 return EQUALITY_UNKNOWN
;
716 bool AlgebraicSolver::collectModelInfo(TheoryModel
* model
, bool fullModel
)
718 Debug("bitvector-model") << "AlgebraicSolver::collectModelInfo\n";
719 AlwaysAssert (!d_quickSolver
->inConflict());
721 d_bv
->computeRelevantTerms(termSet
);
723 // collect relevant terms that the bv theory abstracts to variables
724 // (variables and parametric terms such as select apply_uf)
725 std::vector
<TNode
> variables
;
726 std::vector
<Node
> values
;
727 for (set
<Node
>::const_iterator it
= termSet
.begin(); it
!= termSet
.end(); ++it
) {
729 if (term
.getType().isBitVector() &&
730 (term
.getMetaKind() == kind::metakind::VARIABLE
||
731 Theory::theoryOf(term
) != THEORY_BV
)) {
732 variables
.push_back(term
);
733 values
.push_back(term
);
738 Debug("bitvector-model") << "Substitutions:\n";
739 for (unsigned i
= 0; i
< variables
.size(); ++i
) {
740 TNode current
= variables
[i
];
741 TNode subst
= Rewriter::rewrite(d_modelMap
->apply(current
));
742 Debug("bitvector-model") << " " << current
<< " => " << subst
<< "\n";
744 collectVariables(subst
, leaf_vars
);
747 Debug("bitvector-model") << "Model:\n";
749 for (NodeSet::const_iterator it
= leaf_vars
.begin(); it
!= leaf_vars
.end(); ++it
) {
751 Node value
= d_quickSolver
->getVarValue(var
, true);
752 Assert (!value
.isNull() || !fullModel
);
754 // may be a shared term that did not appear in the current assertions
755 // AJR: need to check whether already in map for cases where collectModelInfo is called multiple times in the same context
756 if (!value
.isNull() && !d_modelMap
->hasSubstitution(var
)) {
757 Debug("bitvector-model") << " " << var
<< " => " << value
<< "\n";
758 Assert (value
.getKind() == kind::CONST_BITVECTOR
);
759 d_modelMap
->addSubstitution(var
, value
);
763 Debug("bitvector-model") << "Final Model:\n";
764 for (unsigned i
= 0; i
< variables
.size(); ++i
) {
765 TNode current
= values
[i
];
766 TNode subst
= Rewriter::rewrite(d_modelMap
->apply(current
));
767 Debug("bitvector-model") << "AlgebraicSolver: " << variables
[i
] << " => " << subst
<< "\n";
768 // Doesn't have to be constant as it may be irrelevant
769 Assert (subst
.getKind() == kind::CONST_BITVECTOR
);
770 if (!model
->assertEquality(variables
[i
], subst
, true))
778 Node
AlgebraicSolver::getModelValue(TNode node
) {
782 AlgebraicSolver::Statistics::Statistics()
783 : d_numCallstoCheck("theory::bv::algebraic::NumCallsToCheck", 0)
784 , d_numSimplifiesToTrue("theory::bv::algebraic::NumSimplifiesToTrue", 0)
785 , d_numSimplifiesToFalse("theory::bv::algebraic::NumSimplifiesToFalse", 0)
786 , d_numUnsat("theory::bv::algebraic::NumUnsat", 0)
787 , d_numSat("theory::bv::algebraic::NumSat", 0)
788 , d_numUnknown("theory::bv::algebraic::NumUnknown", 0)
789 , d_solveTime("theory::bv::algebraic::SolveTime")
790 , d_useHeuristic("theory::bv::algebraic::UseHeuristic", 0.2)
792 smtStatisticsRegistry()->registerStat(&d_numCallstoCheck
);
793 smtStatisticsRegistry()->registerStat(&d_numSimplifiesToTrue
);
794 smtStatisticsRegistry()->registerStat(&d_numSimplifiesToFalse
);
795 smtStatisticsRegistry()->registerStat(&d_numUnsat
);
796 smtStatisticsRegistry()->registerStat(&d_numSat
);
797 smtStatisticsRegistry()->registerStat(&d_numUnknown
);
798 smtStatisticsRegistry()->registerStat(&d_solveTime
);
799 smtStatisticsRegistry()->registerStat(&d_useHeuristic
);
802 AlgebraicSolver::Statistics::~Statistics() {
803 smtStatisticsRegistry()->unregisterStat(&d_numCallstoCheck
);
804 smtStatisticsRegistry()->unregisterStat(&d_numSimplifiesToTrue
);
805 smtStatisticsRegistry()->unregisterStat(&d_numSimplifiesToFalse
);
806 smtStatisticsRegistry()->unregisterStat(&d_numUnsat
);
807 smtStatisticsRegistry()->unregisterStat(&d_numSat
);
808 smtStatisticsRegistry()->unregisterStat(&d_numUnknown
);
809 smtStatisticsRegistry()->unregisterStat(&d_solveTime
);
810 smtStatisticsRegistry()->unregisterStat(&d_useHeuristic
);
813 bool hasExpensiveBVOperatorsRec(TNode fact
, TNodeSet
& seen
) {
814 if (fact
.getKind() == kind::BITVECTOR_MULT
||
815 fact
.getKind() == kind::BITVECTOR_UDIV_TOTAL
||
816 fact
.getKind() == kind::BITVECTOR_UREM_TOTAL
) {
820 if (seen
.find(fact
) != seen
.end()) {
824 if (fact
.getNumChildren() == 0) {
827 for (unsigned i
= 0; i
< fact
.getNumChildren(); ++i
) {
828 bool difficult
= hasExpensiveBVOperatorsRec(fact
[i
], seen
);
836 bool hasExpensiveBVOperators(TNode fact
) {
838 return hasExpensiveBVOperatorsRec(fact
, seen
);
841 void ExtractSkolemizer::skolemize(std::vector
<WorklistElement
>& facts
) {
843 for (unsigned i
= 0; i
< facts
.size(); ++i
) {
844 TNode current
= facts
[i
].node
;
845 collectExtracts(current
, seen
);
848 for (VarExtractMap::iterator it
= d_varToExtract
.begin(); it
!= d_varToExtract
.end(); ++it
) {
849 ExtractList
& el
= it
->second
;
850 TNode var
= it
->first
;
851 Base
& base
= el
.base
;
853 unsigned bw
= utils::getSize(var
);
854 // compute decomposition
855 std::vector
<unsigned> cuts
;
856 for (unsigned i
= 1; i
<= bw
; ++i
) {
857 if (base
.isCutPoint(i
)) {
861 unsigned previous
= 0;
862 unsigned current
= 0;
863 std::vector
<Node
> skolems
;
864 for (unsigned i
= 0; i
< cuts
.size(); ++i
) {
866 Assert (current
> 0);
867 int size
= current
- previous
;
869 Node sk
= utils::mkVar(size
);
870 skolems
.push_back(sk
);
873 if (current
< bw
-1) {
874 int size
= bw
- current
;
876 Node sk
= utils::mkVar(size
);
877 skolems
.push_back(sk
);
879 NodeBuilder
<> skolem_nb(kind::BITVECTOR_CONCAT
);
881 for (int i
= skolems
.size() - 1; i
>= 0; --i
) {
882 skolem_nb
<< skolems
[i
];
885 Node skolem_concat
= skolems
.size() == 1 ? (Node
)skolems
[0] : (Node
) skolem_nb
;
886 Assert (utils::getSize(skolem_concat
) == utils::getSize(var
));
887 storeSkolem(var
, skolem_concat
);
889 for (unsigned i
= 0; i
< el
.extracts
.size(); ++i
) {
890 unsigned h
= el
.extracts
[i
].high
;
891 unsigned l
= el
.extracts
[i
].low
;
892 Node extract
= utils::mkExtract(var
, h
, l
);
893 Node skolem_extract
= Rewriter::rewrite(utils::mkExtract(skolem_concat
, h
, l
));
894 Assert (skolem_extract
.getMetaKind() == kind::metakind::VARIABLE
||
895 skolem_extract
.getKind() == kind::BITVECTOR_CONCAT
);
896 storeSkolem(extract
, skolem_extract
);
900 for (unsigned i
= 0; i
< facts
.size(); ++i
) {
901 facts
[i
] = WorklistElement(skolemize(facts
[i
].node
), facts
[i
].id
);
905 Node
ExtractSkolemizer::mkSkolem(Node node
) {
906 Assert (node
.getKind() == kind::BITVECTOR_EXTRACT
&&
907 node
[0].getMetaKind() == kind::metakind::VARIABLE
);
908 Assert (!d_skolemSubst
.hasSubstitution(node
));
909 return utils::mkVar(utils::getSize(node
));
912 void ExtractSkolemizer::unSkolemize(std::vector
<WorklistElement
>& facts
) {
913 for (unsigned i
= 0; i
< facts
.size(); ++i
) {
914 facts
[i
] = WorklistElement(unSkolemize(facts
[i
].node
), facts
[i
].id
);
918 void ExtractSkolemizer::storeSkolem(TNode node
, TNode skolem
) {
919 d_skolemSubst
.addSubstitution(node
, skolem
);
920 d_modelMap
->addSubstitution(node
, skolem
);
921 d_skolemSubstRev
.addSubstitution(skolem
, node
);
924 Node
ExtractSkolemizer::unSkolemize(TNode node
) {
925 return d_skolemSubstRev
.apply(node
);
928 Node
ExtractSkolemizer::skolemize(TNode node
) {
929 return d_skolemSubst
.apply(node
);
932 void ExtractSkolemizer::ExtractList::addExtract(Extract
& e
) {
933 extracts
.push_back(e
);
935 base
.sliceAt(e
.high
+1);
938 void ExtractSkolemizer::storeExtract(TNode var
, unsigned high
, unsigned low
) {
939 Assert (var
.getMetaKind() == kind::metakind::VARIABLE
);
940 if (d_varToExtract
.find(var
) == d_varToExtract
.end()) {
941 d_varToExtract
[var
] = ExtractList(utils::getSize(var
));
943 VarExtractMap::iterator it
= d_varToExtract
.find(var
);
944 ExtractList
& el
= it
->second
;
945 Extract
e(high
, low
);
949 void ExtractSkolemizer::collectExtracts(TNode node
, TNodeSet
& seen
) {
950 if (seen
.find(node
) != seen
.end()) {
954 if (node
.getKind() == kind::BITVECTOR_EXTRACT
&&
955 node
[0].getMetaKind() == kind::metakind::VARIABLE
) {
956 unsigned high
= utils::getExtractHigh(node
);
957 unsigned low
= utils::getExtractLow(node
);
959 storeExtract(var
, high
, low
);
964 if (node
.getNumChildren() == 0)
967 for (unsigned i
= 0; i
< node
.getNumChildren(); ++i
) {
968 collectExtracts(node
[i
], seen
);
973 ExtractSkolemizer::ExtractSkolemizer(theory::SubstitutionMap
* modelMap
)
976 , d_modelMap(modelMap
)
977 , d_skolemSubst(&d_emptyContext
)
978 , d_skolemSubstRev(&d_emptyContext
)
981 ExtractSkolemizer::~ExtractSkolemizer() {
984 Node
mergeExplanations(const std::vector
<Node
>& expls
) {
986 for (unsigned i
= 0; i
< expls
.size(); ++i
) {
987 TNode expl
= expls
[i
];
988 Assert (expl
.getType().isBoolean());
989 if (expl
.getKind() == kind::AND
) {
990 for (unsigned i
= 0; i
< expl
.getNumChildren(); ++i
) {
991 TNode child
= expl
[i
];
992 if (child
== utils::mkTrue())
994 literals
.insert(child
);
996 } else if (expl
!= utils::mkTrue()) {
997 literals
.insert(expl
);
1001 if (literals
.size() == 0) {
1002 return utils::mkTrue();
1003 }else if (literals
.size() == 1) {
1004 return *literals
.begin();
1007 NodeBuilder
<> nb(kind::AND
);
1009 for (TNodeSet::const_iterator it
= literals
.begin(); it
!= literals
.end(); ++it
) {
1015 Node
mergeExplanations(TNode expl1
, TNode expl2
) {
1016 std::vector
<Node
> expls
;
1017 expls
.push_back(expl1
);
1018 expls
.push_back(expl2
);
1019 return mergeExplanations(expls
);
1022 } /* namespace CVC4::theory::bv */
1023 } /* namespace CVc4::theory */
1024 } /* namespace CVC4 */