/********************* */ /*! \file sygus_grammar_cons.cpp ** \verbatim ** Top contributors (to current version): ** Andrew Reynolds ** This file is part of the CVC4 project. ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** ** \brief implementation of class for constructing inductive datatypes that correspond to ** grammars that encode syntactic restrictions for SyGuS. **/ #include "theory/quantifiers/sygus_grammar_cons.h" #include #include "expr/datatype.h" #include "options/quantifiers_options.h" #include "theory/quantifiers/term_database_sygus.h" #include "theory/quantifiers/term_util.h" using namespace CVC4::kind; using namespace std; namespace CVC4 { namespace theory { namespace quantifiers { CegGrammarConstructor::CegGrammarConstructor( QuantifiersEngine * qe ) : d_qe( qe ), d_is_syntax_restricted(false), d_has_ite(true){ } void CegGrammarConstructor::collectTerms( Node n, std::map< TypeNode, std::vector< Node > >& consts ){ std::unordered_map visited; std::unordered_map::iterator it; std::stack visit; TNode cur; visit.push(n); do { cur = visit.top(); visit.pop(); it = visited.find(cur); if (it == visited.end()) { visited[cur] = true; // is this a constant? if( cur.isConst() ){ TypeNode tn = cur.getType(); Node c = cur; if( tn.isReal() ){ c = NodeManager::currentNM()->mkConst( c.getConst().abs() ); } if( std::find( consts[tn].begin(), consts[tn].end(), c )==consts[tn].end() ){ Trace("cegqi-debug") << "...consider const : " << c << std::endl; consts[tn].push_back( c ); } } // recurse for (unsigned i = 0; i < cur.getNumChildren(); i++) { visit.push(cur[i]); } } } while (!visit.empty()); } Node CegGrammarConstructor::process( Node q, std::map< Node, Node >& templates, std::map< Node, Node >& templates_arg ) { // convert to deep embedding and finalize single invocation here // now, construct the grammar Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl; std::map< TypeNode, std::vector< Node > > extra_cons; if( options::sygusAddConstGrammar() ){ Trace("cegqi") << "CegConjecture : collect constants..." << std::endl; collectTerms( q[1], extra_cons ); } std::vector< Node > qchildren; std::map< Node, Node > synth_fun_vars; std::vector< Node > ebvl; Node qbody_subs = q[1]; for( unsigned i=0; i::iterator itt = templates.find( sf ); if( itt!=templates.end() ){ Node templ = itt->second; TNode templ_arg = templates_arg[sf]; Assert( !templ_arg.isNull() ); Trace("cegqi-debug") << "Template for " << sf << " is : " << templ << " with arg " << templ_arg << std::endl; // if there is a template for this argument, make a sygus type on top of it if( options::sygusTemplEmbedGrammar() ){ Trace("cegqi-debug") << " embed this template as a grammar..." << std::endl; tn = mkSygusTemplateType( templ, templ_arg, tn, sfvl, ss.str() ); }else{ // otherwise, apply it as a preprocessing pass Trace("cegqi-debug") << " apply this template as a substituion during preprocess..." << std::endl; std::vector< Node > schildren; std::vector< Node > largs; for( unsigned j=0; jmkBoundVar( sfvl[j].getType() ) ); } std::vector< Node > subsfn_children; subsfn_children.push_back( sf ); subsfn_children.insert( subsfn_children.end(), schildren.begin(), schildren.end() ); Node subsfn = NodeManager::currentNM()->mkNode( kind::APPLY_UF, subsfn_children ); TNode subsf = subsfn; Trace("cegqi-debug") << " substitute arg : " << templ_arg << " -> " << subsf << std::endl; templ = templ.substitute( templ_arg, subsf ); // substitute lambda arguments templ = templ.substitute( schildren.begin(), schildren.end(), largs.begin(), largs.end() ); Node subsn = NodeManager::currentNM()->mkNode( kind::LAMBDA, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, largs ), templ ); TNode var = sf; TNode subs = subsn; Trace("cegqi-debug") << " substitute : " << var << " -> " << subs << std::endl; qbody_subs = qbody_subs.substitute( var, subs ); Trace("cegqi-debug") << " body is now : " << qbody_subs << std::endl; } } d_qe->getTermDatabaseSygus()->registerSygusType( tn ); // check grammar restrictions if( !d_qe->getTermDatabaseSygus()->sygusToBuiltinType( tn ).isBoolean() ){ if( !d_qe->getTermDatabaseSygus()->hasKind( tn, ITE ) ){ d_has_ite = false; } } Assert( tn.isDatatype() ); const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); Assert( dt.isSygus() ); if( !dt.getSygusAllowAll() ){ d_is_syntax_restricted = true; } // ev is the first-order variable corresponding to this synth fun std::stringstream ssf; ssf << "f" << sf; Node ev = NodeManager::currentNM()->mkBoundVar( ssf.str(), tn ); ebvl.push_back( ev ); synth_fun_vars[sf] = ev; Trace("cegqi") << "...embedding synth fun : " << sf << " -> " << ev << std::endl; } qchildren.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, ebvl ) ); if( qbody_subs!=q[1] ){ Trace("cegqi") << "...rewriting : " << qbody_subs << std::endl; qbody_subs = Rewriter::rewrite( qbody_subs ); Trace("cegqi") << "...got : " << qbody_subs << std::endl; } qchildren.push_back( convertToEmbedding( qbody_subs, synth_fun_vars ) ); if( q.getNumChildren()==3 ){ qchildren.push_back( q[2] ); } return NodeManager::currentNM()->mkNode( kind::FORALL, qchildren ); } Node CegGrammarConstructor::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars ){ std::unordered_map visited; std::unordered_map::iterator it; std::stack visit; TNode cur; visit.push(n); do { cur = visit.top(); visit.pop(); it = visited.find(cur); if (it == visited.end()) { visited[cur] = Node::null(); visit.push(cur); for (unsigned i = 0; i < cur.getNumChildren(); i++) { visit.push(cur[i]); } } else if (it->second.isNull()) { Node ret = cur; Kind ret_k = cur.getKind(); Node op; bool childChanged = false; std::vector children; // get the potential operator if( cur.getNumChildren()>0 ){ if( cur.getKind()==kind::APPLY_UF ){ op = cur.getOperator(); } }else{ op = cur; } // is the operator a synth function? if( !op.isNull() ){ std::map< Node, Node >::iterator its = synth_fun_vars.find( op ); if( its!=synth_fun_vars.end() ){ Assert( its->second.getType().isDatatype() ); // will make into an application of an evaluation function const Datatype& dt = ((DatatypeType)its->second.getType().toType()).getDatatype(); Assert( dt.isSygus() ); children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); children.push_back( its->second ); childChanged = true; ret_k = kind::APPLY_UF; } } if( !childChanged ){ // otherwise, we apply the previous operator if( cur.getMetaKind() == kind::metakind::PARAMETERIZED ){ children.push_back( cur.getOperator() ); } } for (unsigned i = 0; i < cur.getNumChildren(); i++) { it = visited.find(cur[i]); Assert(it != visited.end()); Assert(!it->second.isNull()); childChanged = childChanged || cur[i] != it->second; children.push_back(it->second); } if (childChanged) { ret = NodeManager::currentNM()->mkNode(ret_k, children); } visited[cur] = ret; } } while (!visit.empty()); Assert(visited.find(n) != visited.end()); Assert(!visited.find(n)->second.isNull()); return visited[n]; } TypeNode CegGrammarConstructor::mkUnresolvedType(const std::string& name, std::set& unres) { TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER); unres.insert( unresolved.toType() ); return unresolved; } void CegGrammarConstructor::mkSygusConstantsForType( TypeNode type, std::vector& ops ) { if( type.isInteger() ){ ops.push_back(NodeManager::currentNM()->mkConst(Rational(0))); ops.push_back(NodeManager::currentNM()->mkConst(Rational(1))); }else if( type.isBitVector() ){ unsigned sz = ((BitVectorType)type.toType()).getSize(); BitVector bval0(sz, (unsigned int)0); ops.push_back( NodeManager::currentNM()->mkConst(bval0) ); BitVector bval1(sz, (unsigned int)1); ops.push_back( NodeManager::currentNM()->mkConst(bval1) ); }else if( type.isBoolean() ){ ops.push_back(NodeManager::currentNM()->mkConst(true)); ops.push_back(NodeManager::currentNM()->mkConst(false)); } //TODO : others? } void CegGrammarConstructor::collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels ){ if( !range.isBoolean() ){ if( std::find( types.begin(), types.end(), range )==types.end() ){ Trace("sygus-grammar-def") << "...will make grammar for " << range << std::endl; types.push_back( range ); if( range.isDatatype() ){ const Datatype& dt = ((DatatypeType)range.toType()).getDatatype(); for( unsigned i=0; i >& extra_cons, std::vector< CVC4::Datatype >& datatypes, std::set& unres ) { // collect the variables std::vector sygus_vars; if( !bvl.isNull() ){ for( unsigned i=0; i > ops; int startIndex = -1; Trace("sygus-grammar-def") << "Construct default grammar for " << fun << " " << range << std::endl; std::map< Type, Type > sygus_to_builtin; std::vector< TypeNode > types; std::map< TypeNode, std::vector< DatatypeConstructorArg > > sels; //types for each of the variables of parametric sort for( unsigned i=0; i unres_types; std::map< TypeNode, Type > type_to_unres; for( unsigned i=0; i()); //make unresolved type Type unres_t = mkUnresolvedType(dname, unres).toType(); unres_types.push_back(unres_t); type_to_unres[types[i]] = unres_t; sygus_to_builtin[unres_t] = types[i].toType(); } for( unsigned i=0; i cnames; std::vector > cargs; Type unres_t = unres_types[i]; //add variables for( unsigned j=0; j() ); } } //add constants std::vector< Node > consts; mkSygusConstantsForType( types[i], consts ); std::map< TypeNode, std::vector< Node > >::iterator itec = extra_cons.find( types[i] ); if( itec!=extra_cons.end() ){ //consts.insert( consts.end(), itec->second.begin(), itec->second.end() ); for( unsigned j=0; jsecond.size(); j++ ){ if( std::find( consts.begin(), consts.end(), itec->second[j] )==consts.end() ){ consts.push_back( itec->second[j] ); } } } for( unsigned j=0; j() ); } //ITE CVC4::Kind k = kind::ITE; Trace("sygus-grammar-def") << "...add for " << k << std::endl; ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); cnames.push_back( kind::kindToString(k) ); cargs.push_back( std::vector< CVC4::Type >() ); cargs.back().push_back(unres_bt); cargs.back().push_back(unres_t); cargs.back().push_back(unres_t); if( types[i].isInteger() ){ for( unsigned j=0; j<2; j++ ){ CVC4::Kind k = j==0 ? kind::PLUS : kind::MINUS; Trace("sygus-grammar-def") << "...add for " << k << std::endl; ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); cnames.push_back(kind::kindToString(k)); cargs.push_back( std::vector< CVC4::Type >() ); cargs.back().push_back(unres_t); cargs.back().push_back(unres_t); } }else if( types[i].isDatatype() ){ Trace("sygus-grammar-def") << "...add for constructors" << std::endl; const Datatype& dt = ((DatatypeType)types[i].toType()).getDatatype(); for( unsigned k=0; k() ); for( unsigned j=0; j() ); //Assert( type_to_unres.find(arg_type)!=type_to_unres.end() ); cargs.back().push_back( type_to_unres[arg_type] ); } } Trace("sygus-grammar-def") << "...make datatype " << datatypes.back() << std::endl; datatypes[i].setSygus( types[i].toType(), bvl.toExpr(), true, true ); for( unsigned j=0; jbooleanType(); datatypes.push_back(Datatype(dbname)); ops.push_back(std::vector()); std::vector cnames; std::vector > cargs; Trace("sygus-grammar-def") << "Make grammar for " << btype << " " << datatypes.back() << std::endl; //add variables for( unsigned i=0; i() ); } } //add constants if no variables and no connected types if( ops.back().empty() && types.empty() ){ std::vector< Node > consts; mkSygusConstantsForType( btype, consts ); for( unsigned j=0; j() ); } } //add operators for( unsigned i=0; i<3; i++ ){ CVC4::Kind k = i==0 ? kind::NOT : ( i==1 ? kind::AND : kind::OR ); Trace("sygus-grammar-def") << "...add for " << k << std::endl; ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); cnames.push_back(kind::kindToString(k)); cargs.push_back( std::vector< CVC4::Type >() ); if( k==kind::NOT ){ cargs.back().push_back(unres_bt); }else if( k==kind::AND || k==kind::OR ){ cargs.back().push_back(unres_bt); cargs.back().push_back(unres_bt); } } //add predicates for types for( unsigned i=0; ioperatorOf(k).toExpr()); std::stringstream ss; ss << kind::kindToString(k) << "_" << types[i]; cnames.push_back(ss.str()); cargs.push_back( std::vector< CVC4::Type >() ); cargs.back().push_back(unres_types[i]); cargs.back().push_back(unres_types[i]); //type specific predicates if( types[i].isInteger() ){ CVC4::Kind k = kind::LEQ; Trace("sygus-grammar-def") << "...add for " << k << std::endl; ops.back().push_back(NodeManager::currentNM()->operatorOf(k).toExpr()); cnames.push_back(kind::kindToString(k)); cargs.push_back( std::vector< CVC4::Type >() ); cargs.back().push_back(unres_types[i]); cargs.back().push_back(unres_types[i]); }else if( types[i].isDatatype() ){ //add for testers Trace("sygus-grammar-def") << "...add for testers" << std::endl; const Datatype& dt = ((DatatypeType)types[i].toType()).getDatatype(); for( unsigned k=0; k() ); cargs.back().push_back(unres_types[i]); } } } if( range==btype ){ startIndex = datatypes.size()-1; } Trace("sygus-grammar-def") << "...make datatype " << datatypes.back() << std::endl; datatypes.back().setSygus( btype.toType(), bvl.toExpr(), true, true ); for( unsigned j=0; j0 ){ CVC4::Datatype tmp_dt = datatypes[0]; datatypes[0] = datatypes[startIndex]; datatypes[startIndex] = tmp_dt; } } TypeNode CegGrammarConstructor::mkSygusDefaultType( TypeNode range, Node bvl, const std::string& fun, std::map< TypeNode, std::vector< Node > >& extra_cons ) { Trace("sygus-grammar-def") << "*** Make sygus default type " << range << ", make datatypes..." << std::endl; for( std::map< TypeNode, std::vector< Node > >::iterator it = extra_cons.begin(); it != extra_cons.end(); ++it ){ Trace("sygus-grammar-def") << " ...using " << it->second.size() << " extra constants for " << it->first << std::endl; } std::set unres; std::vector< CVC4::Datatype > datatypes; mkSygusDefaultGrammar( range, bvl, fun, extra_cons, datatypes, unres ); Trace("sygus-grammar-def") << "...made " << datatypes.size() << " datatypes, now make mutual datatype types..." << std::endl; Assert( !datatypes.empty() ); std::vector types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); Assert( types.size()==datatypes.size() ); return TypeNode::fromType( types[0] ); } TypeNode CegGrammarConstructor::mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, const std::string& fun, unsigned& tcount ) { if( templ==templ_arg ){ //Assert( templ_arg.getType()==sygusToBuiltinType( templ_arg_sygus_type ) ); return templ_arg_sygus_type; }else{ tcount++; std::set unres; std::vector< CVC4::Datatype > datatypes; std::stringstream ssd; ssd << fun << "_templ_" << tcount; std::string dbname = ssd.str(); datatypes.push_back(Datatype(dbname)); Node op; std::vector< Type > argTypes; if( templ.getNumChildren()==0 ){ // TODO : can short circuit to this case when !TermUtil::containsTerm( templ, templ_arg ) op = templ; }else{ Assert( templ.hasOperator() ); op = templ.getOperator(); // make constructor taking arguments types from children for( unsigned i=0; i types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); Assert( types.size()==1 ); return TypeNode::fromType( types[0] ); } } TypeNode CegGrammarConstructor::mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, const std::string& fun ) { unsigned tcount = 0; return mkSygusTemplateTypeRec( templ, templ_arg, templ_arg_sygus_type, bvl, fun, tcount ); } }/* namespace CVC4::theory::quantifiers */ }/* namespace CVC4::theory */ }/* namespace CVC4 */