Node RePairAssocCommutativeOperators::case_assoccomm(TNode n){ Kind k = n.getKind(); Assert(isAssociateCommutative(k)); Assert(n.getMetaKind() != kind::metakind::PARAMETERIZED); unsigned N = n.getNumChildren(); Assert(N >= 2); Node last = rePairAssocCommutativeOperators( n[N-1]); Node nextToLast = rePairAssocCommutativeOperators(n[N-2]); NodeManager* nm = NodeManager::currentNM(); Node last2 = nm->mkNode(k, nextToLast, last); if(N <= 2){ return last2; } else{ Assert(N > 2); Node prevRound = last2; for(unsigned prevPos = N-2; prevPos > 0; --prevPos){ unsigned currPos = prevPos-1; Node curr = rePairAssocCommutativeOperators(n[currPos]); Node round = nm->mkNode(k, curr, prevRound); prevRound = round; } return prevRound; } }
Node UfModelTreeNode::getFunctionValue(std::vector<Node>& args, int index, Node argDefaultValue, bool simplify) { if(!d_data.empty()) { Node defaultValue = argDefaultValue; if(d_data.find(Node::null()) != d_data.end()) { defaultValue = d_data[Node::null()].getFunctionValue(args, index + 1, argDefaultValue, simplify); } vector<Node> caseArgs; map<Node, Node> caseValues; for(map< Node, UfModelTreeNode>::iterator it = d_data.begin(); it != d_data.end(); ++it) { if(!it->first.isNull()) { Node val = it->second.getFunctionValue(args, index + 1, defaultValue, simplify); caseArgs.push_back(it->first); caseValues[it->first] = val; } } NodeManager* nm = NodeManager::currentNM(); Node retNode = defaultValue; if(!simplify) { // "non-simplifying" mode - expand function values to things like: // IF (x=0 AND y=0 AND z=0) THEN value1 // ELSE IF (x=0 AND y=0 AND z=1) THEN value2 // [...etc...] for(int i = (int)caseArgs.size() - 1; i >= 0; --i) { Node val = caseValues[ caseArgs[ i ] ]; if(val.getKind() == ITE) { // use a stack to reverse the order, since we're traversing outside-in stack<TNode> stk; do { stk.push(val); val = val[2]; } while(val.getKind() == ITE); AlwaysAssert(val == defaultValue, "default values don't match when constructing function definition!"); while(!stk.empty()) { val = stk.top(); stk.pop(); retNode = nm->mkNode(ITE, nm->mkNode(AND, args[index].eqNode(caseArgs[i]), val[0]), val[1], retNode); } } else { retNode = nm->mkNode(ITE, args[index].eqNode(caseArgs[i]), caseValues[caseArgs[i]], retNode); } } } else { // "simplifying" mode - condense function values for(int i = (int)caseArgs.size() - 1; i >= 0; --i) { retNode = nm->mkNode(ITE, args[index].eqNode(caseArgs[i]), caseValues[caseArgs[i]], retNode); } } return retNode; } else { Assert(!d_value.isNull()); return d_value; } }
Node InequalityGraph::makeDiseqSplitLemma(TNode diseq) { Assert(diseq.getKind() == kind::NOT && diseq[0].getKind() == kind::EQUAL); NodeManager* nm = NodeManager::currentNM(); TNode a = diseq[0][0]; TNode b = diseq[0][1]; Node a_lt_b = nm->mkNode(kind::BITVECTOR_ULT, a, b); Node b_lt_a = nm->mkNode(kind::BITVECTOR_ULT, b, a); Node eq = diseq[0]; Node lemma = nm->mkNode(kind::OR, a_lt_b, b_lt_a, eq); return lemma; }
bool DtInstantiator::processEqualTerms(CegInstantiator* ci, SolvedForm& sf, Node pv, std::vector<Node>& eqc, CegInstEffort effort) { Trace("cegqi-dt-debug") << "try based on constructors in equivalence class." << std::endl; // look in equivalence class for a constructor NodeManager* nm = NodeManager::currentNM(); for (unsigned k = 0, size = eqc.size(); k < size; k++) { Node n = eqc[k]; if (n.getKind() == APPLY_CONSTRUCTOR) { Trace("cegqi-dt-debug") << "...try based on constructor term " << n << std::endl; std::vector<Node> children; children.push_back(n.getOperator()); const Datatype& dt = static_cast<DatatypeType>(d_type.toType()).getDatatype(); unsigned cindex = Datatype::indexOf(n.getOperator().toExpr()); // now must solve for selectors applied to pv for (unsigned j = 0, nargs = dt[cindex].getNumArgs(); j < nargs; j++) { Node c = nm->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr(dt[cindex].getSelectorInternal(d_type.toType(), j)), pv); ci->pushStackVariable(c); children.push_back(c); } Node val = nm->mkNode(kind::APPLY_CONSTRUCTOR, children); TermProperties pv_prop_dt; if (ci->constructInstantiationInc(pv, val, pv_prop_dt, sf)) { return true; } // cleanup for (unsigned j = 0, nargs = dt[cindex].getNumArgs(); j < nargs; j++) { ci->popStackVariable(); } break; } } return false; }
void PseudoBooleanProcessor::learnGeqSub(Node geq) { Assert(geq.getKind() == kind::GEQ); const bool negated = false; bool success = decomposeAssertion(geq, negated); if (!success) { Debug("pbs::rewrites") << "failed " << std::endl; return; } Assert(d_off.value().isIntegral()); Integer off = d_off.value().ceiling(); // \sum pos >= \sum neg + off // for now special case everything we want // target easy clauses if (d_pos.size() == 1 && d_neg.size() == 1 && off.isZero()) { // x >= y // |- (y >= 1) => (x >= 1) Node x = d_pos.front(); Node y = d_neg.front(); Node xGeq1 = mkGeqOne(x); Node yGeq1 = mkGeqOne(y); Node imp = yGeq1.impNode(xGeq1); addSub(geq, imp); } else if (d_pos.size() == 0 && d_neg.size() == 2 && off.isNegativeOne()) { // 0 >= (x + y -1) // |- 1 >= x + y // |- (or (not (x >= 1)) (not (y >= 1))) Node x = d_neg[0]; Node y = d_neg[1]; Node xGeq1 = mkGeqOne(x); Node yGeq1 = mkGeqOne(y); Node cases = (xGeq1.notNode()).orNode(yGeq1.notNode()); addSub(geq, cases); } else if (d_pos.size() == 2 && d_neg.size() == 1 && off.isZero()) { // (x + y) >= z // |- (z >= 1) => (or (x >= 1) (y >=1 )) Node x = d_pos[0]; Node y = d_pos[1]; Node z = d_neg[0]; Node xGeq1 = mkGeqOne(x); Node yGeq1 = mkGeqOne(y); Node zGeq1 = mkGeqOne(z); NodeManager* nm = NodeManager::currentNM(); Node dis = nm->mkNode(kind::OR, zGeq1.notNode(), xGeq1, yGeq1); addSub(geq, dis); } }
bool CoreSolver::decomposeFact(TNode fact) { Debug("bv-slicer") << "CoreSolver::decomposeFact fact=" << fact << endl; // FIXME: are this the right things to assert? // assert decompositions since the equality engine does not know the semantics of // concat: // a == a_1 concat ... concat a_k // b == b_1 concat ... concat b_k TNode eq = fact.getKind() == kind::NOT? fact[0] : fact; TNode a = eq[0]; TNode b = eq[1]; Node new_a = getBaseDecomposition(a); Node new_b = getBaseDecomposition(b); Assert (utils::getSize(new_a) == utils::getSize(new_b) && utils::getSize(new_a) == utils::getSize(a)); NodeManager* nm = NodeManager::currentNM(); Node a_eq_new_a = nm->mkNode(kind::EQUAL, a, new_a); Node b_eq_new_b = nm->mkNode(kind::EQUAL, b, new_b); bool ok = true; ok = assertFactToEqualityEngine(a_eq_new_a, utils::mkTrue()); if (!ok) return false; ok = assertFactToEqualityEngine(b_eq_new_b, utils::mkTrue()); if (!ok) return false; ok = assertFactToEqualityEngine(fact, fact); if (!ok) return false; if (fact.getKind() == kind::EQUAL) { // assert the individual equalities as well // a_i == b_i if (new_a.getKind() == kind::BITVECTOR_CONCAT && new_b.getKind() == kind::BITVECTOR_CONCAT) { Assert (new_a.getNumChildren() == new_b.getNumChildren()); for (unsigned i = 0; i < new_a.getNumChildren(); ++i) { Node eq_i = nm->mkNode(kind::EQUAL, new_a[i], new_b[i]); ok = assertFactToEqualityEngine(eq_i, fact); if (!ok) return false; } } } return true; }
void InequalityGraph::getAllValuesInModel(std::vector<Node>& assignments) { NodeManager* nm = NodeManager::currentNM(); for (ModelValues::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) { TermId id = (*it).first; BitVector value = (*it).second.value; TNode var = getTermNode(id); Node constant = utils::mkConst(value); Node assignment = nm->mkNode(kind::EQUAL, var, constant); assignments.push_back(assignment); Debug("bitvector-model") << " " << var << " => " << constant << "\n"; } }
Node InferBoundsResult::getLiteral() const{ const Rational& q = getValue().getNoninfinitesimalPart(); NodeManager* nm = NodeManager::currentNM(); Node qnode = nm->mkConst(q); Kind k; if(d_upperBound){ // x <= q + c*delta Assert(getValue().infinitesimalSgn() <= 0); k = boundIsRational() ? kind::LEQ : kind::LT; }else{ // x >= q + c*delta Assert(getValue().infinitesimalSgn() >= 0); k = boundIsRational() ? kind::GEQ : kind::GT; } Node atom = nm->mkNode(k, getTerm(), qnode); Node lit = Rewriter::rewrite(atom); return lit; }
EqualityStatus InequalitySolver::getEqualityStatus(TNode a, TNode b) { if (!isComplete()) return EQUALITY_UNKNOWN; NodeManager* nm = NodeManager::currentNM(); Node a_lt_b = nm->mkNode(kind::BITVECTOR_ULT, a, b); Node b_lt_a = nm->mkNode(kind::BITVECTOR_ULT, b, a); // if an inequality containing the terms has been asserted then we know // the equality is false if (d_assertionSet.contains(a_lt_b) || d_assertionSet.contains(b_lt_a)) { return EQUALITY_FALSE; } if (!d_inequalityGraph.hasValueInModel(a) || !d_inequalityGraph.hasValueInModel(b)) { return EQUALITY_UNKNOWN; } // TODO: check if this disequality is entailed by inequalities via // transitivity BitVector a_val = d_inequalityGraph.getValueInModel(a); BitVector b_val = d_inequalityGraph.getValueInModel(b); if (a_val == b_val) { return EQUALITY_TRUE_IN_MODEL; } else { return EQUALITY_FALSE_IN_MODEL; } }
void CoreSolver::buildModel() { Debug("bv-core") << "CoreSolver::buildModel() \n"; NodeManager* nm = NodeManager::currentNM(); d_modelValues.clear(); TNodeSet constants; TNodeSet constants_in_eq_engine; // collect constants in equality engine eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(&d_equalityEngine); while (!eqcs_i.isFinished()) { TNode repr = *eqcs_i; if (repr.getKind() == kind::CONST_BITVECTOR) { // must check if it's just the constant eq::EqClassIterator it(repr, &d_equalityEngine); if (!(++it).isFinished() || true) { constants.insert(repr); constants_in_eq_engine.insert(repr); } } ++eqcs_i; } // build repr to value map eqcs_i = eq::EqClassesIterator(&d_equalityEngine); while (!eqcs_i.isFinished()) { TNode repr = *eqcs_i; ++eqcs_i; if (!repr.isVar() && repr.getKind() != kind::CONST_BITVECTOR && !d_bv->isSharedTerm(repr)) { continue; } TypeNode type = repr.getType(); if (type.isBitVector() && repr.getKind() != kind::CONST_BITVECTOR) { Debug("bv-core-model") << " processing " << repr << "\n"; // we need to assign a value for it TypeEnumerator te(type); Node val; do { val = *te; ++te; // Debug("bv-core-model") << " trying value " << val << "\n"; // Debug("bv-core-model") << " is in set? " << constants.count(val) << // "\n"; Debug("bv-core-model") << " enumerator done? " << // te.isFinished() << "\n"; } while (constants.count(val) != 0 && !(te.isFinished())); if (te.isFinished() && constants.count(val) != 0) { // if we cannot enumerate anymore values we just return the lemma // stating that at least two of the representatives are equal. std::vector<TNode> representatives; representatives.push_back(repr); for (TNodeSet::const_iterator it = constants_in_eq_engine.begin(); it != constants_in_eq_engine.end(); ++it) { TNode constant = *it; if (utils::getSize(constant) == utils::getSize(repr)) { representatives.push_back(constant); } } for (ModelValue::const_iterator it = d_modelValues.begin(); it != d_modelValues.end(); ++it) { representatives.push_back(it->first); } std::vector<Node> equalities; for (unsigned i = 0; i < representatives.size(); ++i) { for (unsigned j = i + 1; j < representatives.size(); ++j) { TNode a = representatives[i]; TNode b = representatives[j]; if (a.getKind() == kind::CONST_BITVECTOR && b.getKind() == kind::CONST_BITVECTOR) { Assert(a != b); continue; } if (utils::getSize(a) == utils::getSize(b)) { equalities.push_back(nm->mkNode(kind::EQUAL, a, b)); } } } // better off letting the SAT solver split on values if (equalities.size() > d_lemmaThreshold) { d_isComplete = false; return; } if (equalities.size() == 0) { Debug("bv-core") << " lemma: true (no equalities)" << std::endl; } else { Node lemma = utils::mkOr(equalities); d_bv->lemma(lemma); Debug("bv-core") << " lemma: " << lemma << std::endl; } return; } Debug("bv-core-model") << " " << repr << " => " << val << "\n"; constants.insert(val); d_modelValues[repr] = val; } } }
Node SymmetryBreaker::generateSymBkConstraints(const vector<vector<Node>>& parts) { vector<Node> constraints; NodeManager* nm = NodeManager::currentNM(); for (const vector<Node>& part : parts) { if (part.size() >= 2) { Kind kd = getOrderKind(part[0]); if (kd == UNDEFINED_KIND) { // no symmetry breaking possible continue; } if (kd != EQUAL) { for (unsigned int i = 0; i < part.size() - 1; ++i) { // Generate less than or equal to constraints: part[i] <= part[i+1] Node constraint = nm->mkNode(kd, part[i], part[i + 1]); constraints.push_back(constraint); Trace("sym-bk") << "[sym-bk] Generate a symmetry breaking constraint: " << constraint << endl; } } else if (part.size() >= 3) { for (unsigned int i = 0; i < part.size(); ++i) { for (unsigned int j = i + 2; j < part.size(); ++j) { // Generate consecutive constraints v_i = v_j => v_i = v_{j-1}, // for all 0 <= i < j-1 < j < part.size() Node constraint = nm->mkNode(IMPLIES, nm->mkNode(kd, part[i], part[j]), nm->mkNode(kd, part[i], part[j - 1])); constraints.push_back(constraint); Trace("sym-bk") << "[sym-bk] Generate a symmetry breaking constraint: " << constraint << endl; } if (i >= 1) { for (unsigned int j = i + 1; j < part.size(); ++j) { Node lhs = nm->mkNode(kd, part[i], part[j]); Node rhs = nm->mkNode(kd, part[i], part[i - 1]); int prev_seg_start_index = 2*i - j - 1; // Since prev_seg_len is always less than i - 1, we just need to make // sure prev_seg_len is greater than or equal to 0 if(prev_seg_start_index >= 0) { rhs = nm->mkNode( OR, rhs, nm->mkNode(kd, part[i - 1], part[prev_seg_start_index])); } // Generate length order constraints // v_i = v_j => (v_{i} = v_{i-1} OR v_{i-1} = x_{(i-1)-(j-i)}) // for all 1 <= i < j < part.size() and (i-1)-(j-i) >= 0 Node constraint = nm->mkNode(IMPLIES, lhs, rhs); constraints.push_back(constraint); Trace("sym-bk") << "[sym-bk] Generate a symmetry breaking constraint: " << constraint << endl; } } } } } } if(constraints.empty()) { return d_trueNode; } else if(constraints.size() == 1) { return constraints[0]; } return nm->mkNode(AND, constraints); }
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 DtInstantiator::solve_dt(Node v, Node a, Node b, Node sa, Node sb) { Trace("cegqi-arith-debug2") << "Solve dt : " << v << " " << a << " " << b << " " << sa << " " << sb << std::endl; Node ret; if (!a.isNull() && a == v) { ret = sb; } else if (!b.isNull() && b == v) { ret = sa; } else if (!a.isNull() && a.getKind() == APPLY_CONSTRUCTOR) { if (!b.isNull() && b.getKind() == APPLY_CONSTRUCTOR) { if (a.getOperator() == b.getOperator()) { for (unsigned i = 0, nchild = a.getNumChildren(); i < nchild; i++) { Node s = solve_dt(v, a[i], b[i], sa[i], sb[i]); if (!s.isNull()) { return s; } } } } else { NodeManager* nm = NodeManager::currentNM(); unsigned cindex = Datatype::indexOf(a.getOperator().toExpr()); TypeNode tn = a.getType(); const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype(); for (unsigned i = 0, nchild = a.getNumChildren(); i < nchild; i++) { Node nn = nm->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr(dt[cindex].getSelectorInternal(tn.toType(), i)), sb); Node s = solve_dt(v, a[i], Node::null(), sa[i], nn); if (!s.isNull()) { return s; } } } } else if (!b.isNull() && b.getKind() == APPLY_CONSTRUCTOR) { // flip sides return solve_dt(v, b, a, sb, sa); } if (!ret.isNull()) { // ensure does not contain v if (expr::hasSubterm(ret, v)) { ret = Node::null(); } } return ret; }
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; } }
PreprocessingPassResult SygusAbduct::applyInternal( AssertionPipeline* assertionsToPreprocess) { NodeManager* nm = NodeManager::currentNM(); Trace("sygus-abduct") << "Run sygus abduct..." << std::endl; Trace("sygus-abduct-debug") << "Collect symbols..." << std::endl; std::unordered_set<Node, NodeHashFunction> symset; std::vector<Node>& asserts = assertionsToPreprocess->ref(); // do we have any assumptions, e.g. via check-sat-assuming? bool usingAssumptions = (assertionsToPreprocess->getNumAssumptions() > 0); // The following is our set of "axioms". We construct this set only when the // usingAssumptions (above) is true. In this case, our input formula is // partitioned into Fa ^ Fc as described in the header of this class, where: // - The conjunction of assertions marked as assumptions are the negated // conjecture Fc, and // - The conjunction of all other assertions are the axioms Fa. std::vector<Node> axioms; for (size_t i = 0, size = asserts.size(); i < size; i++) { expr::getSymbols(asserts[i], symset); // if we are not an assumption, add it to the set of axioms if (usingAssumptions && i < assertionsToPreprocess->getAssumptionsStart()) { axioms.push_back(asserts[i]); } } Trace("sygus-abduct-debug") << "...finish, got " << symset.size() << " symbols." << std::endl; Trace("sygus-abduct-debug") << "Setup symbols..." << std::endl; std::vector<Node> syms; std::vector<Node> vars; std::vector<Node> varlist; std::vector<TypeNode> varlistTypes; for (const Node& s : symset) { TypeNode tn = s.getType(); if (tn.isFirstClass()) { std::stringstream ss; ss << s; Node var = nm->mkBoundVar(tn); syms.push_back(s); vars.push_back(var); Node vlv = nm->mkBoundVar(ss.str(), tn); varlist.push_back(vlv); varlistTypes.push_back(tn); } } Trace("sygus-abduct-debug") << "...finish" << std::endl; Trace("sygus-abduct-debug") << "Make abduction predicate..." << std::endl; // make the abduction predicate to synthesize TypeNode abdType = varlistTypes.empty() ? nm->booleanType() : nm->mkPredicateType(varlistTypes); Node abd = nm->mkBoundVar("A", abdType); Trace("sygus-abduct-debug") << "...finish" << std::endl; Trace("sygus-abduct-debug") << "Make abduction predicate app..." << std::endl; std::vector<Node> achildren; achildren.push_back(abd); achildren.insert(achildren.end(), vars.begin(), vars.end()); Node abdApp = vars.empty() ? abd : nm->mkNode(APPLY_UF, achildren); Trace("sygus-abduct-debug") << "...finish" << std::endl; Trace("sygus-abduct-debug") << "Set attributes..." << std::endl; // set the sygus bound variable list Node abvl = nm->mkNode(BOUND_VAR_LIST, varlist); abd.setAttribute(theory::SygusSynthFunVarListAttribute(), abvl); Trace("sygus-abduct-debug") << "...finish" << std::endl; Trace("sygus-abduct-debug") << "Make conjecture body..." << std::endl; Node input = asserts.size() == 1 ? asserts[0] : nm->mkNode(AND, asserts); input = input.substitute(syms.begin(), syms.end(), vars.begin(), vars.end()); // A(x) => ~input( x ) input = nm->mkNode(OR, abdApp.negate(), input.negate()); Trace("sygus-abduct-debug") << "...finish" << std::endl; Trace("sygus-abduct-debug") << "Make conjecture..." << std::endl; Node res = input.negate(); if (!vars.empty()) { Node bvl = nm->mkNode(BOUND_VAR_LIST, vars); // exists x. ~( A( x ) => ~input( x ) ) res = nm->mkNode(EXISTS, bvl, res); } // sygus attribute Node sygusVar = nm->mkSkolem("sygus", nm->booleanType()); theory::SygusAttribute ca; sygusVar.setAttribute(ca, true); Node instAttr = nm->mkNode(INST_ATTRIBUTE, sygusVar); std::vector<Node> iplc; iplc.push_back(instAttr); if (!axioms.empty()) { Node aconj = axioms.size() == 1 ? axioms[0] : nm->mkNode(AND, axioms); aconj = aconj.substitute(syms.begin(), syms.end(), vars.begin(), vars.end()); Trace("sygus-abduct") << "---> Assumptions: " << aconj << std::endl; Node sc = nm->mkNode(AND, aconj, abdApp); Node vbvl = nm->mkNode(BOUND_VAR_LIST, vars); sc = nm->mkNode(EXISTS, vbvl, sc); Node sygusScVar = nm->mkSkolem("sygus_sc", nm->booleanType()); sygusScVar.setAttribute(theory::SygusSideConditionAttribute(), sc); instAttr = nm->mkNode(INST_ATTRIBUTE, sygusScVar); // build in the side condition // exists x. A( x ) ^ input_axioms( x ) // as an additional annotation on the sygus conjecture. In other words, // the abducts A we procedure must be consistent with our axioms. iplc.push_back(instAttr); } Node instAttrList = nm->mkNode(INST_PATTERN_LIST, iplc); Node fbvl = nm->mkNode(BOUND_VAR_LIST, abd); // forall A. exists x. ~( A( x ) => ~input( x ) ) res = nm->mkNode(FORALL, fbvl, res, instAttrList); Trace("sygus-abduct-debug") << "...finish" << std::endl; res = theory::Rewriter::rewrite(res); Trace("sygus-abduct") << "Generate: " << res << std::endl; Node trueNode = nm->mkConst(true); assertionsToPreprocess->replace(0, res); for (size_t i = 1, size = assertionsToPreprocess->size(); i < size; ++i) { assertionsToPreprocess->replace(i, trueNode); } return PreprocessingPassResult::NO_CONFLICT; }
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(); } }
Node NaryBuilder::mkAssoc(Kind kind, const std::vector<Node>& children){ if(children.size() == 0){ return zeroArity(kind); }else if(children.size() == 1){ return children[0]; }else{ const unsigned int max = kind::metakind::getUpperBoundForKind(kind); const unsigned int min = kind::metakind::getLowerBoundForKind(kind); Assert(min <= children.size()); unsigned int numChildren = children.size(); NodeManager* nm = NodeManager::currentNM(); if( numChildren <= max ) { return nm->mkNode(kind,children); } typedef std::vector<Node>::const_iterator const_iterator; const_iterator it = children.begin() ; const_iterator end = children.end() ; /* The new top-level children and the children of each sub node */ std::vector<Node> newChildren; std::vector<Node> subChildren; while( it != end && numChildren > max ) { /* Grab the next max children and make a node for them. */ for(const_iterator next = it + max; it != next; ++it, --numChildren ) { subChildren.push_back(*it); } Node subNode = nm->mkNode(kind,subChildren); newChildren.push_back(subNode); subChildren.clear(); } /* If there's children left, "top off" the Expr. */ if(numChildren > 0) { /* If the leftovers are too few, just copy them into newChildren; * otherwise make a new sub-node */ if(numChildren < min) { for(; it != end; ++it) { newChildren.push_back(*it); } } else { for(; it != end; ++it) { subChildren.push_back(*it); } Node subNode = nm->mkNode(kind, subChildren); newChildren.push_back(subNode); } } /* It's inconceivable we could have enough children for this to fail * (more than 2^32, in most cases?). */ AlwaysAssert( newChildren.size() <= max, "Too many new children in mkAssociative" ); /* It would be really weird if this happened (it would require * min > 2, for one thing), but let's make sure. */ AlwaysAssert( newChildren.size() >= min, "Too few new children in mkAssociative" ); return nm->mkNode(kind,newChildren); } }
Node PseudoBooleanProcessor::mkGeqOne(Node v) { NodeManager* nm = NodeManager::currentNM(); return nm->mkNode(kind::GEQ, v, mkRationalNode(Rational(1))); }
Node BVToBool::convertBvTerm(TNode node) { Assert(node.getType().isBitVector() && node.getType().getBitVectorSize() == 1); if (hasBoolCache(node)) return getBoolCache(node); NodeManager* nm = NodeManager::currentNM(); if (!isConvertibleBvTerm(node)) { ++(d_statistics.d_numTermsForcedLifted); Node result = nm->mkNode(kind::EQUAL, node, d_one); addToBoolCache(node, result); Debug("bv-to-bool") << "BVToBool::convertBvTerm " << node << " => " << result << "\n"; return result; } if (node.getNumChildren() == 0) { Assert(node.getKind() == kind::CONST_BITVECTOR); Node result = node == d_one ? bv::utils::mkTrue() : bv::utils::mkFalse(); // addToCache(node, result); Debug("bv-to-bool") << "BVToBool::convertBvTerm " << node << " => " << result << "\n"; return result; } ++(d_statistics.d_numTermsLifted); Kind kind = node.getKind(); if (kind == kind::ITE) { Node cond = liftNode(node[0]); Node true_branch = convertBvTerm(node[1]); Node false_branch = convertBvTerm(node[2]); Node result = nm->mkNode(kind::ITE, cond, true_branch, false_branch); addToBoolCache(node, result); Debug("bv-to-bool") << "BVToBool::convertBvTerm " << node << " => " << result << "\n"; return result; } Kind new_kind; // special case for XOR as it has to be binary // while BITVECTOR_XOR can be n-ary if (kind == kind::BITVECTOR_XOR) { new_kind = kind::XOR; Node result = convertBvTerm(node[0]); for (unsigned i = 1; i < node.getNumChildren(); ++i) { Node converted = convertBvTerm(node[i]); result = nm->mkNode(kind::XOR, result, converted); } Debug("bv-to-bool") << "BVToBool::convertBvTerm " << node << " => " << result << "\n"; return result; } if (kind == kind::BITVECTOR_COMP) { Node result = nm->mkNode(kind::EQUAL, node[0], node[1]); addToBoolCache(node, result); Debug("bv-to-bool") << "BVToBool::convertBvTerm " << node << " => " << result << "\n"; return result; } switch (kind) { case kind::BITVECTOR_OR: new_kind = kind::OR; break; case kind::BITVECTOR_AND: new_kind = kind::AND; break; case kind::BITVECTOR_NOT: new_kind = kind::NOT; break; default: Unhandled(); } NodeBuilder<> builder(new_kind); for (unsigned i = 0; i < node.getNumChildren(); ++i) { builder << convertBvTerm(node[i]); } Node result = builder; addToBoolCache(node, result); Debug("bv-to-bool") << "BVToBool::convertBvTerm " << node << " => " << result << "\n"; return result; }
// static RewriteResponse TheorySetsRewriter::postRewrite(TNode node) { NodeManager* nm = NodeManager::currentNM(); switch(node.getKind()) { case kind::IN: { if(!node[0].isConst() || !node[1].isConst()) break; // both are constants bool isMember = checkConstantMembership(node[0], node[1]); return RewriteResponse(REWRITE_DONE, nm->mkConst(isMember)); } case kind::SUBSET: { // rewrite (A subset-or-equal B) as (A union B = B) TNode A = node[0]; TNode B = node[1]; return RewriteResponse(REWRITE_AGAIN, nm->mkNode(kind::EQUAL, nm->mkNode(kind::UNION, A, B), B) ); }//kind::SUBSET case kind::EQUAL: case kind::IFF: { //rewrite: t = t with true (t term) //rewrite: c = c' with c different from c' false (c, c' constants) //otherwise: sort them if(node[0] == node[1]) { Trace("sets-postrewrite") << "Sets::postRewrite returning true" << std::endl; return RewriteResponse(REWRITE_DONE, nm->mkConst(true)); } else if (node[0].isConst() && node[1].isConst()) { Trace("sets-postrewrite") << "Sets::postRewrite returning false" << std::endl; return RewriteResponse(REWRITE_DONE, nm->mkConst(false)); } else if (node[0] > node[1]) { Node newNode = nm->mkNode(node.getKind(), node[1], node[0]); Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; return RewriteResponse(REWRITE_DONE, newNode); } break; } case kind::UNION: case kind::INTERSECTION: { if(node[0] == node[1]) { Trace("sets-postrewrite") << "Sets::postRewrite returning " << node[0] << std::endl; return RewriteResponse(REWRITE_DONE, node[0]); } else if (node[0] > node[1]) { Node newNode = nm->mkNode(node.getKind(), node[1], node[0]); Trace("sets-postrewrite") << "Sets::postRewrite returning " << newNode << std::endl; return RewriteResponse(REWRITE_DONE, newNode); } break; } default: break; }//switch(node.getKind()) // This default implementation return RewriteResponse(REWRITE_DONE, node); }