bool SharedTermsDatabase::areDisequal(TNode a, TNode b) const { if (d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b)) { return d_equalityEngine.areDisequal(a,b,false); } else { Assert(d_equalityEngine.hasTerm(a) || a.isConst()); Assert(d_equalityEngine.hasTerm(b) || b.isConst()); // one (or both) are in the equality engine return false; } }
bool SharedTermsDatabase::areEqual(TNode a, TNode b) const { if (d_equalityEngine.hasTerm(a) && d_equalityEngine.hasTerm(b)) { return d_equalityEngine.areEqual(a,b); } else { Assert(d_equalityEngine.hasTerm(a) || a.isConst()); Assert(d_equalityEngine.hasTerm(b) || b.isConst()); // since one (or both) of them is a constant, and the other is in the equality engine, they are not same return false; } }
bool ITESimplifier::leavesAreConst(TNode e, TheoryId tid) { Assert((e.getKind() == kind::ITE && !e.getType().isBoolean()) || Theory::theoryOf(e) != THEORY_BOOL); if (e.isConst()) { return true; } hash_map<Node, bool, NodeHashFunction>::iterator it; it = d_leavesConstCache.find(e); if (it != d_leavesConstCache.end()) { return (*it).second; } if (!containsTermITE(e) && Theory::isLeafOf(e, tid)) { d_leavesConstCache[e] = false; return false; } Assert(e.getNumChildren() > 0); size_t k = 0, sz = e.getNumChildren(); if (e.getKind() == kind::ITE) { k = 1; } for (; k < sz; ++k) { if (!leavesAreConst(e[k], tid)) { d_leavesConstCache[e] = false; return false; } } d_leavesConstCache[e] = true; return true; }
Node ModelPostprocessor::rewriteAs(TNode n, TypeNode asType) { if(n.getType().isSubtypeOf(asType)) { // good to go, we have the right type return n; } if(!n.isConst()) { // we don't handle non-const right now return n; } if(asType.isBoolean()) { if(n.getType().isBitVector(1u)) { // type mismatch: should only happen for Boolean-term conversion under // datatype constructor applications; rewrite from BV(1) back to Boolean bool tf = (n.getConst<BitVector>().getValue() == 1); return NodeManager::currentNM()->mkConst(tf); } if(n.getType().isDatatype() && n.getType().hasAttribute(BooleanTermAttr())) { // type mismatch: should only happen for Boolean-term conversion under // datatype constructor applications; rewrite from datatype back to Boolean Assert(n.getKind() == kind::APPLY_CONSTRUCTOR); Assert(n.getNumChildren() == 0); // we assume (by construction) false is first; see boolean_terms.cpp bool tf = (Datatype::indexOf(n.getOperator().toExpr()) == 1); Debug("boolean-terms") << "+++ rewriteAs " << n << " : " << asType << " ==> " << tf << endl; return NodeManager::currentNM()->mkConst(tf); } } if(n.getType().isBoolean()) { bool tf = n.getConst<bool>(); if(asType.isBitVector(1u)) { return NodeManager::currentNM()->mkConst(BitVector(1u, tf ? 1u : 0u)); } if(asType.isDatatype() && asType.hasAttribute(BooleanTermAttr())) { const Datatype& asDatatype = asType.getConst<Datatype>(); return NodeManager::currentNM()->mkNode(kind::APPLY_CONSTRUCTOR, (tf ? asDatatype[0] : asDatatype[1]).getConstructor()); } } if(n.getType().isRecord() && asType.isRecord()) { Debug("boolean-terms") << "+++ got a record - rewriteAs " << n << " : " << asType << endl; const Record& rec CVC4_UNUSED = n.getType().getConst<Record>(); const Record& asRec = asType.getConst<Record>(); Assert(rec.getNumFields() == asRec.getNumFields()); Assert(n.getNumChildren() == asRec.getNumFields()); NodeBuilder<> b(n.getKind()); b << asType; for(size_t i = 0; i < n.getNumChildren(); ++i) { b << rewriteAs(n[i], TypeNode::fromType(asRec[i].second)); } Node out = b; Debug("boolean-terms") << "+++ returning record " << out << endl; return out; }
void AbstractionModule::makeFreshSkolems(TNode node, SubstitutionMap& map, SubstitutionMap& reverse_map) { if (map.hasSubstitution(node)) { return; } if (node.getMetaKind() == kind::metakind::VARIABLE) { Node skolem = utils::mkVar(utils::getSize(node)); map.addSubstitution(node, skolem); reverse_map.addSubstitution(skolem, node); return; } if (node.isConst()) return; for (unsigned i = 0; i < node.getNumChildren(); ++i) { makeFreshSkolems(node[i], map, reverse_map); } }
Node RePairAssocCommutativeOperators::case_other(TNode n){ if(n.isConst() || n.isVar()){ return n; } NodeBuilder<> nb(n.getKind()); if(n.getMetaKind() == kind::metakind::PARAMETERIZED) { nb << n.getOperator(); } // Remove the ITEs from the children for(TNode::const_iterator i = n.begin(), end = n.end(); i != end; ++i) { Node newChild = rePairAssocCommutativeOperators(*i); nb << newChild; } Node result = (Node)nb; return result; }
void AbstractionModule::makeFreshArgs(TNode func, std::vector<Node>& fresh_args) { Assert (fresh_args.size() == 0); Assert (func.getKind() == kind::APPLY_UF); TNodeNodeMap d_map; for (unsigned i = 0; i < func.getNumChildren(); ++i) { TNode arg = func[i]; if (arg.isConst()) { fresh_args.push_back(arg); continue; } Assert (arg.getMetaKind() == kind::metakind::VARIABLE); TNodeNodeMap::iterator it = d_map.find(arg); if (it != d_map.end()) { fresh_args.push_back(it->second); } else { Node skolem = utils::mkVar(utils::getSize(arg)); d_map[arg] = skolem; fresh_args.push_back(skolem); } } Assert (fresh_args.size() == func.getNumChildren()); }
Node TheoryModel::getModelValue(TNode n, bool hasBoundVars) const { Assert(n.getKind() != kind::FORALL && n.getKind() != kind::EXISTS); if(n.getKind() == kind::LAMBDA) { NodeManager* nm = NodeManager::currentNM(); Node body = getModelValue(n[1], true); // This is a bit ugly, but cache inside simplifier can change, so can't be const // The ite simplifier is needed to get rid of artifacts created by Boolean terms body = const_cast<ITESimplifier*>(&d_iteSimp)->simpITE(body); body = Rewriter::rewrite(body); return nm->mkNode(kind::LAMBDA, n[0], body); } if(n.isConst() || (hasBoundVars && n.getKind() == kind::BOUND_VARIABLE)) { return n; } TypeNode t = n.getType(); if (t.isFunction() || t.isPredicate()) { if (d_enableFuncModels) { std::map< Node, Node >::const_iterator it = d_uf_models.find(n); if (it != d_uf_models.end()) { // Existing function return it->second; } // Unknown function symbol: return LAMBDA x. c, where c is the first constant in the enumeration of the range type vector<TypeNode> argTypes = t.getArgTypes(); vector<Node> args; NodeManager* nm = NodeManager::currentNM(); for (unsigned i = 0; i < argTypes.size(); ++i) { args.push_back(nm->mkBoundVar(argTypes[i])); } Node boundVarList = nm->mkNode(kind::BOUND_VAR_LIST, args); TypeEnumerator te(t.getRangeType()); return nm->mkNode(kind::LAMBDA, boundVarList, *te); } // TODO: if func models not enabled, throw an error? Unreachable(); } if (n.getNumChildren() > 0) { std::vector<Node> children; if (n.getKind() == APPLY_UF) { Node op = getModelValue(n.getOperator(), hasBoundVars); children.push_back(op); } else if (n.getMetaKind() == kind::metakind::PARAMETERIZED) { children.push_back(n.getOperator()); } //evaluate the children for (unsigned i = 0; i < n.getNumChildren(); ++i) { Node val = getModelValue(n[i], hasBoundVars); children.push_back(val); } Node val = Rewriter::rewrite(NodeManager::currentNM()->mkNode(n.getKind(), children)); Assert(hasBoundVars || val.isConst()); return val; } if (!d_equalityEngine.hasTerm(n)) { // Unknown term - return first enumerated value for this type TypeEnumerator te(n.getType()); return *te; } Node val = d_equalityEngine.getRepresentative(n); Assert(d_reps.find(val) != d_reps.end()); std::map< Node, Node >::const_iterator it = d_reps.find( val ); if( it!=d_reps.end() ){ return it->second; }else{ return Node::null(); } }
bool AlgebraicSolver::check(Theory::Effort e) { Assert(options::bitblastMode() == theory::bv::BITBLAST_MODE_LAZY); if (!Theory::fullEffort(e)) return true; if (!useHeuristic()) return true; ++(d_numCalls); TimerStat::CodeTimer algebraicTimer(d_statistics.d_solveTime); Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check (" << e << ")\n"; ++(d_statistics.d_numCallstoCheck); d_explanations.clear(); d_ids.clear(); d_inputAssertions.clear(); std::vector<WorklistElement> worklist; uint64_t original_bb_cost = 0; NodeSet seen_assertions; // Processing assertions from scratch for (AssertionQueue::const_iterator it = assertionsBegin(); it != assertionsEnd(); ++it) { Debug("bv-subtheory-algebraic") << " " << *it << "\n"; TNode assertion = *it; unsigned id = worklist.size(); d_ids[assertion] = id; worklist.push_back(WorklistElement(assertion, id)); d_inputAssertions.insert(assertion); storeExplanation(assertion); uint64_t assertion_size = d_quickSolver->computeAtomWeight(assertion, seen_assertions); Assert (original_bb_cost <= original_bb_cost + assertion_size); original_bb_cost+= assertion_size; } for (unsigned i = 0; i < worklist.size(); ++i) { d_ids[worklist[i].node] = worklist[i].id; } Debug("bv-subtheory-algebraic") << "Assertions " << worklist.size() <<" : \n"; Assert (d_explanations.size() == worklist.size()); delete d_modelMap; d_modelMap = new SubstitutionMap(d_context); SubstitutionEx subst(d_modelMap); // first round of substitutions processAssertions(worklist, subst); if (!d_isDifficult.get()) { // skolemize all possible extracts ExtractSkolemizer skolemizer(d_modelMap); skolemizer.skolemize(worklist); // second round of substitutions processAssertions(worklist, subst); } NodeSet subst_seen; uint64_t subst_bb_cost = 0; unsigned r = 0; unsigned w = 0; for (; r < worklist.size(); ++r) { TNode fact = worklist[r].node; unsigned id = worklist[r].id; if (Dump.isOn("bv-algebraic")) { Node expl = d_explanations[id]; Node query = utils::mkNot(utils::mkNode(kind::IMPLIES, expl, fact)); Dump("bv-algebraic") << EchoCommand("ThoeryBV::AlgebraicSolver::substitution explanation"); Dump("bv-algebraic") << PushCommand(); Dump("bv-algebraic") << AssertCommand(query.toExpr()); Dump("bv-algebraic") << CheckSatCommand(); Dump("bv-algebraic") << PopCommand(); } if (fact.isConst() && fact.getConst<bool>() == true) { continue; } if (fact.isConst() && fact.getConst<bool>() == false) { // we have a conflict Node conflict = BooleanSimplification::simplify(d_explanations[id]); d_bv->setConflict(conflict); d_isComplete.set(true); Debug("bv-subtheory-algebraic") << " UNSAT: assertion simplfies to false with conflict: "<< conflict << "\n"; if (Dump.isOn("bv-algebraic")) { Dump("bv-algebraic") << EchoCommand("TheoryBV::AlgebraicSolver::conflict"); Dump("bv-algebraic") << PushCommand(); Dump("bv-algebraic") << AssertCommand(conflict.toExpr()); Dump("bv-algebraic") << CheckSatCommand(); Dump("bv-algebraic") << PopCommand(); } ++(d_statistics.d_numSimplifiesToFalse); ++(d_numSolved); return false; } subst_bb_cost+= d_quickSolver->computeAtomWeight(fact, subst_seen); worklist[w] = WorklistElement(fact, id); Node expl = BooleanSimplification::simplify(d_explanations[id]); storeExplanation(id, expl); d_ids[fact] = id; ++w; } worklist.resize(w); if(Debug.isOn("bv-subtheory-algebraic")) { Debug("bv-subtheory-algebraic") << "Assertions post-substitutions " << worklist.size() << ":\n"; for (unsigned i = 0; i < worklist.size(); ++i) { Debug("bv-subtheory-algebraic") << " " << worklist[i].node << "\n"; } } // all facts solved to true if (worklist.empty()) { Debug("bv-subtheory-algebraic") << " SAT: everything simplifies to true.\n"; ++(d_statistics.d_numSimplifiesToTrue); ++(d_numSolved); return true; } double ratio = ((double)subst_bb_cost)/original_bb_cost; if (ratio > 0.5 || !d_isDifficult.get()) { // give up if problem not reduced enough d_isComplete.set(false); return true; } d_quickSolver->clearSolver(); d_quickSolver->push(); std::vector<Node> facts; for (unsigned i = 0; i < worklist.size(); ++i) { facts.push_back(worklist[i].node); } bool ok = quickCheck(facts); Debug("bv-subtheory-algebraic") << "AlgebraicSolver::check done " << ok << ".\n"; return ok; }
TheoryId Theory::theoryOf(TheoryOfMode mode, TNode node) { TheoryId tid = THEORY_BUILTIN; switch(mode) { case THEORY_OF_TYPE_BASED: // Constants, variables, 0-ary constructors if (node.isVar() || node.isConst()) { tid = Theory::theoryOf(node.getType()); } else if (node.getKind() == kind::EQUAL) { // Equality is owned by the theory that owns the domain tid = Theory::theoryOf(node[0].getType()); } else { // Regular nodes are owned by the kind tid = kindToTheoryId(node.getKind()); } break; case THEORY_OF_TERM_BASED: // Variables if (node.isVar()) { if (Theory::theoryOf(node.getType()) != theory::THEORY_BOOL) { // We treat the variables as uninterpreted tid = s_uninterpretedSortOwner; } else { // Except for the Boolean ones, which we just ignore anyhow tid = theory::THEORY_BOOL; } } else if (node.isConst()) { // Constants go to the theory of the type tid = Theory::theoryOf(node.getType()); } else if (node.getKind() == kind::EQUAL) { // Equality // If one of them is an ITE, it's irelevant, since they will get replaced out anyhow if (node[0].getKind() == kind::ITE) { tid = Theory::theoryOf(node[0].getType()); } else if (node[1].getKind() == kind::ITE) { tid = Theory::theoryOf(node[1].getType()); } else { TNode l = node[0]; TNode r = node[1]; TypeNode ltype = l.getType(); TypeNode rtype = r.getType(); if( ltype != rtype ){ tid = Theory::theoryOf(l.getType()); }else { // If both sides belong to the same theory the choice is easy TheoryId T1 = Theory::theoryOf(l); TheoryId T2 = Theory::theoryOf(r); if (T1 == T2) { tid = T1; } else { TheoryId T3 = Theory::theoryOf(ltype); // This is a case of // * x*y = f(z) -> UF // * x = c -> UF // * f(x) = read(a, y) -> either UF or ARRAY // at least one of the theories has to be parametric, i.e. theory of the type is different // from the theory of the term if (T1 == T3) { tid = T2; } else if (T2 == T3) { tid = T1; } else { // If both are parametric, we take the smaller one (arbitrary) tid = T1 < T2 ? T1 : T2; } } } } } else { // Regular nodes are owned by the kind tid = kindToTheoryId(node.getKind()); } break; default: Unreachable(); } Trace("theory::internal") << "theoryOf(" << mode << ", " << node << ") -> " << tid << std::endl; return tid; }