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(); } }