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; }
bool InstStrategyCbqi::hasNonCbqiVariable( Node q ){ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ TypeNode tn = q[0][i].getType(); if( !tn.isInteger() && !tn.isReal() && !tn.isBoolean() ){ if( options::cbqiSplx() ){ return true; }else{ //datatypes supported in new implementation if( !tn.isDatatype() ){ return true; } } } } return false; }
Kind SymmetryBreaker::getOrderKind(Node node) { TypeNode tn = node.getType(); if (tn.isBoolean()) { return IMPLIES; } else if (tn.isReal()) { return LEQ; } else if (tn.isBitVector()) { return BITVECTOR_ULE; } if (tn.isFirstClass()) { return EQUAL; } return UNDEFINED_KIND; }
Node AbsDef::getFunctionValue( FirstOrderModelAbs * m, TNode op, std::vector< Node >& vars, unsigned depth ) { if( depth==vars.size() ){ TypeNode tn = op.getType(); if( tn.getNumChildren()>0 ){ tn = tn[tn.getNumChildren() - 1]; } if( d_value>=0 ){ Assert( d_value<(int)m->d_rep_set.d_type_reps[tn].size() ); if( tn.isBoolean() ){ return NodeManager::currentNM()->mkConst( d_value==1 ); }else{ return m->d_rep_set.d_type_reps[tn][d_value]; } }else{ return Node::null(); } }else{ TypeNode tn = vars[depth].getType(); Node curr; curr = d_def[d_default].getFunctionValue( m, op, vars, depth+1 ); for( std::map< unsigned, AbsDef >::iterator it = d_def.begin(); it != d_def.end(); ++it ){ if( it->first!=d_default ){ unsigned id = getId( it->first ); Assert( id<m->d_rep_set.d_type_reps[tn].size() ); TNode n = m->d_rep_set.d_type_reps[tn][id]; Node fv = it->second.getFunctionValue( m, op, vars, depth+1 ); if( !curr.isNull() && !fv.isNull() ){ curr = NodeManager::currentNM()->mkNode( ITE, vars[depth].eqNode( n ), fv, curr ); }else{ curr = Node::null(); } } } return curr; } }
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; } }
void TheoryEngineModelBuilder::buildModel(Model* m, bool fullModel) { TheoryModel* tm = (TheoryModel*)m; // buildModel with fullModel = true should only be called once in any context Assert(!tm->d_modelBuilt); tm->d_modelBuilt = fullModel; // Reset model tm->reset(); // Collect model info from the theories Trace("model-builder") << "TheoryEngineModelBuilder: Collect model info..." << std::endl; d_te->collectModelInfo(tm, fullModel); // Loop through all terms and make sure that assignable sub-terms are in the equality engine eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( &tm->d_equalityEngine ); { NodeSet cache; for ( ; !eqcs_i.isFinished(); ++eqcs_i) { eq::EqClassIterator eqc_i = eq::EqClassIterator((*eqcs_i), &tm->d_equalityEngine); for ( ; !eqc_i.isFinished(); ++eqc_i) { checkTerms(*eqc_i, tm, cache); } } } Trace("model-builder") << "Collect representatives..." << std::endl; // Process all terms in the equality engine, store representatives for each EC std::map< Node, Node > assertedReps, constantReps; TypeSet typeConstSet, typeRepSet, typeNoRepSet; std::set< TypeNode > allTypes; eqcs_i = eq::EqClassesIterator(&tm->d_equalityEngine); for ( ; !eqcs_i.isFinished(); ++eqcs_i) { // eqc is the equivalence class representative Node eqc = (*eqcs_i); Trace("model-builder") << "Processing EC: " << eqc << endl; Assert(tm->d_equalityEngine.getRepresentative(eqc) == eqc); TypeNode eqct = eqc.getType(); Assert(assertedReps.find(eqc) == assertedReps.end()); Assert(constantReps.find(eqc) == constantReps.end()); // Loop through terms in this EC Node rep, const_rep; eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, &tm->d_equalityEngine); for ( ; !eqc_i.isFinished(); ++eqc_i) { Node n = *eqc_i; Trace("model-builder") << " Processing Term: " << n << endl; // Record as rep if this node was specified as a representative if (tm->d_reps.find(n) != tm->d_reps.end()){ Assert(rep.isNull()); rep = tm->d_reps[n]; Assert(!rep.isNull() ); Trace("model-builder") << " Rep( " << eqc << " ) = " << rep << std::endl; } // Record as const_rep if this node is constant if (n.isConst()) { Assert(const_rep.isNull()); const_rep = n; Trace("model-builder") << " ConstRep( " << eqc << " ) = " << const_rep << std::endl; } //model-specific processing of the term tm->addTerm(n); } // Assign representative for this EC if (!const_rep.isNull()) { // Theories should not specify a rep if there is already a constant in the EC Assert(rep.isNull() || rep == const_rep); constantReps[eqc] = const_rep; typeConstSet.add(eqct.getBaseType(), const_rep); } else if (!rep.isNull()) { assertedReps[eqc] = rep; typeRepSet.add(eqct.getBaseType(), eqc); allTypes.insert(eqct); } else { typeNoRepSet.add(eqct, eqc); allTypes.insert(eqct); } } // Need to ensure that each EC has a constant representative. Trace("model-builder") << "Processing EC's..." << std::endl; TypeSet::iterator it; set<TypeNode>::iterator type_it; set<Node>::iterator i, i2; bool changed, unassignedAssignable, assignOne = false; set<TypeNode> evaluableSet; // Double-fixed-point loop // Outer loop handles a special corner case (see code at end of loop for details) for (;;) { // Inner fixed-point loop: we are trying to learn constant values for every EC. Each time through this loop, we process all of the // types by type and may learn some new EC values. EC's in one type may depend on EC's in another type, so we need a fixed-point loop // to ensure that we learn as many EC values as possible do { changed = false; unassignedAssignable = false; evaluableSet.clear(); // Iterate over all types we've seen for (type_it = allTypes.begin(); type_it != allTypes.end(); ++type_it) { TypeNode t = *type_it; TypeNode tb = t.getBaseType(); set<Node>* noRepSet = typeNoRepSet.getSet(t); // 1. Try to evaluate the EC's in this type if (noRepSet != NULL && !noRepSet->empty()) { Trace("model-builder") << " Eval phase, working on type: " << t << endl; bool assignable, evaluable, evaluated; d_normalizedCache.clear(); for (i = noRepSet->begin(); i != noRepSet->end(); ) { i2 = i; ++i; assignable = false; evaluable = false; evaluated = false; eq::EqClassIterator eqc_i = eq::EqClassIterator(*i2, &tm->d_equalityEngine); for ( ; !eqc_i.isFinished(); ++eqc_i) { Node n = *eqc_i; if (isAssignable(n)) { assignable = true; } else { evaluable = true; Node normalized = normalize(tm, n, constantReps, true); if (normalized.isConst()) { typeConstSet.add(tb, normalized); constantReps[*i2] = normalized; Trace("model-builder") << " Eval: Setting constant rep of " << (*i2) << " to " << normalized << endl; changed = true; evaluated = true; noRepSet->erase(i2); break; } } } if (!evaluated) { if (evaluable) { evaluableSet.insert(tb); } if (assignable) { unassignedAssignable = true; } } } } // 2. Normalize any non-const representative terms for this type set<Node>* repSet = typeRepSet.getSet(t); if (repSet != NULL && !repSet->empty()) { Trace("model-builder") << " Normalization phase, working on type: " << t << endl; d_normalizedCache.clear(); for (i = repSet->begin(); i != repSet->end(); ) { Assert(assertedReps.find(*i) != assertedReps.end()); Node rep = assertedReps[*i]; Node normalized = normalize(tm, rep, constantReps, false); Trace("model-builder") << " Normalizing rep (" << rep << "), normalized to (" << normalized << ")" << endl; if (normalized.isConst()) { changed = true; typeConstSet.add(t.getBaseType(), normalized); constantReps[*i] = normalized; assertedReps.erase(*i); i2 = i; ++i; repSet->erase(i2); } else { if (normalized != rep) { assertedReps[*i] = normalized; changed = true; } ++i; } } } } } while (changed); if (!fullModel || !unassignedAssignable) { break; } // 3. Assign unassigned assignable EC's using type enumeration - assign a value *different* from all other EC's if the type is infinite // Assign first value from type enumerator otherwise - for finite types, we rely on polite framework to ensure that EC's that have to be // different are different. // Only make assignments on a type if: // 1. fullModel is true // 2. there are no terms that share the same base type with un-normalized representatives // 3. there are no terms that share teh same base type that are unevaluated evaluable terms // Alternatively, if 2 or 3 don't hold but we are in a special deadlock-breaking mode where assignOne is true, go ahead and make one assignment changed = false; for (it = typeNoRepSet.begin(); it != typeNoRepSet.end(); ++it) { set<Node>& noRepSet = TypeSet::getSet(it); if (noRepSet.empty()) { continue; } TypeNode t = TypeSet::getType(it); TypeNode tb = t.getBaseType(); if (!assignOne) { set<Node>* repSet = typeRepSet.getSet(tb); if (repSet != NULL && !repSet->empty()) { continue; } if (evaluableSet.find(tb) != evaluableSet.end()) { continue; } } Trace("model-builder") << " Assign phase, working on type: " << t << endl; bool assignable, evaluable CVC4_UNUSED; for (i = noRepSet.begin(); i != noRepSet.end(); ) { i2 = i; ++i; eq::EqClassIterator eqc_i = eq::EqClassIterator(*i2, &tm->d_equalityEngine); assignable = false; evaluable = false; for ( ; !eqc_i.isFinished(); ++eqc_i) { Node n = *eqc_i; if (isAssignable(n)) { assignable = true; } else { evaluable = true; } } if (assignable) { Assert(!evaluable || assignOne); Assert(!t.isBoolean() || (*i2).getKind() == kind::APPLY_UF); Node n; if (t.getCardinality().isInfinite()) { n = typeConstSet.nextTypeEnum(t, true); } else { TypeEnumerator te(t); n = *te; } Assert(!n.isNull()); constantReps[*i2] = n; Trace("model-builder") << " Assign: Setting constant rep of " << (*i2) << " to " << n << endl; changed = true; noRepSet.erase(i2); if (assignOne) { assignOne = false; break; } } } } // Corner case - I'm not sure this can even happen - but it's theoretically possible to have a cyclical dependency // in EC assignment/evaluation, e.g. EC1 = {a, b + 1}; EC2 = {b, a - 1}. In this case, neither one will get assigned because we are waiting // to be able to evaluate. But we will never be able to evaluate because the variables that need to be assigned are in // these same EC's. In this case, repeat the whole fixed-point computation with the difference that the first EC // that has both assignable and evaluable expressions will get assigned. if (!changed) { Assert(!assignOne); // check for infinite loop! assignOne = true; } } #ifdef CVC4_ASSERTIONS if (fullModel) { // Assert that all representatives have been converted to constants for (it = typeRepSet.begin(); it != typeRepSet.end(); ++it) { set<Node>& repSet = TypeSet::getSet(it); if (!repSet.empty()) { Trace("model-builder") << "***Non-empty repSet, size = " << repSet.size() << ", first = " << *(repSet.begin()) << endl; Assert(false); } } } #endif /* CVC4_ASSERTIONS */ Trace("model-builder") << "Copy representatives to model..." << std::endl; tm->d_reps.clear(); std::map< Node, Node >::iterator itMap; for (itMap = constantReps.begin(); itMap != constantReps.end(); ++itMap) { tm->d_reps[itMap->first] = itMap->second; tm->d_rep_set.add(itMap->second); } if (!fullModel) { // Make sure every EC has a rep for (itMap = assertedReps.begin(); itMap != assertedReps.end(); ++itMap ) { tm->d_reps[itMap->first] = itMap->second; tm->d_rep_set.add(itMap->second); } for (it = typeNoRepSet.begin(); it != typeNoRepSet.end(); ++it) { set<Node>& noRepSet = TypeSet::getSet(it); set<Node>::iterator i; for (i = noRepSet.begin(); i != noRepSet.end(); ++i) { tm->d_reps[*i] = *i; tm->d_rep_set.add(*i); } } } //modelBuilder-specific initialization processBuildModel( tm, fullModel ); #ifdef CVC4_ASSERTIONS if (fullModel) { // Check that every term evaluates to its representative in the model for (eqcs_i = eq::EqClassesIterator(&tm->d_equalityEngine); !eqcs_i.isFinished(); ++eqcs_i) { // eqc is the equivalence class representative Node eqc = (*eqcs_i); Node rep; itMap = constantReps.find(eqc); if (itMap == constantReps.end() && eqc.getType().isBoolean()) { rep = tm->getValue(eqc); Assert(rep.isConst()); } else { Assert(itMap != constantReps.end()); rep = itMap->second; } eq::EqClassIterator eqc_i = eq::EqClassIterator(eqc, &tm->d_equalityEngine); for ( ; !eqc_i.isFinished(); ++eqc_i) { Node n = *eqc_i; static int repCheckInstance = 0; ++repCheckInstance; Debug("check-model::rep-checking") << "( " << repCheckInstance <<") " << "n: " << n << endl << "getValue(n): " << tm->getValue(n) << endl << "rep: " << rep << endl; Assert(tm->getValue(*eqc_i) == rep); } } } #endif /* CVC4_ASSERTIONS */ }