bool TheoryUFTim::equiv(TNode x, TNode y) { Assert(x.getKind() == kind::APPLY_UF); Assert(y.getKind() == kind::APPLY_UF); if(x.getNumChildren() != y.getNumChildren()) { return false; } if(x.getOperator() != y.getOperator()) { return false; } // intentionally don't look at operator TNode::iterator xIter = x.begin(); TNode::iterator yIter = y.begin(); while(xIter != x.end()) { if(!sameCongruenceClass(*xIter, *yIter)) { return false; } ++xIter; ++yIter; } return true; }
Node TheoryBuiltinRewriter::blastDistinct(TNode in) { Assert(in.getKind() == kind::DISTINCT); if(in.getNumChildren() == 2) { // if this is the case exactly 1 != pair will be generated so the // AND is not required Node eq = NodeManager::currentNM()->mkNode(in[0].getType().isBoolean() ? kind::IFF : kind::EQUAL, in[0], in[1]); Node neq = NodeManager::currentNM()->mkNode(kind::NOT, eq); return neq; } // assume that in.getNumChildren() > 2 => diseqs.size() > 1 vector<Node> diseqs; for(TNode::iterator i = in.begin(); i != in.end(); ++i) { TNode::iterator j = i; while(++j != in.end()) { Node eq = NodeManager::currentNM()->mkNode((*i).getType().isBoolean() ? kind::IFF : kind::EQUAL, *i, *j); Node neq = NodeManager::currentNM()->mkNode(kind::NOT, eq); diseqs.push_back(neq); } } Node out = NodeManager::currentNM()->mkNode(kind::AND, diseqs); return out; }
void ArithStaticLearner::staticLearning(TNode n, NodeBuilder<>& learned){ vector<TNode> workList; workList.push_back(n); TNodeSet processed; //Contains an underapproximation of nodes that must hold. TNodeSet defTrue; defTrue.insert(n); while(!workList.empty()) { n = workList.back(); bool unprocessedChildren = false; for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) { if(processed.find(*i) == processed.end()) { // unprocessed child workList.push_back(*i); unprocessedChildren = true; } } if(n.getKind() == AND && defTrue.find(n) != defTrue.end() ){ for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) { defTrue.insert(*i); } } if(unprocessedChildren) { continue; } workList.pop_back(); // has node n been processed in the meantime ? if(processed.find(n) != processed.end()) { continue; } processed.insert(n); process(n,learned, defTrue); } }
void PicklerPrivate::toCaseOperator(TNode n) { Kind k = n.getKind(); kind::MetaKind m = metaKindOf(k); Assert(m == kind::metakind::PARAMETERIZED || m == kind::metakind::OPERATOR); if(m == kind::metakind::PARAMETERIZED) { toCaseNode(n.getOperator()); } for(TNode::iterator i = n.begin(), i_end = n.end(); i != i_end; ++i) { toCaseNode(*i); } d_current << mkOperatorHeader(k, n.getNumChildren()); }
void TheoryEngineModelBuilder::checkTerms(TNode n, TheoryModel* tm, NodeSet& cache) { if (cache.find(n) != cache.end()) { return; } if (isAssignable(n)) { tm->d_equalityEngine.addTerm(n); } for(TNode::iterator child_it = n.begin(); child_it != n.end(); ++child_it) { checkTerms(*child_it, tm, cache); } cache.insert(n); }
void Theory::collectTerms(TNode n, set<Node>& termSet) const { if (termSet.find(n) != termSet.end()) { return; } Trace("theory::collectTerms") << "Theory::collectTerms: adding " << n << endl; termSet.insert(n); if (n.getKind() == kind::NOT || n.getKind() == kind::EQUAL || !isLeaf(n)) { for(TNode::iterator child_it = n.begin(); child_it != n.end(); ++child_it) { collectTerms(*child_it, termSet); } } }
Node TheoryBuiltinRewriter::blastChain(TNode in) { Assert(in.getKind() == kind::CHAIN); Kind chainedOp = in.getOperator().getConst<Chain>().getOperator(); if(in.getNumChildren() == 2) { // if this is the case exactly 1 pair will be generated so the // AND is not required return NodeManager::currentNM()->mkNode(chainedOp, in[0], in[1]); } else { NodeBuilder<> conj(kind::AND); for(TNode::iterator i = in.begin(), j = i + 1; j != in.end(); ++i, ++j) { conj << NodeManager::currentNM()->mkNode(chainedOp, *i, *j); } return conj; } }
bool PropEngine::properExplanation(TNode node, TNode expl) const { if(! d_cnfStream->hasLiteral(node)) { Trace("properExplanation") << "properExplanation(): Failing because node " << "being explained doesn't have a SAT literal ?!" << std::endl << "properExplanation(): The node is: " << node << std::endl; return false; } SatLiteral nodeLit = d_cnfStream->getLiteral(node); for(TNode::kinded_iterator i = expl.begin(kind::AND), i_end = expl.end(kind::AND); i != i_end; ++i) { if(! d_cnfStream->hasLiteral(*i)) { Trace("properExplanation") << "properExplanation(): Failing because one of explanation " << "nodes doesn't have a SAT literal" << std::endl << "properExplanation(): The explanation node is: " << *i << std::endl; return false; } SatLiteral iLit = d_cnfStream->getLiteral(*i); if(iLit == nodeLit) { Trace("properExplanation") << "properExplanation(): Failing because the node" << std::endl << "properExplanation(): " << node << std::endl << "properExplanation(): cannot be made to explain itself!" << std::endl; return false; } if(! d_satSolver->properExplanation(nodeLit, iLit)) { Trace("properExplanation") << "properExplanation(): SAT solver told us that node" << std::endl << "properExplanation(): " << *i << std::endl << "properExplanation(): is not part of a proper explanation node for" << std::endl << "properExplanation(): " << node << std::endl << "properExplanation(): Perhaps it one of the two isn't assigned or the explanation" << std::endl << "properExplanation(): node wasn't propagated before the node being explained" << std::endl; return false; } } return true; }
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 UnconstrainedSimplifier::visitAll(TNode assertion) { // Do a topological sort of the subexpressions and substitute them vector<unc_preprocess_stack_element> toVisit; toVisit.push_back(assertion); while (!toVisit.empty()) { // The current node we are processing TNode current = toVisit.back().node; TNode parent = toVisit.back().parent; toVisit.pop_back(); TNodeCountMap::iterator find = d_visited.find(current); if (find != d_visited.end()) { if (find->second == 1) { d_visitedOnce.erase(current); if (current.isVar()) { d_unconstrained.erase(current); } } ++find->second; continue; } d_visited[current] = 1; d_visitedOnce[current] = parent; if (current.getNumChildren() == 0) { if (current.getKind()==kind::VARIABLE || current.getKind()==kind::SKOLEM) { d_unconstrained.insert(current); } } else { for(TNode::iterator child_it = current.begin(); child_it != current.end(); ++ child_it) { TNode childNode = *child_it; toVisit.push_back(unc_preprocess_stack_element(childNode, current)); } } } }
void TheoryUFTim::registerTerm(TNode n) { Debug("uf") << "uf: begin registerTerm(" << n << ")" << std::endl; d_registered.push_back(n); ECData* ecN; if(n.getAttribute(ECAttr(), ecN)) { /* registerTerm(n) is only called when a node has not been seen in the * current context. ECAttr() is not a context-dependent attribute. * When n.hasAttribute(ECAttr(),...) is true on a registerTerm(n) call, * then it must be the case that this attribute was created in a previous * and no longer valid context. Because of this we have to reregister the * predecessors lists. * Also we do not have to worry about duplicates because all of the Link* * setup before are removed when the context n was setup in was popped out * of. All we are going to do here are sanity checks. */ /* * Consider the following chain of events: * 1) registerTerm(n) is called on node n where n : f(m) in context level X, * 2) A new ECData is created on the heap, ecN, * 3) n is added to the predessecor list of m in context level X, * 4) We pop out of X, * 5) n is removed from the predessecor list of m because this is context * dependent, the Link* will be destroyed and pointers to the Link * structs in the ECData objects will be updated. * 6) registerTerm(n) is called on node n in context level Y, * 7) If n.hasAttribute(ECAttr(), &ecN), then ecN is still around, * but the predecessor list is not * * The above assumes that the code is working correctly. */ Assert(ecN->getFirst() == NULL, "Equivalence class data exists for the node being registered. " "Expected getFirst() == NULL. " "This data is either already in use or was not properly maintained " "during backtracking"); /*Assert(ecN->getLast() == NULL, "Equivalence class data exists for the node being registered. " "Expected getLast() == NULL. " "This data is either already in use or was not properly maintained " "during backtracking.");*/ Assert(ecN->isClassRep(), "Equivalence class data exists for the node being registered. " "Expected isClassRep() to be true. " "This data is either already in use or was not properly maintained " "during backtracking"); Assert(ecN->getWatchListSize() == 0, "Equivalence class data exists for the node being registered. " "Expected getWatchListSize() == 0. " "This data is either already in use or was not properly maintained " "during backtracking"); } else { //The attribute does not exist, so it is created and set ecN = new (true) ECData(getContext(), n); n.setAttribute(ECAttr(), ecN); } /* If the node is an APPLY_UF, we need to add it to the predecessor list * of its children. */ if(n.getKind() == APPLY_UF) { TNode::iterator cIter = n.begin(); for(; cIter != n.end(); ++cIter) { TNode child = *cIter; /* Because this can be called after nodes have been merged, we need * to lookup the representative in the UnionFind datastructure. */ ECData* ecChild = ccFind(child.getAttribute(ECAttr())); /* Because this can be called after nodes have been merged we may need * to be merged with other predecessors of the equivalence class. */ for(Link* Px = ecChild->getFirst(); Px != NULL; Px = Px->d_next ) { if(equiv(n, Px->d_data)) { Node pend = n.eqNode(Px->d_data); d_pending.push_back(pend); } } ecChild->addPredecessor(n); } } Debug("uf") << "uf: end registerTerm(" << n << ")" << std::endl; }
Node RemoveITE::run(TNode node, std::vector<Node>& output, IteSkolemMap& iteSkolemMap) { // Current node Debug("ite") << "removeITEs(" << node << ")" << endl; // The result may be cached already NodeManager *nodeManager = NodeManager::currentNM(); ITECache::iterator i = d_iteCache.find(node); if(i != d_iteCache.end()) { Node cachedRewrite = (*i).second; Debug("ite") << "removeITEs: in-cache: " << cachedRewrite << endl; return cachedRewrite.isNull() ? Node(node) : cachedRewrite; } // If an ITE replace it if(node.getKind() == kind::ITE) { TypeNode nodeType = node.getType(); if(!nodeType.isBoolean()) { // Make the skolem to represent the ITE Node skolem = nodeManager->mkSkolem("termITE_$$", nodeType, "a variable introduced due to term-level ITE removal"); // The new assertion Node newAssertion = nodeManager->mkNode(kind::ITE, node[0], skolem.eqNode(node[1]), skolem.eqNode(node[2])); Debug("ite") << "removeITEs(" << node << ") => " << newAssertion << endl; // Attach the skolem d_iteCache[node] = skolem; // Remove ITEs from the new assertion, rewrite it and push it to the output newAssertion = run(newAssertion, output, iteSkolemMap); iteSkolemMap[skolem] = output.size(); output.push_back(newAssertion); // The representation is now the skolem return skolem; } } // If not an ITE, go deep if( node.getKind() != kind::FORALL && node.getKind() != kind::EXISTS && node.getKind() != kind::REWRITE_RULE ) { vector<Node> newChildren; bool somethingChanged = false; if(node.getMetaKind() == kind::metakind::PARAMETERIZED) { newChildren.push_back(node.getOperator()); } // Remove the ITEs from the children for(TNode::const_iterator it = node.begin(), end = node.end(); it != end; ++it) { Node newChild = run(*it, output, iteSkolemMap); somethingChanged |= (newChild != *it); newChildren.push_back(newChild); } // If changes, we rewrite if(somethingChanged) { Node cachedRewrite = nodeManager->mkNode(node.getKind(), newChildren); d_iteCache[node] = cachedRewrite; return cachedRewrite; } else { d_iteCache[node] = Node::null(); return node; } } else { d_iteCache[node] = Node::null(); return node; } }
TypeNode NodeManager::getType(TNode n, bool check) throw(TypeCheckingExceptionPrivate, AssertionException) { // Many theories' type checkers call Node::getType() directly. This // is incorrect, since "this" might not be the caller's curent node // manager. Rather than force the individual typecheckers not to do // this (by policy, which would be imperfect and lead to // hard-to-find bugs, which it has in the past), we just set this // node manager to be current for the duration of this check. // NodeManagerScope nms(this); TypeNode typeNode; bool hasType = getAttribute(n, TypeAttr(), typeNode); bool needsCheck = check && !getAttribute(n, TypeCheckedAttr()); Debug("getType") << "getting type for " << n << endl; if(needsCheck && !(*d_options)[options::earlyTypeChecking]) { /* Iterate and compute the children bottom up. This avoids stack overflows in computeType() when the Node graph is really deep, which should only affect us when we're type checking lazily. */ stack<TNode> worklist; worklist.push(n); while( !worklist.empty() ) { TNode m = worklist.top(); bool readyToCompute = true; for( TNode::iterator it = m.begin(), end = m.end(); it != end; ++it ) { if( !hasAttribute(*it, TypeAttr()) || (check && !getAttribute(*it, TypeCheckedAttr())) ) { readyToCompute = false; worklist.push(*it); } } if( readyToCompute ) { /* All the children have types, time to compute */ typeNode = TypeChecker::computeType(this, m, check); worklist.pop(); } } // end while /* Last type computed in loop should be the type of n */ Assert( typeNode == getAttribute(n, TypeAttr()) ); } else if( !hasType || needsCheck ) { /* We can compute the type top-down, without worrying about deep recursion. */ typeNode = TypeChecker::computeType(this, n, check); } /* The type should be have been computed and stored. */ Assert( hasAttribute(n, TypeAttr()) ); /* The check should have happened, if we asked for it. */ Assert( !check || getAttribute(n, TypeCheckedAttr()) ); Debug("getType") << "type of " << n << " is " << typeNode << endl; return typeNode; }
void TheoryUF::ppStaticLearn(TNode n, NodeBuilder<>& learned) { //TimerStat::CodeTimer codeTimer(d_staticLearningTimer); vector<TNode> workList; workList.push_back(n); __gnu_cxx::hash_set<TNode, TNodeHashFunction> processed; while(!workList.empty()) { n = workList.back(); if(n.getKind() == kind::FORALL || n.getKind() == kind::EXISTS) { // unsafe to go under quantifiers; we might pull bound vars out of scope! processed.insert(n); workList.pop_back(); continue; } bool unprocessedChildren = false; for(TNode::iterator i = n.begin(), iend = n.end(); i != iend; ++i) { if(processed.find(*i) == processed.end()) { // unprocessed child workList.push_back(*i); unprocessedChildren = true; } } if(unprocessedChildren) { continue; } workList.pop_back(); // has node n been processed in the meantime ? if(processed.find(n) != processed.end()) { continue; } processed.insert(n); // == DIAMONDS == Debug("diamonds") << "===================== looking at" << endl << n << endl; // binary OR of binary ANDs of EQUALities if(n.getKind() == kind::OR && n.getNumChildren() == 2 && n[0].getKind() == kind::AND && n[0].getNumChildren() == 2 && n[1].getKind() == kind::AND && n[1].getNumChildren() == 2 && (n[0][0].getKind() == kind::EQUAL || n[0][0].getKind() == kind::IFF) && (n[0][1].getKind() == kind::EQUAL || n[0][1].getKind() == kind::IFF) && (n[1][0].getKind() == kind::EQUAL || n[1][0].getKind() == kind::IFF) && (n[1][1].getKind() == kind::EQUAL || n[1][1].getKind() == kind::IFF)) { // now we have (a = b && c = d) || (e = f && g = h) Debug("diamonds") << "has form of a diamond!" << endl; TNode a = n[0][0][0], b = n[0][0][1], c = n[0][1][0], d = n[0][1][1], e = n[1][0][0], f = n[1][0][1], g = n[1][1][0], h = n[1][1][1]; // test that one of {a, b} = one of {c, d}, and make "b" the // shared node (i.e. put in the form (a = b && b = d)) // note we don't actually care about the shared ones, so the // "swaps" below are one-sided, ignoring b and c if(a == c) { a = b; } else if(a == d) { a = b; d = c; } else if(b == c) { // nothing to do } else if(b == d) { d = c; } else { // condition not satisfied Debug("diamonds") << "+ A fails" << endl; continue; } Debug("diamonds") << "+ A holds" << endl; // same: one of {e, f} = one of {g, h}, and make "f" the // shared node (i.e. put in the form (e = f && f = h)) if(e == g) { e = f; } else if(e == h) { e = f; h = g; } else if(f == g) { // nothing to do } else if(f == h) { h = g; } else { // condition not satisfied Debug("diamonds") << "+ B fails" << endl; continue; } Debug("diamonds") << "+ B holds" << endl; // now we have (a = b && b = d) || (e = f && f = h) // test that {a, d} == {e, h} if( (a == e && d == h) || (a == h && d == e) ) { // learn: n implies a == d Debug("diamonds") << "+ C holds" << endl; Node newEquality = a.getType().isBoolean() ? a.iffNode(d) : a.eqNode(d); Debug("diamonds") << " ==> " << newEquality << endl; learned << n.impNode(newEquality); } else { Debug("diamonds") << "+ C fails" << endl; } } } if(options::ufSymmetryBreaker()) { d_symb.assertFormula(n); } }/* TheoryUF::ppStaticLearn() */
void UnconstrainedSimplifier::processUnconstrained() { TNodeSet::iterator it = d_unconstrained.begin(), iend = d_unconstrained.end(); vector<TNode> workList; for ( ; it != iend; ++it) { workList.push_back(*it); } Node currentSub; TNode parent; bool swap; bool isSigned; bool strict; vector<TNode> delayQueueLeft; vector<Node> delayQueueRight; TNode current = workList.back(); workList.pop_back(); for (;;) { Assert(d_visitedOnce.find(current) != d_visitedOnce.end()); parent = d_visitedOnce[current]; if (!parent.isNull()) { swap = isSigned = strict = false; switch (parent.getKind()) { // If-then-else operator - any two unconstrained children makes the parent unconstrained case kind::ITE: { Assert(parent[0] == current || parent[1] == current || parent[2] == current); bool uCond = parent[0] == current || d_unconstrained.find(parent[0]) != d_unconstrained.end(); bool uThen = parent[1] == current || d_unconstrained.find(parent[1]) != d_unconstrained.end(); bool uElse = parent[2] == current || d_unconstrained.find(parent[2]) != d_unconstrained.end(); if ((uCond && uThen) || (uCond && uElse) || (uThen && uElse)) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (uThen) { if (parent[1] != current) { if (parent[1].isVar()) { currentSub = parent[1]; } else { Assert(d_substitutions.hasSubstitution(parent[1])); currentSub = d_substitutions.apply(parent[1]); } } else if (currentSub.isNull()) { currentSub = current; } } else if (parent[2] != current) { if (parent[2].isVar()) { currentSub = parent[2]; } else { Assert(d_substitutions.hasSubstitution(parent[2])); currentSub = d_substitutions.apply(parent[2]); } } else if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } } else if (uCond) { Cardinality card = parent.getType().getCardinality(); if (card.isFinite() && !card.isLargeFinite() && card.getFiniteCardinality() == 2) { // Special case: condition is unconstrained, then and else are different, and total cardinality of the type is 2, then the result // is unconstrained Node test; if (parent.getType().isBoolean()) { test = Rewriter::rewrite(parent[1].iffNode(parent[2])); } else { test = Rewriter::rewrite(parent[1].eqNode(parent[2])); } if (test == NodeManager::currentNM()->mkConst<bool>(false)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(parent.getType(), currentSub); current = parent; } } } break; } // Comparisons that return a different type - assuming domains are larger than 1, any // unconstrained child makes parent unconstrained as well case kind::EQUAL: if (parent[0].getType() != parent[1].getType()) { TNode other = (parent[0] == current) ? parent[1] : parent[0]; if (current.getType().isSubtypeOf(other.getType())) { break; } } if( parent[0].getType().isDatatype() ){ TypeNode tn = parent[0].getType(); const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); if( dt.isRecursiveSingleton( tn.toType() ) ){ //domain size may be 1 break; } } case kind::BITVECTOR_COMP: case kind::LT: case kind::LEQ: case kind::GT: case kind::GEQ: { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; Assert(parent[0] != parent[1] && (parent[0] == current || parent[1] == current)); if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(parent.getType(), currentSub); current = parent; } else { currentSub = Node(); } break; } // Unary operators that propagate unconstrainedness case kind::NOT: case kind::BITVECTOR_NOT: case kind::BITVECTOR_NEG: case kind::UMINUS: ++d_numUnconstrainedElim; Assert(parent[0] == current); if (currentSub.isNull()) { currentSub = current; } current = parent; break; // Unary operators that propagate unconstrainedness and return a different type case kind::BITVECTOR_EXTRACT: ++d_numUnconstrainedElim; Assert(parent[0] == current); if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(parent.getType(), currentSub); current = parent; break; // Operators returning same type requiring all children to be unconstrained case kind::AND: case kind::OR: case kind::IMPLIES: case kind::BITVECTOR_AND: case kind::BITVECTOR_OR: case kind::BITVECTOR_NAND: case kind::BITVECTOR_NOR: { bool allUnconstrained = true; for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) { if (d_unconstrained.find(*child_it) == d_unconstrained.end()) { allUnconstrained = false; break; } } if (allUnconstrained) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } } } break; // Require all children to be unconstrained and different case kind::BITVECTOR_SHL: case kind::BITVECTOR_LSHR: case kind::BITVECTOR_ASHR: case kind::BITVECTOR_UDIV_TOTAL: case kind::BITVECTOR_UREM_TOTAL: case kind::BITVECTOR_SDIV: case kind::BITVECTOR_SREM: case kind::BITVECTOR_SMOD: { bool allUnconstrained = true; bool allDifferent = true; for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) { if (d_unconstrained.find(*child_it) == d_unconstrained.end()) { allUnconstrained = false; break; } for(TNode::iterator child_it2 = child_it + 1; child_it2 != parent.end(); ++child_it2) { if (*child_it == *child_it2) { allDifferent = false; break; } } } if (allUnconstrained && allDifferent) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } } break; } // Requires all children to be unconstrained and different, and returns a different type case kind::BITVECTOR_CONCAT: { bool allUnconstrained = true; bool allDifferent = true; for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) { if (d_unconstrained.find(*child_it) == d_unconstrained.end()) { allUnconstrained = false; break; } for(TNode::iterator child_it2 = child_it + 1; child_it2 != parent.end(); ++child_it2) { if (*child_it == *child_it2) { allDifferent = false; break; } } } if (allUnconstrained && allDifferent) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(parent.getType(), currentSub); current = parent; } else { currentSub = Node(); } } } break; // N-ary operators returning same type requiring at least one child to be unconstrained case kind::PLUS: case kind::MINUS: if (current.getType().isInteger() && !parent.getType().isInteger()) { break; } case kind::IFF: case kind::XOR: case kind::BITVECTOR_XOR: case kind::BITVECTOR_XNOR: case kind::BITVECTOR_PLUS: case kind::BITVECTOR_SUB: if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } break; // Multiplication/division: must be non-integer and other operand must be non-zero case kind::MULT: { case kind::DIVISION: Assert(parent.getNumChildren() == 2); TNode other; if (parent[0] == current) { other = parent[1]; } else { Assert(parent[1] == current); other = parent[0]; } if (d_unconstrained.find(other) != d_unconstrained.end()) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { if (current.getType().isInteger() && other.getType().isInteger()) { Assert(parent.getKind() == kind::DIVISION || parent.getType().isInteger()); if (parent.getKind() == kind::DIVISION) { break; } } ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } } else { // if only the denominator of a division is unconstrained, can't set it to 0 so the result is not unconstrained if (parent.getKind() == kind::DIVISION && current == parent[1]) { break; } NodeManager* nm = NodeManager::currentNM(); // if we are an integer, the only way we are unconstrained is if we are a MULT by -1 if (current.getType().isInteger()) { // div/mult by 1 should have been simplified Assert(other != nm->mkConst<Rational>(1)); if (other == nm->mkConst<Rational>(-1)) { // div by -1 should have been simplified Assert(parent.getKind() == kind::MULT); Assert(parent.getType().isInteger()); } else { break; } } else { // TODO: could build ITE here Node test = other.eqNode(nm->mkConst<Rational>(0)); if (Rewriter::rewrite(test) != nm->mkConst<bool>(false)) { break; } } ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } current = parent; } break; } // Bitvector MULT - current must only appear once in the children: // all other children must be unconstrained or odd case kind::BITVECTOR_MULT: { bool found = false; bool done = false; for(TNode::iterator child_it = parent.begin(); child_it != parent.end(); ++child_it) { if ((*child_it) == current) { if (found) { done = true; break; } found = true; continue; } else if (d_unconstrained.find(*child_it) != d_unconstrained.end()) { continue; } else { NodeManager* nm = NodeManager::currentNM(); Node extractOp = nm->mkConst<BitVectorExtract>(BitVectorExtract(0,0)); vector<Node> children; children.push_back(*child_it); Node test = nm->mkNode(extractOp, children); BitVector one(1,unsigned(1)); test = test.eqNode(nm->mkConst<BitVector>(one)); if (Rewriter::rewrite(test) != nm->mkConst<bool>(true)) { done = true; break; } } } if (done) { break; } if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } break; } // Uninterpreted function - if domain is infinite, no quantifiers are used, and any child is unconstrained, result is unconstrained case kind::APPLY_UF: if (d_logicInfo.isQuantified() || !current.getType().getCardinality().isInfinite()) { break; } if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } if (parent.getType() != current.getType()) { currentSub = newUnconstrainedVar(parent.getType(), currentSub); } current = parent; } else { currentSub = Node(); } break; // Array select - if array is unconstrained, so is result case kind::SELECT: if (parent[0] == current) { ++d_numUnconstrainedElim; Assert(current.getType().isArray()); if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(current.getType().getArrayConstituentType(), currentSub); current = parent; } break; // Array store - if both store and value are unconstrained, so is resulting store case kind::STORE: if (((parent[0] == current && d_unconstrained.find(parent[2]) != d_unconstrained.end()) || (parent[2] == current && d_unconstrained.find(parent[0]) != d_unconstrained.end()))) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (parent[0] != current) { if (parent[0].isVar()) { currentSub = parent[0]; } else { Assert(d_substitutions.hasSubstitution(parent[0])); currentSub = d_substitutions.apply(parent[0]); } } else if (currentSub.isNull()) { currentSub = current; } current = parent; } else { currentSub = Node(); } } break; // Bit-vector comparisons: replace with new Boolean variable, but have // to also conjoin with a side condition as there is always one case // when the comparison is forced to be false case kind::BITVECTOR_ULT: case kind::BITVECTOR_UGE: case kind::BITVECTOR_UGT: case kind::BITVECTOR_ULE: case kind::BITVECTOR_SLT: case kind::BITVECTOR_SGE: case kind::BITVECTOR_SGT: case kind::BITVECTOR_SLE: { // Tuples over (signed, swap, strict). switch (parent.getKind()) { case kind::BITVECTOR_UGE: break; case kind::BITVECTOR_ULT: strict = true; break; case kind::BITVECTOR_ULE: swap = true; break; case kind::BITVECTOR_UGT: swap = true; strict = true; break; case kind::BITVECTOR_SGE: isSigned = true; break; case kind::BITVECTOR_SLT: isSigned = true; strict = true; break; case kind::BITVECTOR_SLE: isSigned = true; swap = true; break; case kind::BITVECTOR_SGT: isSigned = true; swap = true; strict = true; break; default: Unreachable(); } TNode other; bool left = false; if (parent[0] == current) { other = parent[1]; left = true; } else { Assert(parent[1] == current); other = parent[0]; } if (d_unconstrained.find(other) != d_unconstrained.end()) { if (d_unconstrained.find(parent) == d_unconstrained.end() && !d_substitutions.hasSubstitution(parent)) { ++d_numUnconstrainedElim; if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(parent.getType(), currentSub); current = parent; } else { currentSub = Node(); } } else { unsigned size = current.getType().getBitVectorSize(); BitVector bv = isSigned ? BitVector(size, Integer(1).multiplyByPow2(size - 1)) : BitVector(size, unsigned(0)); if (swap == left) { bv = ~bv; } if (currentSub.isNull()) { currentSub = current; } currentSub = newUnconstrainedVar(parent.getType(), currentSub); current = parent; NodeManager* nm = NodeManager::currentNM(); Node test = Rewriter::rewrite(other.eqNode(nm->mkConst<BitVector>(bv))); if (test == nm->mkConst<bool>(false)) { break; } if (strict) { currentSub = currentSub.andNode(test.notNode()); } else { currentSub = currentSub.orNode(test); } // Delay adding this substitution - see comment at end of function delayQueueLeft.push_back(current); delayQueueRight.push_back(currentSub); currentSub = Node(); parent = TNode(); } break; } // Do nothing case kind::BITVECTOR_SIGN_EXTEND: case kind::BITVECTOR_ZERO_EXTEND: case kind::BITVECTOR_REPEAT: case kind::BITVECTOR_ROTATE_LEFT: case kind::BITVECTOR_ROTATE_RIGHT: default: break; } if (current == parent && d_visited[parent] == 1) { d_unconstrained.insert(parent); continue; } } if (!currentSub.isNull()) { Assert(currentSub.isVar()); d_substitutions.addSubstitution(current, currentSub, false); } if (workList.empty()) { break; } current = workList.back(); currentSub = Node(); workList.pop_back(); } TNode left; Node right; // All substitutions except those arising from bitvector comparisons are // substitutions t -> x where x is a variable. This allows us to build the // substitution very quickly (never invalidating the substitution cache). // Bitvector comparisons are more complicated and may require // back-substitution and cache-invalidation. So we do these last. while (!delayQueueLeft.empty()) { left = delayQueueLeft.back(); if (!d_substitutions.hasSubstitution(left)) { right = d_substitutions.apply(delayQueueRight.back()); d_substitutions.addSubstitution(delayQueueLeft.back(), right); } delayQueueLeft.pop_back(); delayQueueRight.pop_back(); } }
Node ITESimplifier::simpITE(TNode assertion) { // Do a topological sort of the subexpressions and substitute them vector<preprocess_stack_element> toVisit; toVisit.push_back(assertion); while (!toVisit.empty()) { // The current node we are processing preprocess_stack_element& stackHead = toVisit.back(); TNode current = stackHead.node; // If node has no ITE's or already in the cache we're done, pop from the stack if (current.getNumChildren() == 0 || (Theory::theoryOf(current) != THEORY_BOOL && !containsTermITE(current))) { d_simpITECache[current] = current; toVisit.pop_back(); continue; } NodeMap::iterator find = d_simpITECache.find(current); if (find != d_simpITECache.end()) { toVisit.pop_back(); continue; } // Not yet substituted, so process if (stackHead.children_added) { // Children have been processed, so substitute NodeBuilder<> builder(current.getKind()); if (current.getMetaKind() == kind::metakind::PARAMETERIZED) { builder << current.getOperator(); } for (unsigned i = 0; i < current.getNumChildren(); ++ i) { Assert(d_simpITECache.find(current[i]) != d_simpITECache.end()); builder << d_simpITECache[current[i]]; } // Mark the substitution and continue Node result = builder; // If this is an atom, we process it if (Theory::theoryOf(result) != THEORY_BOOL && result.getType().isBoolean()) { result = simpITEAtom(result); } result = Rewriter::rewrite(result); d_simpITECache[current] = result; toVisit.pop_back(); } else { // Mark that we have added the children if any if (current.getNumChildren() > 0) { stackHead.children_added = true; // We need to add the children for(TNode::iterator child_it = current.begin(); child_it != current.end(); ++ child_it) { TNode childNode = *child_it; NodeMap::iterator childFind = d_simpITECache.find(childNode); if (childFind == d_simpITECache.end()) { toVisit.push_back(childNode); } } } else { // No children, so we're done d_simpITECache[current] = current; toVisit.pop_back(); } } } return d_simpITECache[assertion]; }