diff options
author | ajreynol <andrew.j.reynolds@gmail.com> | 2017-07-10 14:06:52 -0500 |
---|---|---|
committer | ajreynol <andrew.j.reynolds@gmail.com> | 2017-07-10 14:07:11 -0500 |
commit | f3590092818d9eab9d961ea602093029ff472a85 (patch) | |
tree | 1401f00df0d9659ba2321ea2088fe0c3f4de9f52 /src/theory/quantifiers | |
parent | d598a9644862d176632071bca8448765d9cc3cc1 (diff) |
Merge datatype shared selectors/sygus comp 2017 branch. Modify the datatypes decision procedure to share selectors of the same type across multiple constructors. Major rewrite of the SyGuS solver. Adds several new strategies for I/O example problems (PBE) and invariant synthesis. Major simplifications to sygus parsing and synthesis conjecture representation. Do not support check-synth in portfolio. Add sygus regressions.
Diffstat (limited to 'src/theory/quantifiers')
19 files changed, 5386 insertions, 1542 deletions
diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp index 6920fc539..903a729f5 100644 --- a/src/theory/quantifiers/bounded_integers.cpp +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -778,7 +778,7 @@ Node BoundedIntegers::matchBoundVar( Node v, Node t, Node e ){ if( e.getKind()==kind::APPLY_CONSTRUCTOR ){ u = matchBoundVar( v, t[i], e[i] ); }else{ - Node se = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index][i].getSelector() ), e ); + Node se = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index].getSelectorInternal( e.getType().toType(), i ) ), e ); u = matchBoundVar( v, t[i], se ); } if( !u.isNull() ){ diff --git a/src/theory/quantifiers/ce_guided_instantiation.cpp b/src/theory/quantifiers/ce_guided_instantiation.cpp index e82af63e4..63e8aa365 100644 --- a/src/theory/quantifiers/ce_guided_instantiation.cpp +++ b/src/theory/quantifiers/ce_guided_instantiation.cpp @@ -16,6 +16,7 @@ #include "expr/datatype.h" #include "options/quantifiers_options.h" +#include "options/datatypes_options.h" #include "smt/smt_statistics_registry.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" @@ -32,19 +33,142 @@ namespace quantifiers { CegConjecture::CegConjecture( QuantifiersEngine * qe, context::Context* c ) - : d_qe( qe ), d_curr_lit( c, 0 ) { + : d_qe( qe ) { d_refine_count = 0; d_ceg_si = new CegConjectureSingleInv( qe, this ); + d_ceg_pbe = new CegConjecturePbe( qe, this ); } CegConjecture::~CegConjecture() { delete d_ceg_si; + delete d_ceg_pbe; +} + +Node CegConjecture::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars, std::map< Node, Node >& visited ){ + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + Node ret = n; + + std::vector< Node > children; + bool childChanged = false; + bool madeOp = false; + Kind ret_k = n.getKind(); + Node op; + if( n.getNumChildren()>0 ){ + if( n.getKind()==kind::APPLY_UF ){ + op = n.getOperator(); + } + }else{ + op = n; + } + // is it a synth function? + std::map< Node, Node >::iterator its = synth_fun_vars.find( op ); + if( its!=synth_fun_vars.end() ){ + Assert( its->second.getType().isDatatype() ); + // make into 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 ); + madeOp = true; + childChanged = true; + ret_k = kind::APPLY_UF; + } + if( n.getNumChildren()>0 || childChanged ){ + if( !madeOp ){ + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node nc = convertToEmbedding( n[i], synth_fun_vars, visited ); + childChanged = childChanged || nc!=n[i]; + children.push_back( nc ); + } + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( ret_k, children ); + } + } + visited[n] = ret; + return ret; + }else{ + return it->second; + } +} + +void CegConjecture::collectConstants( Node n, std::map< TypeNode, std::vector< Node > >& consts, std::map< Node, bool >& visited ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( n.isConst() ){ + TypeNode tn = n.getType(); + Node nc = n; + if( tn.isReal() ){ + nc = NodeManager::currentNM()->mkConst( n.getConst<Rational>().abs() ); + } + if( std::find( consts[tn].begin(), consts[tn].end(), nc )==consts[tn].end() ){ + Trace("cegqi-debug") << "...consider const : " << nc << std::endl; + consts[tn].push_back( nc ); + } + } + + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + collectConstants( n[i], consts, visited ); + } + } } void CegConjecture::assign( Node q ) { Assert( d_quant.isNull() ); Assert( q.getKind()==FORALL ); + Trace("cegqi") << "CegConjecture : assign : " << q << std::endl; d_assert_quant = q; + std::map< TypeNode, std::vector< Node > > extra_cons; + + Trace("cegqi") << "CegConjecture : collect constants..." << std::endl; + if( options::sygusAddConstGrammar() ){ + std::map< Node, bool > cvisited; + collectConstants( q[1], extra_cons, cvisited ); + } + + Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl; + //convert to deep embedding + std::vector< Node > qchildren; + std::map< Node, Node > visited; + std::map< Node, Node > synth_fun_vars; + std::vector< Node > ebvl; + for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ + Node v = q[0][i]; + Node sf = v.getAttribute(SygusSynthFunAttribute()); + Assert( !sf.isNull() ); + Node sfvl = sf.getAttribute(SygusSynthFunVarListAttribute()); + // sfvl may be null for constant synthesis functions + Trace("cegqi-debug") << "...sygus var list associated with " << sf << " is " << sfvl << std::endl; + TypeNode tn; + if( v.getType().isDatatype() && ((DatatypeType)v.getType().toType()).getDatatype().isSygus() ){ + tn = v.getType(); + }else{ + // make the default grammar + std::stringstream ss; + ss << sf; + tn = d_qe->getTermDatabaseSygus()->mkSygusDefaultType( v.getType(), sfvl, ss.str(), extra_cons ); + } + d_qe->getTermDatabaseSygus()->registerSygusType( tn ); + // ev is the first-order variable corresponding to this synth fun + std::stringstream ss; + ss << "f" << sf; + Node ev = NodeManager::currentNM()->mkBoundVar( ss.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 ) ); + qchildren.push_back( convertToEmbedding( q[1], synth_fun_vars, visited ) ); + if( q.getNumChildren()==3 ){ + qchildren.push_back( q[2] ); + } + q = NodeManager::currentNM()->mkNode( kind::FORALL, qchildren ); + Trace("cegqi") << "CegConjecture : converted to embedding : " << q << std::endl; + //register with single invocation if applicable if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) && options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){ d_ceg_si->initialize( q ); @@ -52,18 +176,44 @@ void CegConjecture::assign( Node q ) { //Node red_lem = NodeManager::currentNM()->mkNode( OR, q.negate(), d_cegqi_si->d_quant ); //may have rewritten quantified formula (for invariant synthesis) q = d_ceg_si->d_quant; + Assert( q.getKind()==kind::FORALL ); } } + d_quant = q; Assert( d_candidates.empty() ); std::vector< Node > vars; for( unsigned i=0; i<q[0].getNumChildren(); i++ ){ vars.push_back( q[0][i] ); - d_candidates.push_back( NodeManager::currentNM()->mkSkolem( "e", q[0][i].getType() ) ); + Node e = NodeManager::currentNM()->mkSkolem( "e", q[0][i].getType() ); + d_candidates.push_back( e ); } Trace("cegqi") << "Base quantified formula is : " << q << std::endl; //construct base instantiation d_base_inst = Rewriter::rewrite( d_qe->getInstantiation( q, vars, d_candidates ) ); + + // register this term with sygus database + std::vector< Node > guarded_lemmas; + if( !isSingleInvocation() ){ + if( options::sygusPbe() ){ + d_ceg_pbe->initialize( d_base_inst, d_candidates, guarded_lemmas ); + } + for( unsigned i=0; i<d_candidates.size(); i++ ){ + Node e = d_candidates[i]; + if( options::sygusPbe() ){ + std::vector< std::vector< Node > > exs; + std::vector< Node > exos; + std::vector< Node > exts; + // use the PBE examples, regardless of the search algorith, since these help search space pruning + if( d_ceg_pbe->getPbeExamples( e, exs, exos, exts ) ){ + d_qe->getTermDatabaseSygus()->registerPbeExamples( e, exs, exos, exts ); + } + }else{ + d_qe->getTermDatabaseSygus()->registerMeasuredTerm( e, e ); + } + } + } + Trace("cegqi") << "Base instantiation is : " << d_base_inst << std::endl; if( d_qe->getTermDatabase()->isQAttrSygus( d_assert_quant ) ){ CegInstantiation::collectDisjuncts( d_base_inst, d_base_disj ); @@ -80,269 +230,50 @@ void CegConjecture::assign( Node q ) { } } } - if( options::sygusUnifCondSol() ){ - // for each variable, determine whether we can do conditional counterexamples - for( unsigned i=0; i<d_candidates.size(); i++ ){ - registerCandidateConditional( d_candidates[i] ); - } - } d_syntax_guided = true; }else if( d_qe->getTermDatabase()->isQAttrSynthesis( d_assert_quant ) ){ d_syntax_guided = false; }else{ Assert( false ); } -} - -void CegConjecture::registerCandidateConditional( Node v ) { - TypeNode tn = v.getType(); - bool type_valid = false; - bool success = false; - std::vector< TypeNode > unif_types; - if( tn.isDatatype() ){ - const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype(); - if( dt.isSygus() ){ - type_valid = true; - if( d_candidates.size()==1 ){ // conditional solutions for multiple function conjectures TODO? - for( unsigned r=0; r<2; r++ ){ - for( unsigned j=0; j<dt.getNumConstructors(); j++ ){ - Node op = Node::fromExpr( dt[j].getSygusOp() ); - if( r==0 ){ - if( op.getKind() == kind::BUILTIN ){ - Kind sk = NodeManager::operatorToKind( op ); - if( sk==kind::ITE ){ - // we can do unification - success = true; - d_cinfo[v].d_csol_op = Node::fromExpr( dt[j].getConstructor() ); - Assert( dt[j].getNumArgs()==3 ); - for( unsigned k=0; k<3; k++ ){ - unif_types.push_back( TypeNode::fromType( dt[j][k].getRangeType() ) ); - } - break; - } - } - }else{ - if( dt[j].getNumArgs()>=3 ){ - // could be a defined ITE (this is a hack for ICFP benchmarks) - std::vector< Node > utchildren; - utchildren.push_back( Node::fromExpr( dt[j].getConstructor() ) ); - std::vector< Node > sks; - for( unsigned k=0; k<dt[j].getNumArgs(); k++ ){ - Type t = dt[j][k].getRangeType(); - Node kv = NodeManager::currentNM()->mkSkolem( "ut", TypeNode::fromType( t ) ); - sks.push_back( kv ); - utchildren.push_back( kv ); - } - Node ut = NodeManager::currentNM()->mkNode( kind::APPLY_CONSTRUCTOR, utchildren ); - std::vector< Node > echildren; - echildren.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); - echildren.push_back( ut ); - Node sbvl = Node::fromExpr( dt.getSygusVarList() ); - for( unsigned k=0; k<sbvl.getNumChildren(); k++ ){ - echildren.push_back( sbvl[k] ); - } - Node eut = NodeManager::currentNM()->mkNode( kind::APPLY_UF, echildren ); - Trace("sygus-unif-debug") << "Test evaluation of " << eut << "..." << std::endl; - eut = d_qe->getTermDatabaseSygus()->unfold( eut ); - Trace("sygus-unif-debug") << "...got " << eut << std::endl; - if( eut.getKind()==kind::ITE ){ - success = true; - std::vector< Node > vs; - std::vector< Node > ss; - std::map< Node, unsigned > templ_var_index; - for( unsigned k=0; k<sks.size(); k++ ){ - echildren[1] = sks[k]; - Node esk = NodeManager::currentNM()->mkNode( kind::APPLY_UF, echildren ); - vs.push_back( esk ); - Node tvar = NodeManager::currentNM()->mkSkolem( "templ", esk.getType() ); - templ_var_index[tvar] = k; - ss.push_back( tvar ); - } - eut = eut.substitute( vs.begin(), vs.end(), ss.begin(), ss.end() ); - Trace("sygus-unif") << "Defined constructor " << j << ", base term is " << eut << std::endl; - //success if we can find a injection from ITE args to sygus args - std::map< unsigned, unsigned > templ_injection; - for( unsigned k=0; k<3; k++ ){ - if( !inferIteTemplate( k, eut[k], templ_var_index, templ_injection ) ){ - Trace("sygus-unif") << "...failed to find injection (range)." << std::endl; - success = false; - break; - } - if( templ_injection.find( k )==templ_injection.end() ){ - Trace("sygus-unif") << "...failed to find injection (domain)." << std::endl; - success = false; - break; - } - } - if( success ){ - d_cinfo[v].d_csol_op = Node::fromExpr( dt[j].getConstructor() ); - for( unsigned k=0; k<3; k++ ){ - Assert( templ_injection.find( k )!=templ_injection.end() ); - unsigned sk_index = templ_injection[k]; - unif_types.push_back( sks[sk_index].getType() ); - //also store the template information, if necessary - Node teut = eut[k]; - if( !teut.isVar() ){ - d_cinfo[v].d_template[k] = teut; - d_cinfo[v].d_template_arg[k] = ss[sk_index]; - Trace("sygus-unif") << " Arg " << k << " : template : " << teut << ", arg " << ss[sk_index] << std::endl; - }else{ - Assert( teut==ss[sk_index] ); - } - } - } - } - } - } - } - if( success ){ - break; - } - } - } - } - } - //mark active - if( !success ){ - d_cinfo[v].d_csol_status = -1; - }else{ - //make progress guard - Node pg = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "P", NodeManager::currentNM()->booleanType(), "Progress guard for conditional solution." ) ); - Node pglem = NodeManager::currentNM()->mkNode( kind::OR, pg.negate(), pg ); - Trace("cegqi-lemma") << "Cegqi::Lemma : progress split : " << pglem << std::endl; - d_qe->getOutputChannel().lemma( pglem ); - d_qe->getOutputChannel().requirePhase( pg, true ); - - Assert( unif_types.size()==3 ); - d_cinfo[v].d_csol_cond = NodeManager::currentNM()->mkSkolem( "c", unif_types[0] ); - for( unsigned k=0; k<2; k++ ){ - d_cinfo[v].d_csol_var[k] = NodeManager::currentNM()->mkSkolem( "e", unif_types[k+1] ); - // optimization : need not be an ITE if types are equivalent TODO - } - d_cinfo[v].d_csol_progress_guard = pg; - Trace("sygus-unif") << "Can do synthesis unification for variable " << v << ", based on operator " << d_cinfo[v].d_csol_op << std::endl; - } - if( !type_valid ){ - Assert( false ); - } -} - -bool CegConjecture::inferIteTemplate( unsigned k, Node n, std::map< Node, unsigned >& templ_var_index, std::map< unsigned, unsigned >& templ_injection ){ - if( n.getNumChildren()==0 ){ - std::map< Node, unsigned >::iterator itt = templ_var_index.find( n ); - if( itt!=templ_var_index.end() ){ - unsigned kk = itt->second; - std::map< unsigned, unsigned >::iterator itti = templ_injection.find( k ); - if( itti==templ_injection.end() ){ - templ_injection[k] = kk; - }else if( itti->second!=kk ){ - return false; - } - } - return true; - }else{ - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - if( !inferIteTemplate( k, n[i], templ_var_index, templ_injection ) ){ - return false; - } - } - } - return true; -} - -void CegConjecture::initializeGuard(){ - if( isAssigned() ){ - if( !d_syntax_guided ){ - if( d_nsg_guard.isNull() ){ - d_nsg_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) ); - d_nsg_guard = d_qe->getValuation().ensureLiteral( d_nsg_guard ); - AlwaysAssert( !d_nsg_guard.isNull() ); - d_qe->getOutputChannel().requirePhase( d_nsg_guard, true ); - //add immediate lemma - Node lem = NodeManager::currentNM()->mkNode( OR, d_nsg_guard.negate(), d_base_inst.negate() ); - Trace("cegqi-lemma") << "Cegqi::Lemma : non-syntax-guided : " << lem << std::endl; - d_qe->getOutputChannel().lemma( lem ); - } - }else if( d_ceg_si->d_si_guard.isNull() ){ - std::vector< Node > lems; - d_ceg_si->getInitialSingleInvLemma( lems ); - for( unsigned i=0; i<lems.size(); i++ ){ - Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation " << i << " : " << lems[i] << std::endl; - d_qe->getOutputChannel().lemma( lems[i] ); - if( Trace.isOn("cegqi-debug") ){ - Node rlem = Rewriter::rewrite( lems[i] ); - Trace("cegqi-debug") << "...rewritten : " << rlem << std::endl; - } + + // initialize the guard + if( !d_syntax_guided ){ + if( d_nsg_guard.isNull() ){ + d_nsg_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) ); + d_nsg_guard = d_qe->getValuation().ensureLiteral( d_nsg_guard ); + AlwaysAssert( !d_nsg_guard.isNull() ); + d_qe->getOutputChannel().requirePhase( d_nsg_guard, true ); + // negated base as a guarded lemma + guarded_lemmas.push_back( d_base_inst.negate() ); + } + }else if( d_ceg_si->d_si_guard.isNull() ){ + std::vector< Node > lems; + d_ceg_si->getInitialSingleInvLemma( lems ); + for( unsigned i=0; i<lems.size(); i++ ){ + Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation " << i << " : " << lems[i] << std::endl; + d_qe->getOutputChannel().lemma( lems[i] ); + if( Trace.isOn("cegqi-debug") ){ + Node rlem = Rewriter::rewrite( lems[i] ); + Trace("cegqi-debug") << "...rewritten : " << rlem << std::endl; } } - Assert( !getGuard().isNull() ); } -} - -void CegConjecture::setMeasureTerm( Node mt ){ - d_measure_term = mt; - d_active_measure_term = mt; -} - -Node CegConjecture::getMeasureTermFactor( Node v ) { - Node ret; - if( getCegqiFairMode()==CEGQI_FAIR_DT_SIZE ){ - if( v.getType().isDatatype() ){ - ret = NodeManager::currentNM()->mkNode( DT_SIZE, v ); - } + Assert( !getGuard().isNull() ); + Node gneg = getGuard().negate(); + for( unsigned i=0; i<guarded_lemmas.size(); i++ ){ + Node lem = NodeManager::currentNM()->mkNode( OR, gneg, guarded_lemmas[i] ); + Trace("cegqi-lemma") << "Cegqi::Lemma : initial (guarded) lemma : " << lem << std::endl; + d_qe->getOutputChannel().lemma( lem ); } - //TODO - Assert( ret.isNull() || ret.getType().isInteger() ); - return ret; -} - -Node CegConjecture::getFairnessLiteral( int i ) { - if( d_measure_term.isNull() ){ - return Node::null(); - }else{ - std::map< int, Node >::iterator it = d_lits.find( i ); - if( it==d_lits.end() ){ - Trace("cegqi-engine") << "******* CEGQI : allocate size literal " << i << std::endl; - Node c = NodeManager::currentNM()->mkConst( Rational( i ) ); - Node lit = NodeManager::currentNM()->mkNode( LEQ, d_measure_term, c ); - lit = Rewriter::rewrite( lit ); - d_lits[i] = lit; - - Node lem = NodeManager::currentNM()->mkNode( kind::OR, lit, lit.negate() ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Fairness split : " << lem << std::endl; - d_qe->getOutputChannel().lemma( lem ); - d_qe->getOutputChannel().requirePhase( lit, true ); - - if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED || getCegqiFairMode()==CEGQI_FAIR_DT_SIZE_PRED ){ - //implies height bounds on each candidate variable - std::vector< Node > lem_c; - for( unsigned j=0; j<d_candidates.size(); j++ ){ - if( getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED ){ - lem_c.push_back( NodeManager::currentNM()->mkNode( DT_HEIGHT_BOUND, d_candidates[j], c ) ); - }else{ - //lem_c.push_back( NodeManager::currentNM()->mkNode( DT_SIZE_BOUND, d_candidates[j], c ) ); - } - } - Node hlem = NodeManager::currentNM()->mkNode( OR, lit.negate(), lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c ) ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Fairness expansion (pred) : " << hlem << std::endl; - d_qe->getOutputChannel().lemma( hlem ); - } - return lit; - }else{ - return it->second; - } - } + Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation() << std::endl; } Node CegConjecture::getGuard() { return !d_syntax_guided ? d_nsg_guard : d_ceg_si->d_si_guard; } -CegqiFairMode CegConjecture::getCegqiFairMode() { - return isSingleInvocation() ? CEGQI_FAIR_NONE : options::ceGuidedInstFair(); -} - bool CegConjecture::isSingleInvocation() const { return d_ceg_si->isSingleInvocation(); } @@ -410,99 +341,21 @@ bool CegConjecture::needsRefinement() { return !d_ce_sk.empty(); } -void CegConjecture::getConditionalCandidateList( std::vector< Node >& clist, Node curr, bool reqAdd ){ - Assert( options::sygusUnifCondSol() ); - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( curr ); - if( it!=d_cinfo.end() ){ - if( !it->second.d_csol_cond.isNull() ){ - if( it->second.d_csol_status!=-1 ){ - int pstatus = getProgressStatus( curr ); - if( pstatus!=-1 ){ - Assert( it->second.d_csol_status==0 || it->second.d_csol_status==1 ); - //interested in model value for condition and branched variables - clist.push_back( it->second.d_csol_cond ); - //assume_flat_ITEs - clist.push_back( it->second.d_csol_var[it->second.d_csol_status] ); - //conditionally get the other branch - getConditionalCandidateList( clist, it->second.d_csol_var[1-it->second.d_csol_status], false ); - return; - }else{ - // it is progress-inactive, will not be included - } - } - //otherwise, yet to expand branch - if( !reqAdd ){ - // if we are not top-level, we can ignore this (it won't be part of solution) - return; - } - }else{ - // a standard variable not handlable by unification - } - clist.push_back( curr ); - } -} - void CegConjecture::getCandidateList( std::vector< Node >& clist, bool forceOrig ) { - if( options::sygusUnifCondSol() && !forceOrig ){ - for( unsigned i=0; i<d_candidates.size(); i++ ){ - getConditionalCandidateList( clist, d_candidates[i], true ); - } + if( d_ceg_pbe->isPbe() && !forceOrig ){ + //Assert( isGround() ); + d_ceg_pbe->getCandidateList( d_candidates, clist ); }else{ clist.insert( clist.end(), d_candidates.begin(), d_candidates.end() ); } } -Node CegConjecture::constructConditionalCandidate( std::map< Node, Node >& cmv, Node curr ) { - Assert( options::sygusUnifCondSol() ); - std::map< Node, Node >::iterator itc = cmv.find( curr ); - if( itc!=cmv.end() ){ - return itc->second; - }else{ - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( curr ); - if( it!=d_cinfo.end() ){ - if( !it->second.d_csol_cond.isNull() ){ - if( it->second.d_csol_status!=-1 ){ - int pstatus = getProgressStatus( curr ); - if( pstatus!=-1 ){ - Assert( it->second.d_csol_status==0 || it->second.d_csol_status==1 ); - Node v_curr = constructConditionalCandidate( cmv, it->second.d_csol_var[it->second.d_csol_status] ); - Node v_next = constructConditionalCandidate( cmv, it->second.d_csol_var[1-it->second.d_csol_status] ); - if( v_next.isNull() ){ - // try taking current branch as a leaf - return v_curr; - }else{ - Node v_cond = constructConditionalCandidate( cmv, it->second.d_csol_cond ); - std::vector< Node > args; - args.push_back( it->second.d_csol_op ); - args.push_back( v_cond ); - args.push_back( it->second.d_csol_status==0 ? v_curr : v_next ); - args.push_back( it->second.d_csol_status==0 ? v_next : v_curr ); - return NodeManager::currentNM()->mkNode( kind::APPLY_CONSTRUCTOR, args ); - } - } - } - } - } - } - return Node::null(); -} - -bool CegConjecture::constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values, std::vector< Node >& candidate_values ) { +bool CegConjecture::constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values, std::vector< Node >& candidate_values, + std::vector< Node >& lems ) { Assert( clist.size()==model_values.size() ); - if( options::sygusUnifCondSol() ){ - std::map< Node, Node > cmv; - for( unsigned i=0; i<clist.size(); i++ ){ - cmv[ clist[i] ] = model_values[i]; - } - for( unsigned i=0; i<d_candidates.size(); i++ ){ - Node n = constructConditionalCandidate( cmv, d_candidates[i] ); - Trace("cegqi-candidate") << "...constructed conditional candidate " << n << " for " << d_candidates[i] << std::endl; - candidate_values.push_back( n ); - if( n.isNull() ){ - Assert( false ); //currently should never happen - return false; - } - } + if( d_ceg_pbe->isPbe() ){ + //Assert( isGround() ); + return d_ceg_pbe->constructCandidates( clist, model_values, d_candidates, candidate_values, lems ); }else{ Assert( model_values.size()==d_candidates.size() ); candidate_values.insert( candidate_values.end(), model_values.begin(), model_values.end() ); @@ -515,175 +368,73 @@ void CegConjecture::doCegConjectureCheck(std::vector< Node >& lems, std::vector< getCandidateList( clist ); std::vector< Node > c_model_values; Trace("cegqi-check") << "CegConjuncture : check, build candidates..." << std::endl; - if( constructCandidates( clist, model_values, c_model_values ) ){ - Assert( c_model_values.size()==d_candidates.size() ); + bool constructed_cand = constructCandidates( clist, model_values, c_model_values, lems ); + + //must get a counterexample to the value of the current candidate + Node inst; + if( constructed_cand ){ if( Trace.isOn("cegqi-check") ){ Trace("cegqi-check") << "CegConjuncture : check candidate : " << std::endl; for( unsigned i=0; i<c_model_values.size(); i++ ){ Trace("cegqi-check") << " " << i << " : " << d_candidates[i] << " -> " << c_model_values[i] << std::endl; } } - //must get a counterexample to the value of the current candidate - Node inst = d_base_inst.substitute( d_candidates.begin(), d_candidates.end(), c_model_values.begin(), c_model_values.end() ); - bool hasActiveConditionalNode = false; - if( options::sygusUnifCondSol() ){ - //TODO - hasActiveConditionalNode = true; - } - //check whether we will run CEGIS on inner skolem variables - bool sk_refine = ( !isGround() || d_refine_count==0 || hasActiveConditionalNode ); - if( sk_refine ){ - Assert( d_ce_sk.empty() ); - d_ce_sk.push_back( std::vector< Node >() ); + Assert( c_model_values.size()==d_candidates.size() ); + inst = d_base_inst.substitute( d_candidates.begin(), d_candidates.end(), c_model_values.begin(), c_model_values.end() ); + }else{ + inst = d_base_inst; + } + + //check whether we will run CEGIS on inner skolem variables + bool sk_refine = ( !isGround() || d_refine_count==0 ) && ( !d_ceg_pbe->isPbe() || constructed_cand ); + if( sk_refine ){ + Assert( d_ce_sk.empty() ); + d_ce_sk.push_back( std::vector< Node >() ); + }else{ + if( !constructed_cand ){ + return; } - std::vector< Node > ic; - ic.push_back( d_assert_quant.negate() ); - std::vector< Node > d; - CegInstantiation::collectDisjuncts( inst, d ); - Assert( d.size()==d_base_disj.size() ); - //immediately skolemize inner existentials - for( unsigned i=0; i<d.size(); i++ ){ - Node dr = Rewriter::rewrite( d[i] ); - if( dr.getKind()==NOT && dr[0].getKind()==FORALL ){ + } + + std::vector< Node > ic; + ic.push_back( d_assert_quant.negate() ); + std::vector< Node > d; + CegInstantiation::collectDisjuncts( inst, d ); + Assert( d.size()==d_base_disj.size() ); + //immediately skolemize inner existentials + for( unsigned i=0; i<d.size(); i++ ){ + Node dr = Rewriter::rewrite( d[i] ); + if( dr.getKind()==NOT && dr[0].getKind()==FORALL ){ + if( constructed_cand ){ ic.push_back( d_qe->getTermDatabase()->getSkolemizedBody( dr[0] ).negate() ); - if( sk_refine ){ - d_ce_sk.back().push_back( dr[0] ); - } - }else{ + } + if( sk_refine ){ + Assert( !isGround() ); + d_ce_sk.back().push_back( dr[0] ); + } + }else{ + if( constructed_cand ){ ic.push_back( dr ); - if( sk_refine ){ - d_ce_sk.back().push_back( Node::null() ); - } if( !d_inner_vars_disj[i].empty() ){ Trace("cegqi-debug") << "*** quantified disjunct : " << d[i] << " simplifies to " << dr << std::endl; } } + if( sk_refine ){ + d_ce_sk.back().push_back( Node::null() ); + } } + } + if( constructed_cand ){ Node lem = NodeManager::currentNM()->mkNode( OR, ic ); lem = Rewriter::rewrite( lem ); + //eagerly unfold applications of evaluation function + if( options::sygusDirectEval() ){ + Trace("cegqi-debug") << "pre-unfold counterexample : " << lem << std::endl; + std::map< Node, Node > visited_n; + lem = d_qe->getTermDatabaseSygus()->getEagerUnfold( lem, visited_n ); + } lems.push_back( lem ); recordInstantiation( c_model_values ); - }else{ - Assert( false ); - } -} - -Node CegConjecture::getActiveConditional( Node curr ) { - Assert( options::sygusUnifCondSol() ); - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( curr ); - Assert( it!=d_cinfo.end() ); - if( !it->second.d_csol_cond.isNull() ){ - if( it->second.d_csol_status==-1 ){ - //yet to branch, this is the one - return curr; - }else{ - int pstatus = getProgressStatus( curr ); - if( pstatus==-1 ){ - // it is progress-inactive - return curr; - }else{ - Assert( it->second.d_csol_status==0 || it->second.d_csol_status==1 ); - return getActiveConditional( it->second.d_csol_var[1-it->second.d_csol_status] ); - } - } - }else{ - //not a conditional - return curr; - } -} - -void CegConjecture::getContextConditionalNodes( Node curr, Node x, std::vector< Node >& nodes ) { - if( curr!=x ){ - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( curr ); - if( !it->second.d_csol_cond.isNull() ){ - if( it->second.d_csol_status!=-1 ){ - nodes.push_back( curr ); - getContextConditionalNodes( it->second.d_csol_var[1-it->second.d_csol_status], x, nodes ); - } - } - } -} - -Node CegConjecture::mkConditionalEvalNode( Node c, std::vector< Node >& args ) { - Assert( !c.isNull() ); - std::vector< Node > condc; - //get evaluator - Assert( c.getType().isDatatype() ); - const Datatype& cd = ((DatatypeType)c.getType().toType()).getDatatype(); - Assert( cd.isSygus() ); - condc.push_back( Node::fromExpr( cd.getSygusEvaluationFunc() ) ); - condc.push_back( c ); - for( unsigned a=0; a<args.size(); a++ ){ - condc.push_back( args[a] ); - } - return NodeManager::currentNM()->mkNode( kind::APPLY_UF, condc ); -} - -Node CegConjecture::mkConditionalNode( Node v, std::vector< Node >& args, unsigned eindex ) { - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( v ); - if( it!=d_cinfo.end() ){ - Assert( eindex<=2 ); - Node en = eindex==0 ? it->second.d_csol_cond : it->second.d_csol_var[eindex-1]; - if( !en.isNull() ){ - Node ret = mkConditionalEvalNode( en, args ); - //consider template - std::map< unsigned, Node >::iterator itt = it->second.d_template.find( eindex ); - if( itt!=it->second.d_template.end() ){ - Assert( it->second.d_template_arg.find( eindex )!=it->second.d_template_arg.end() ); - TNode var = it->second.d_template_arg[eindex]; - TNode subs = ret; - Node rret = itt->second.substitute( var, subs ); - ret = rret; - } - return ret; - } - } - Assert( false ); - return Node::null(); -} - -Node CegConjecture::mkConditional( Node v, std::vector< Node >& args, bool pol ) { - Node ret = mkConditionalNode( v, args, 0 ); - if( !pol ){ - ret = ret.negate(); - } - return ret; -} - -Node CegConjecture::purifyConditionalEvaluations( Node n, std::map< Node, Node >& csol_active, std::map< Node, Node >& psubs, std::map< Node, Node >& visited ){ - std::map< Node, Node >::iterator itv = visited.find( n ); - if( itv!=visited.end() ){ - return itv->second; - }else{ - Node ret; - if( n.getKind()==APPLY_UF ){ - std::map< Node, Node >::iterator itc = csol_active.find( n[0] ); - if( itc!=csol_active.end() ){ - //purify it with a variable - ret = NodeManager::currentNM()->mkSkolem( "y", n.getType(), "purification variable for sygus conditional solution" ); - psubs[n] = ret; - } - } - if( ret.isNull() ){ - ret = n; - if( n.getNumChildren()>0 ){ - std::vector< Node > children; - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - children.push_back( n.getOperator() ); - } - bool childChanged = false; - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node nc = purifyConditionalEvaluations( n[i], csol_active, psubs, visited ); - childChanged = childChanged || nc!=n[i]; - children.push_back( nc ); - } - if( childChanged ){ - ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); - } - } - } - visited[n] = ret; - return ret; } } @@ -717,119 +468,9 @@ void CegConjecture::doCegConjectureRefine( std::vector< Node >& lems ){ } } - std::map< Node, Node > csol_active; std::map< Node, std::vector< Node > > csol_ccond_nodes; std::map< Node, std::map< Node, bool > > csol_cpol; - if( options::sygusUnifCondSol() ){ - //previous non-ground conditional refinement lemmas must satisfy the current point - if( !isGround() ){ - Trace("cegqi-refine") << "doCegConjectureRefine : check for new refinements of previous lemmas..." << std::endl; - for( unsigned i=0; i<d_refinement_lemmas_ngr.size(); i++ ){ - Node prev_lem = d_refinement_lemmas_ngr[i]; - prev_lem = prev_lem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() ); - if( d_refinement_lemmas_reproc.find( prev_lem )==d_refinement_lemmas_reproc.end() ){ - d_refinement_lemmas_reproc[prev_lem] = true; - //do auxiliary variable substitution - std::vector< Node > subs; - for( unsigned ii=0; ii<d_refinement_lemmas_aux_vars[i].size(); ii++ ){ - subs.push_back( NodeManager::currentNM()->mkSkolem( "y", d_refinement_lemmas_aux_vars[i][ii].getType(), - "purification variable for non-ground sygus conditional solution" ) ); - } - prev_lem = prev_lem.substitute( d_refinement_lemmas_aux_vars[i].begin(), d_refinement_lemmas_aux_vars[i].end(), subs.begin(), subs.end() ); - prev_lem = Rewriter::rewrite( prev_lem ); - Trace("sygus-unif") << "...previous conditional refinement lemma with new counterexample : " << prev_lem << std::endl; - lems.push_back( prev_lem ); - } - } - if( !lems.empty() ){ - Trace("cegqi-refine") << "...added lemmas, abort further refinement." << std::endl; - d_ce_sk.clear(); - return; - } - } - Trace("cegqi-refine") << "doCegConjectureRefine : conditional solution refinement, expand active conditional nodes" << std::endl; - std::vector< Node > new_active_measure_sum; - for( unsigned i=0; i<d_candidates.size(); i++ ){ - Node v = d_candidates[i]; - Node ac = getActiveConditional( v ); - Assert( !ac.isNull() ); - //compute the contextual conditions - getContextConditionalNodes( v, ac, csol_ccond_nodes[v] ); - if( !csol_ccond_nodes[v].empty() ){ - //it will be conditionally evaluated, this is a placeholder - csol_active[v] = Node::null(); - } - Trace("sygus-unif") << "Active conditional for " << v << " is : " << ac << std::endl; - //if it is a conditional - bool is_active_conditional = false; - if( !d_cinfo[ac].d_csol_cond.isNull() ){ - int pstatus = getProgressStatus( ac ); - Assert( pstatus!=0 ); - if( pstatus==-1 ){ - //inject new progress point TODO? - Trace("sygus-unif") << "...progress status is " << pstatus << ", do not expand." << std::endl; - Assert( false ); - }else{ - is_active_conditional = true; - //expand this conditional - Trace("sygus-unif") << "****** For " << v << ", expanding an active conditional node : " << ac << std::endl; - d_cinfo[ac].d_csol_status = 0; //TODO: prefer some branches more than others based on the grammar? - Trace("sygus-unif") << "...expanded to " << d_cinfo[ac].d_csol_op << "( "; - Trace("sygus-unif") << d_cinfo[ac].d_csol_cond << ", " << d_cinfo[ac].d_csol_var[0] << ", "; - Trace("sygus-unif") << d_cinfo[ac].d_csol_var[1] << " )" << std::endl; - registerCandidateConditional( d_cinfo[ac].d_csol_var[1-d_cinfo[ac].d_csol_status] ); - //add to measure sum - Node acfc = getMeasureTermFactor( d_cinfo[ac].d_csol_cond ); - if( !acfc.isNull() ){ - new_active_measure_sum.push_back( acfc ); - } - Node acfv = getMeasureTermFactor( d_cinfo[ac].d_csol_var[d_cinfo[ac].d_csol_status] ); - if( !acfv.isNull() ){ - new_active_measure_sum.push_back( acfv ); - } - csol_active[v] = ac; - } - } - if( !is_active_conditional ){ - Trace("sygus-unif") << "* For " << v << ", its active node " << ac << " is not a conditional node." << std::endl; - //if we have not already included this in the measure, do so - if( d_cinfo[ac].d_csol_status==-1 ){ - Node acf = getMeasureTermFactor( ac ); - if( !acf.isNull() ){ - new_active_measure_sum.push_back( acf ); - } - d_cinfo[ac].d_csol_status = 2; - } - } - if( !csol_ccond_nodes[v].empty() ){ - Trace("sygus-unif") << "...it is nested under " << csol_ccond_nodes[v].size() << " other conditionals" << std::endl; - } - } - // must add to active measure - if( !new_active_measure_sum.empty() ){ - Node mcsum = new_active_measure_sum.size()==1 ? new_active_measure_sum[0] : NodeManager::currentNM()->mkNode( kind::PLUS, new_active_measure_sum ); - Node mclem = NodeManager::currentNM()->mkNode( kind::LEQ, mcsum, d_active_measure_term ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Measure component lemma : " << mclem << std::endl; - d_qe->getOutputChannel().lemma( mclem ); - /* - for( unsigned i=0; i<new_active_measure_sum.size(); i++ ){ - Node mclem = NodeManager::currentNM()->mkNode( kind::LEQ, new_active_measure_sum[i], d_active_measure_term ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Measure component lemma : " << mclem << std::endl; - d_qe->getOutputChannel().lemma( mclem ); - } - - Node new_active_measure = NodeManager::currentNM()->mkSkolem( "K", NodeManager::currentNM()->integerType() ); - new_active_measure_sum.push_back( new_active_measure ); - Node namlem = NodeManager::currentNM()->mkNode( kind::GEQ, new_active_measure, NodeManager::currentNM()->mkConst(Rational(0))); - Node ramlem = d_active_measure_term.eqNode( NodeManager::currentNM()->mkNode( kind::PLUS, new_active_measure_sum ) ); - namlem = NodeManager::currentNM()->mkNode( kind::AND, ramlem, namlem ); - Trace("cegqi-lemma") << "Cegqi::Lemma : Measure expansion : " << namlem << std::endl; - d_qe->getOutputChannel().lemma( namlem ); - d_active_measure_term = new_active_measure; - */ - } - } //for conditional evaluation std::map< Node, Node > psubs_visited; @@ -855,14 +496,7 @@ void CegConjecture::doCegConjectureRefine( std::vector< Node >& lems ){ } if( !c_disj.isNull() ){ //compute the body, inst_cond - if( options::sygusUnifCondSol() ){ - Trace("sygus-unif") << "Process " << c_disj << std::endl; - c_disj = purifyConditionalEvaluations( c_disj, csol_active, psubs, psubs_visited ); - Trace("sygus-unif") << "Purified to : " << c_disj << std::endl; - Trace("sygus-unif") << "...now with " << psubs.size() << " definitions." << std::endl; - }else{ - //standard CEGIS refinement : plug in values, assert that d_candidates must satisfy entire specification - } + //standard CEGIS refinement : plug in values, assert that d_candidates must satisfy entire specification lem_c.push_back( c_disj ); } } @@ -872,118 +506,10 @@ void CegConjecture::doCegConjectureRefine( std::vector< Node >& lems ){ std::vector< Node > psubs_cond_conc; std::map< Node, std::vector< Node > > psubs_apply; std::vector< Node > paux_vars; - if( options::sygusUnifCondSol() ){ - Trace("cegqi-refine") << "doCegConjectureRefine : add conditional assumptions for " << psubs.size() << " evaluations..." << std::endl; - for( std::map< Node, Node >::iterator itp = psubs.begin(); itp != psubs.end(); ++itp ){ - Assert( csol_active.find( itp->first[0] )!=csol_active.end() ); - paux_vars.push_back( itp->second ); - std::vector< Node > args; - for( unsigned a=1; a<itp->first.getNumChildren(); a++ ){ - args.push_back( itp->first[a] ); - } - Node ac = csol_active[itp->first[0]]; - Assert( d_cinfo.find( ac )!=d_cinfo.end() ); - Node c = d_cinfo[ac].d_csol_cond; - psubs_apply[ c ].push_back( itp->first ); - Trace("sygus-unif") << " process assumption " << itp->first << " == " << itp->second << ", with current condition " << c; - Trace("sygus-unif") << ", and " << csol_ccond_nodes[itp->first[0]].size() << " context conditionals." << std::endl; - std::vector< Node> assm; - if( !c.isNull() ){ - assm.push_back( mkConditional( ac, args, true ) ); - } - for( unsigned j=0; j<csol_ccond_nodes[itp->first[0]].size(); j++ ){ - Node acc = csol_ccond_nodes[itp->first[0]][j]; - bool pol = ( d_cinfo[acc].d_csol_status==1 ); - assm.push_back( mkConditional( acc, args, pol ) ); - } - Assert( !assm.empty() ); - Node c_ant = assm.size()==1 ? assm[0] : NodeManager::currentNM()->mkNode( kind::AND, assm ); - psubs_cond_ant.push_back( c_ant ); - // make the evaluation node - Node eret = mkConditionalNode( ac, args, d_cinfo[ac].d_csol_status+1 ); - Node c_conc = eret.eqNode( itp->second ); - psubs_cond_conc.push_back( c_conc ); - Trace("sygus-unif") << " ...made conditional correctness assumption : " << c_ant << " => " << c_conc << std::endl; - } - }else{ - Assert( psubs.empty() ); - } + Assert( psubs.empty() ); Node base_lem = lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c ); - if( options::sygusUnifCondSol() ){ - Trace("sygus-unif-debug") << "We have base lemma : " << base_lem << std::endl; - //progress lemmas - Trace("cegqi-refine") << "doCegConjectureRefine : add progress lemmas..." << std::endl; - std::map< Node, bool > cprocessed; - for( std::map< Node, Node >::iterator itc = csol_active.begin(); itc !=csol_active.end(); ++itc ){ - Node x = itc->first; - Node ac = itc->second; - Assert( d_cinfo.find( ac )!=d_cinfo.end() ); - Node c = d_cinfo[ac].d_csol_cond; - if( !c.isNull() ){ - Trace("sygus-unif") << " process conditional " << c << " for " << x << ", which was applied " << psubs_apply[c].size() << " times." << std::endl; - //make the progress point - Assert( x.getType().isDatatype() ); - const Datatype& dx = ((DatatypeType)x.getType().toType()).getDatatype(); - Node sbvl = Node::fromExpr( dx.getSygusVarList() ); - std::vector< Node > prgr_pt; - for( unsigned a=0; a<sbvl.getNumChildren(); a++ ){ - prgr_pt.push_back( NodeManager::currentNM()->mkSkolem( "kp", sbvl[a].getType(), "progress point for sygus conditional" ) ); - } - Node pdlem; - if( !psubs_apply[c].empty() ){ - std::vector< Node > prgr_domain_d; - for( unsigned j=0; j<psubs_apply[c].size(); j++ ){ - std::vector< Node > prgr_domain; - for( unsigned a=1; a<psubs_apply[c][j].getNumChildren(); a++ ){ - Assert( a<=prgr_pt.size() ); - prgr_domain.push_back( prgr_pt[a-1].eqNode( psubs_apply[c][j][a] ) ); - } - if( !prgr_domain.empty() ){ - //the point is in the domain of this function application - Node pdc = prgr_domain.size()==1 ? prgr_domain[0] : NodeManager::currentNM()->mkNode( AND, prgr_domain ); - prgr_domain_d.push_back( pdc ); - } - } - if( !prgr_domain_d.empty() ){ - //the progress point is in the domain of some function application - pdlem = prgr_domain_d.size()==1 ? prgr_domain_d[0] : NodeManager::currentNM()->mkNode( OR, prgr_domain_d ); - pdlem = pdlem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() ); - Trace("sygus-unif") << "Progress domain point lemma is " << pdlem << std::endl; - lems.push_back( pdlem ); - } - } - //the condition holds for the point, if this is an active condition - Node cplem = mkConditional( ac, prgr_pt ); - if( !csol_ccond_nodes[x].empty() ){ - std::vector< Node > prgr_conj; - prgr_conj.push_back( cplem ); - // ...and not for its context - for( unsigned j=0; j<csol_ccond_nodes[x].size(); j++ ){ - Node acc = csol_ccond_nodes[x][j]; - bool pol = ( d_cinfo[acc].d_csol_status==1 ); - prgr_conj.push_back( mkConditional( acc, prgr_pt, pol ) ); - } - cplem = NodeManager::currentNM()->mkNode( kind::AND, prgr_conj ); - } - Assert( !d_cinfo[x].d_csol_progress_guard.isNull() ); - cplem = NodeManager::currentNM()->mkNode( kind::OR, d_cinfo[x].d_csol_progress_guard.negate(), cplem ); - Trace("sygus-unif") << "Progress lemma is " << cplem << std::endl; - lems.push_back( cplem ); - } - } - /* - if( !prgr_conj.empty() ){ - Node prgr_lem = prgr_conj.size()==1 ? prgr_conj[0] : NodeManager::currentNM()->mkNode( kind::AND, prgr_conj ); - prgr_lem = prgr_lem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() ); - prgr_lem = NodeManager::currentNM()->mkNode( OR, getGuard().negate(), prgr_lem ); - Trace("sygus-unif") << "Progress lemma is " << prgr_lem << std::endl; - lems.push_back( prgr_lem ); - } - */ - } - Trace("cegqi-refine") << "doCegConjectureRefine : construct and finalize lemmas..." << std::endl; Node lem = base_lem; @@ -992,37 +518,8 @@ void CegConjecture::doCegConjectureRefine( std::vector< Node >& lems ){ base_lem = Rewriter::rewrite( base_lem ); d_refinement_lemmas_base.push_back( base_lem ); - if( options::sygusUnifCondSol() ){ - //add the conditional evaluation - if( !psubs_cond_ant.empty() ){ - std::vector< Node > children; - children.push_back( base_lem ); - std::vector< Node > pav; - std::vector< Node > pcv; - for( unsigned i=0; i<psubs_cond_ant.size(); i++ ){ - children.push_back( NodeManager::currentNM()->mkNode( kind::OR, psubs_cond_ant[i].negate(), psubs_cond_conc[i] ) ); - Node pa = psubs_cond_ant[i].substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() ); - pav.push_back( pa ); - Node pc = psubs_cond_conc[i].substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() ); - pcv.push_back( pc ); - } - d_refinement_lemmas_ceval_ant.push_back( pav ); - d_refinement_lemmas_ceval_conc.push_back( pcv ); - lem = NodeManager::currentNM()->mkNode( AND, children ); - } - } - lem = NodeManager::currentNM()->mkNode( OR, getGuard().negate(), lem ); - if( options::sygusUnifCondSol() ){ - if( !isGround() ){ - //store the non-ground version of the lemma - lem = Rewriter::rewrite( lem ); - d_refinement_lemmas_ngr.push_back( lem ); - d_refinement_lemmas_aux_vars.push_back( paux_vars ); - } - } - lem = lem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() ); lem = Rewriter::rewrite( lem ); d_refinement_lemmas.push_back( lem ); @@ -1071,62 +568,6 @@ void CegConjecture::debugPrint( const char * c ) { } } -int CegConjecture::getProgressStatus( Node v ) { - Assert( options::sygusUnifCondSol() ); - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( v ); - if( it!=d_cinfo.end() ){ - if( !it->second.d_csol_cond.isNull() ){ - if( it->second.d_csol_status!=-1 ){ - Node plit = it->second.d_csol_progress_guard; - Assert( !plit.isNull() ); - //check SAT value of plit - bool value; - if( d_qe->getValuation().hasSatValue( plit, value ) ) { - if( !value ){ - return -1; - }else{ - return 1; - } - }else{ - return 0; - } - } - } - } - return -2; -} - -Node CegConjecture::getNextDecisionRequestConditional( Node v, unsigned& priority ) { - Assert( options::sygusUnifCondSol() ); - int pstatus = getProgressStatus( v ); - if( pstatus>=0 ){ - std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( v ); - Assert( it!=d_cinfo.end() ); - if( pstatus==1 ){ - //active, recurse - Assert( it->second.d_csol_status==0 || it->second.d_csol_status==1 ); - return getNextDecisionRequestConditional( it->second.d_csol_var[1-it->second.d_csol_status], priority ); - }else if( pstatus==0 ){ - //needs decision - priority = 1; - return it->second.d_csol_progress_guard; - } - } - return Node::null(); -} - -Node CegConjecture::getNextDecisionRequest( unsigned& priority ) { - if( options::sygusUnifCondSol() ){ - for( unsigned i=0; i<d_candidates.size(); i++ ){ - Node lit = getNextDecisionRequestConditional( d_candidates[i], priority ); - if( !lit.isNull() ){ - return lit; - } - } - } - return Node::null(); -} - CegInstantiation::CegInstantiation( QuantifiersEngine * qe, context::Context* c ) : QuantifiersModule( qe ){ d_conj = new CegConjecture( qe, qe->getSatContext() ); d_last_inst_si = false; @@ -1174,6 +615,7 @@ void CegInstantiation::check( Theory::Effort e, unsigned quant_e ) { } void CegInstantiation::preRegisterQuantifier( Node q ) { +/* if( options::sygusDirectEval() ){ if( q.getNumChildren()==3 && q[2].getKind()==INST_PATTERN_LIST && q[2][0].getKind()==INST_PATTERN ){ //check whether it is an evaluation axiom @@ -1207,46 +649,15 @@ void CegInstantiation::preRegisterQuantifier( Node q ) { } } } - } + } + */ } void CegInstantiation::registerQuantifier( Node q ) { - if( d_quantEngine->getOwner( q )==this && d_eval_axioms.find( q )==d_eval_axioms.end() ){ + if( d_quantEngine->getOwner( q )==this ){ // && d_eval_axioms.find( q )==d_eval_axioms.end() ){ if( !d_conj->isAssigned() ){ Trace("cegqi") << "Register conjecture : " << q << std::endl; d_conj->assign( q ); - - //fairness - if( d_conj->getCegqiFairMode()!=CEGQI_FAIR_NONE ){ - std::vector< Node > mc; - if( options::sygusUnifCondSol() || - d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_HEIGHT_PRED || d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_SIZE_PRED ){ - //measure term is a fresh constant - mc.push_back( NodeManager::currentNM()->mkSkolem( "K", NodeManager::currentNM()->integerType() ) ); - }else{ - std::vector< Node > clist; - d_conj->getCandidateList( clist, true ); - for( unsigned j=0; j<clist.size(); j++ ){ - TypeNode tn = clist[j].getType(); - if( d_conj->getCegqiFairMode()==CEGQI_FAIR_DT_SIZE ){ - if( tn.isDatatype() ){ - mc.push_back( NodeManager::currentNM()->mkNode( DT_SIZE, clist[j] ) ); - } - }else if( d_conj->getCegqiFairMode()==CEGQI_FAIR_UF_DT_SIZE ){ - registerMeasuredType( tn ); - std::map< TypeNode, Node >::iterator it = d_uf_measure.find( tn ); - if( it!=d_uf_measure.end() ){ - mc.push_back( NodeManager::currentNM()->mkNode( APPLY_UF, it->second, clist[j] ) ); - } - } - } - } - if( !mc.empty() ){ - Node mt = mc.size()==1 ? mc[0] : NodeManager::currentNM()->mkNode( PLUS, mc ); - Trace("cegqi") << "Measure term is : " << mt << std::endl; - d_conj->setMeasureTerm( mt ); - } - } }else{ Assert( d_conj->d_quant==q ); } @@ -1259,10 +670,7 @@ void CegInstantiation::assertNode( Node n ) { } Node CegInstantiation::getNextDecisionRequest( unsigned& priority ) { - //enforce fairness if( d_conj->isAssigned() ){ - d_conj->initializeGuard(); - // std::vector< Node > req_dec; const CegConjectureSingleInv* ceg_si = d_conj->getCegConjectureSingleInv(); if( ! ceg_si->d_full_guard.isNull() ){ @@ -1283,32 +691,7 @@ Node CegInstantiation::getNextDecisionRequest( unsigned& priority ) { Trace("cegqi-debug2") << "CEGQI : " << req_dec[i] << " already has value " << value << std::endl; } } - - //ask the conjecture directly - Node lit = d_conj->getNextDecisionRequest( priority ); - if( !lit.isNull() ){ - return lit; - } - - if( d_conj->getCegqiFairMode()!=CEGQI_FAIR_NONE ){ - Node lit = d_conj->getFairnessLiteral( d_conj->getCurrentTermSize() ); - bool value; - if( d_quantEngine->getValuation().hasSatValue( lit, value ) ) { - if( !value ){ - d_conj->incrementCurrentTermSize(); - lit = d_conj->getFairnessLiteral( d_conj->getCurrentTermSize() ); - Trace("cegqi-debug") << "CEGQI : Decide on next lit : " << lit << "..." << std::endl; - priority = 1; - return lit; - } - }else{ - Trace("cegqi-debug") << "CEGQI : Decide on current lit : " << lit << "..." << std::endl; - priority = 1; - return lit; - } - } } - return Node::null(); } @@ -1319,11 +702,9 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { conj->debugPrint("cegqi-engine-debug"); Trace("cegqi-engine-debug") << std::endl; } - if( conj->getCegqiFairMode()!=CEGQI_FAIR_NONE ){ - Trace("cegqi-engine") << " * Current term size : " << conj->getCurrentTermSize() << std::endl; - } if( !conj->needsRefinement() ){ + Trace("cegqi-engine-debug") << "Do conjecture check..." << std::endl; if( conj->d_syntax_guided ){ std::vector< Node > clems; conj->doCegConjectureSingleInvCheck( clems ); @@ -1345,25 +726,24 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { if( options::sygusDirectEval() ){ bool addedEvalLemmas = false; if( options::sygusCRefEval() ){ - Trace("cegqi-debug") << "Do cref evaluation..." << std::endl; + Trace("cegqi-engine") << " *** Do conjecture refinement evaluation..." << std::endl; // see if any refinement lemma is refuted by evaluation std::vector< Node > cre_lems; getCRefEvaluationLemmas( conj, clist, model_values, cre_lems ); if( !cre_lems.empty() ){ - Trace("cegqi-engine") << " *** Do conjecture refinement evaluation..." << std::endl; for( unsigned j=0; j<cre_lems.size(); j++ ){ Node lem = cre_lems[j]; - Trace("cegqi-lemma") << "Cegqi::Lemma : cref evaluation : " << lem << std::endl; if( d_quantEngine->addLemma( lem ) ){ + Trace("cegqi-lemma") << "Cegqi::Lemma : cref evaluation : " << lem << std::endl; addedEvalLemmas = true; } } if( addedEvalLemmas ){ - return; + //return; } } } - Trace("cegqi-debug") << "Do direct evaluation..." << std::endl; + Trace("cegqi-engine") << " *** Do direct evaluation..." << std::endl; std::vector< Node > eager_terms; std::vector< Node > eager_vals; std::vector< Node > eager_exps; @@ -1373,15 +753,14 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { } Trace("cegqi-debug") << "...produced " << eager_terms.size() << " eager evaluation lemmas." << std::endl; if( !eager_terms.empty() ){ - Trace("cegqi-engine") << " *** Do direct evaluation..." << std::endl; for( unsigned j=0; j<eager_terms.size(); j++ ){ Node lem = NodeManager::currentNM()->mkNode( kind::OR, eager_exps[j].negate(), eager_terms[j].eqNode( eager_vals[j] ) ); if( d_quantEngine->getTheoryEngine()->isTheoryEnabled(THEORY_BV) ){ //FIXME: hack to incorporate hacks from BV for division by zero lem = bv::TheoryBVRewriter::eliminateBVSDiv( lem ); } - Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl; if( d_quantEngine->addLemma( lem ) ){ + Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl; addedEvalLemmas = true; } } @@ -1390,25 +769,6 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { return; } } - //check if we must apply fairness lemmas - if( conj->getCegqiFairMode()==CEGQI_FAIR_UF_DT_SIZE ){ - Trace("cegqi-debug") << "Get measure lemmas..." << std::endl; - std::vector< Node > lems; - for( unsigned j=0; j<clist.size(); j++ ){ - Trace("cegqi-debug") << " get measure lemmas for " << clist[j] << " -> " << model_values[j] << std::endl; - getMeasureLemmas( clist[j], model_values[j], lems ); - } - Trace("cegqi-debug") << "...produced " << lems.size() << " measure lemmas." << std::endl; - if( !lems.empty() ){ - Trace("cegqi-engine") << " *** Do measure refinement..." << std::endl; - for( unsigned j=0; j<lems.size(); j++ ){ - Trace("cegqi-lemma") << "Cegqi::Lemma : measure : " << lems[j] << std::endl; - d_quantEngine->addLemma( lems[j] ); - } - Trace("cegqi-engine") << " ...refine size." << std::endl; - return; - } - } Trace("cegqi-engine") << " *** Check candidate phase..." << std::endl; std::vector< Node > cclems; @@ -1417,12 +777,6 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { for( unsigned i=0; i<cclems.size(); i++ ){ Node lem = cclems[i]; d_last_inst_si = false; - //eagerly unfold applications of evaluation function - if( options::sygusDirectEval() ){ - Trace("cegqi-eager") << "pre-unfold counterexample : " << lem << std::endl; - std::map< Node, Node > visited_n; - lem = getEagerUnfold( lem, visited_n ); - } Trace("cegqi-lemma") << "Cegqi::Lemma : counterexample : " << lem << std::endl; if( d_quantEngine->addLemma( lem ) ){ ++(d_statistics.d_cegqi_lemmas_ce); @@ -1482,308 +836,68 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) { } void CegInstantiation::getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems ) { + Trace("sygus-cref-eval") << "Cref eval : conjecture has " << conj->getNumRefinementLemmas() << " refinement lemmas." << std::endl; if( conj->getNumRefinementLemmas()>0 ){ Assert( vs.size()==ms.size() ); - std::map< Node, Node > vtm; - for( unsigned i=0; i<vs.size(); i++ ){ - vtm[vs[i]] = ms[i]; - } - /* - if( options::sygusUnifCondSol() ){ - // first, check progress lemmas TODO? - for( unsigned i=0; i<conj->getNumRefinementLemmas(); i++ ){ - Node plem = conj->getConditionalProgressLemma( i ); - std::vector< Node > pp; - conj-> - std::map< Node, Node > visitedp; - std::map< Node, std::vector< Node > > expp; - conj->getModelValues - } - } - */ + + Node neg_guard = conj->getGuard().negate(); for( unsigned i=0; i<conj->getNumRefinementLemmas(); i++ ){ Node lem; std::map< Node, Node > visited; std::map< Node, std::vector< Node > > exp; - if( options::sygusUnifCondSol() ){ - for( unsigned j=0; j<conj->getNumConditionalEvaluations( i ); j++ ){ - std::map< Node, Node > visitedc; - std::map< Node, std::vector< Node > > expc; - Node ce = conj->getConditionalEvaluationAntec( i, j ); - Node cee = crefEvaluate( ce, vtm, visitedc, expc ); - Trace("sygus-cref-eval") << "Check conditional evaluation condition : " << ce << ", evaluates to " << cee << std::endl; - if( !cee.isNull() && cee==d_quantEngine->getTermDatabase()->d_true ){ - Node conc = conj->getConditionalEvaluationConc( i, j ); - // the conditional holds, we will apply this as a substitution - for( unsigned r=0; r<2; r++ ){ - if( conc[r].isVar() ){ - Node v = conc[r]; - Node c = conc[1-r]; - Assert( exp.find( v )==exp.end() ); - visited[v] = c; - //exp[v].insert( exp[v].end(), expc[ce].begin(), expc[ce].end() ); - exp[v].push_back( ce ); - Trace("sygus-cref-eval") << " consider " << v << " -> " << c << " with expanation " << ce << std::endl; - break; - } - } - } - } - //if at least one conditional fires - if( !visited.empty() ){ - lem = conj->getRefinementBaseLemma( i ); - } - }else{ - lem = conj->getRefinementBaseLemma( i ); - } + lem = conj->getRefinementBaseLemma( i ); if( !lem.isNull() ){ - Trace("sygus-cref-eval") << "Check refinement lemma " << lem << " against current model." << std::endl; - Node elem = crefEvaluate( lem, vtm, visited, exp ); - Trace("sygus-cref-eval") << "...evaluated to " << elem << ", exp size = " << exp[lem].size() << std::endl; - if( !elem.isNull() && elem==d_quantEngine->getTermDatabase()->d_false ){ - elem = conj->getGuard().negate(); - Node cre_lem; - if( !exp[lem].empty() ){ - Node en = exp[lem].size()==1 ? exp[lem][0] : NodeManager::currentNM()->mkNode( kind::AND, exp[lem] ); - cre_lem = NodeManager::currentNM()->mkNode( kind::OR, en.negate(), elem ); - }else{ - cre_lem = elem; - } - if( std::find( lems.begin(), lems.end(), cre_lem )==lems.end() ){ - Trace("sygus-cref-eval") << "...produced lemma : " << cre_lem << std::endl; - lems.push_back( cre_lem ); + std::vector< Node > lem_conj; + //break into conjunctions + if( lem.getKind()==kind::AND ){ + for( unsigned i=0; i<lem.getNumChildren(); i++ ){ + lem_conj.push_back( lem[i] ); } + }else{ + lem_conj.push_back( lem ); } - } - } - } -} - -Node CegInstantiation::crefEvaluate( Node n, std::map< Node, Node >& vtm, std::map< Node, Node >& visited, std::map< Node, std::vector< Node > >& exp ){ - std::map< Node, Node >::iterator itv = visited.find( n ); - Node ret; - std::vector< Node > exp_c; - if( itv!=visited.end() ){ - if( !itv->second.isConst() ){ - //we stored a partially evaluated node, actually evaluate the result now - ret = crefEvaluate( itv->second, vtm, visited, exp ); - exp_c.push_back( itv->second ); - }else{ - return itv->second; - } - }else{ - if( n.getKind()==kind::APPLY_UF ){ - //it is an evaluation function - Trace("sygus-cref-eval-debug") << "Compute evaluation for : " << n << std::endl; - //unfold by one step - Node nn = d_quantEngine->getTermDatabaseSygus()->unfold( n, vtm, exp[n] ); - Trace("sygus-cref-eval-debug") << "...unfolded once to " << nn << std::endl; - Assert( nn!=n ); - //it is the result of evaluating the unfolding - ret = crefEvaluate( nn, vtm, visited, exp ); - //carry explanation - exp_c.push_back( nn ); - } - if( ret.isNull() ){ - if( n.getNumChildren()>0 ){ - std::vector< Node > children; - bool childChanged = false; - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - children.push_back( n.getOperator() ); - } - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node nc = crefEvaluate( n[i], vtm, visited, exp ); - childChanged = nc!=n[i] || childChanged; - children.push_back( nc ); - //Boolean short circuiting - if( n.getKind()==kind::AND ){ - if( nc==d_quantEngine->getTermDatabase()->d_false ){ - ret = nc; - exp_c.clear(); - } - }else if( n.getKind()==kind::OR ){ - if( nc==d_quantEngine->getTermDatabase()->d_true ){ - ret = nc; - exp_c.clear(); - } - }else if( n.getKind()==kind::ITE && i==0 ){ - int index = -1; - if( nc==d_quantEngine->getTermDatabase()->d_true ){ - index = 1; - }else if( nc==d_quantEngine->getTermDatabase()->d_false ){ - index = 2; + EvalSygusInvarianceTest vsit; + vsit.d_result = d_quantEngine->getTermDatabase()->d_false; + for( unsigned j=0; j<lem_conj.size(); j++ ){ + Node lemc = lem_conj[j]; + Trace("sygus-cref-eval") << "Check refinement lemma conjunct " << lemc << " against current model." << std::endl; + Trace("sygus-cref-eval2") << "Check refinement lemma conjunct " << lemc << " against current model." << std::endl; + Node cre_lem; + Node lemcs = lemc.substitute( vs.begin(), vs.end(), ms.begin(), ms.end() ); + Trace("sygus-cref-eval2") << "...under substitution it is : " << lemcs << std::endl; + Node lemcsu = d_quantEngine->getTermDatabaseSygus()->evaluateWithUnfolding( lemcs, vsit.d_visited ); + Trace("sygus-cref-eval2") << "...after unfolding is : " << lemcsu << std::endl; + if( lemcsu==d_quantEngine->getTermDatabase()->d_false ){ + std::vector< Node > msu; + std::vector< Node > mexp; + msu.insert( msu.end(), ms.begin(), ms.end() ); + for( unsigned k=0; k<vs.size(); k++ ){ + vsit.d_var = vs[k]; + vsit.d_update_nvn = msu[k]; + msu[k] = vs[k]; + // substitute for everything except this + vsit.d_conj = lemc.substitute( vs.begin(), vs.end(), msu.begin(), msu.end() ); + // get minimal explanation for this + Trace("sygus-cref-eval2-debug") << " compute min explain of : " << vs[k] << " = " << vsit.d_update_nvn << std::endl; + d_quantEngine->getTermDatabaseSygus()->getExplanationFor( vs[k], vsit.d_update_nvn, mexp, vsit ); + msu[k] = vsit.d_update_nvn; } - if( index!=-1 ){ - ret = crefEvaluate( n[index], vtm, visited, exp ); - exp_c.push_back( n[index] ); + if( !mexp.empty() ){ + Node en = mexp.size()==1 ? mexp[0] : NodeManager::currentNM()->mkNode( kind::AND, mexp ); + cre_lem = NodeManager::currentNM()->mkNode( kind::OR, en.negate(), neg_guard ); + }else{ + cre_lem = neg_guard; } } - //carry explanation - exp_c.push_back( n[i] ); - if( !ret.isNull() ){ - break; - } - } - if( ret.isNull() ){ - if( childChanged ){ - ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); - ret = Rewriter::rewrite( ret ); - }else{ - ret = n; - } - } - }else{ - ret = n; - } - } - } - //carry explanation from children - for( unsigned i=0; i<exp_c.size(); i++ ){ - Node nn = exp_c[i]; - std::map< Node, std::vector< Node > >::iterator itx = exp.find( nn ); - if( itx!=exp.end() ){ - for( unsigned j=0; j<itx->second.size(); j++ ){ - if( std::find( exp[n].begin(), exp[n].end(), itx->second[j] )==exp[n].end() ){ - exp[n].push_back( itx->second[j] ); - } - } - } - } - Trace("sygus-cref-eval-debug") << "... evaluation of " << n << " is (" << ret.getKind() << ") " << ret << std::endl; - Trace("sygus-cref-eval-debug") << "...... exp size = " << exp[n].size() << std::endl; - Assert( ret.isNull() || ret.isConst() ); - visited[n] = ret; - return ret; -} - -void CegInstantiation::registerMeasuredType( TypeNode tn ) { - std::map< TypeNode, Node >::iterator it = d_uf_measure.find( tn ); - if( it==d_uf_measure.end() ){ - if( tn.isDatatype() ){ - TypeNode op_tn = NodeManager::currentNM()->mkFunctionType( tn, NodeManager::currentNM()->integerType() ); - Node op = NodeManager::currentNM()->mkSkolem( "tsize", op_tn, "was created by ceg instantiation to enforce fairness." ); - d_uf_measure[tn] = op; - } - } -} - -Node CegInstantiation::getSizeTerm( Node n, TypeNode tn, std::vector< Node >& lems ) { - std::map< Node, Node >::iterator itt = d_size_term.find( n ); - if( itt==d_size_term.end() ){ - registerMeasuredType( tn ); - Node sn = NodeManager::currentNM()->mkNode( APPLY_UF, d_uf_measure[tn], n ); - lems.push_back( NodeManager::currentNM()->mkNode( LEQ, NodeManager::currentNM()->mkConst( Rational(0) ), sn ) ); - d_size_term[n] = sn; - return sn; - }else{ - return itt->second; - } -} - -void CegInstantiation::getMeasureLemmas( Node n, Node v, std::vector< Node >& lems ) { - Trace("cegqi-lemma-debug") << "Get measure lemma " << n << " " << v << std::endl; - Assert( n.getType()==v.getType() ); - TypeNode tn = n.getType(); - if( tn.isDatatype() ){ - Assert( v.getKind()==APPLY_CONSTRUCTOR ); - const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - int index = Datatype::indexOf( v.getOperator().toExpr() ); - std::map< int, Node >::iterator it = d_size_term_lemma[n].find( index ); - if( it==d_size_term_lemma[n].end() ){ - Node lhs = getSizeTerm( n, tn, lems ); - //add measure lemma - std::vector< Node > sumc; - for( unsigned j=0; j<dt[index].getNumArgs(); j++ ){ - TypeNode tnc = v[j].getType(); - if( tnc.isDatatype() ){ - Node seln = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index][j].getSelector() ), n ); - sumc.push_back( getSizeTerm( seln, tnc, lems ) ); - } - } - Node rhs; - if( !sumc.empty() ){ - sumc.push_back( NodeManager::currentNM()->mkConst( Rational(1) ) ); - rhs = NodeManager::currentNM()->mkNode( PLUS, sumc ); - }else{ - rhs = NodeManager::currentNM()->mkConst( Rational(0) ); - } - Node lem = lhs.eqNode( rhs ); - Node cond = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[index].getTester() ), n ); - lem = NodeManager::currentNM()->mkNode( OR, cond.negate(), lem ); - - d_size_term_lemma[n][index] = lem; - Trace("cegqi-lemma-debug") << "...constructed lemma " << lem << std::endl; - lems.push_back( lem ); - //return; - } - //get lemmas for children - for( unsigned i=0; i<v.getNumChildren(); i++ ){ - Node nn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[index][i].getSelector() ), n ); - getMeasureLemmas( nn, v[i], lems ); - } - - } -} - -Node CegInstantiation::getEagerUnfold( Node n, std::map< Node, Node >& visited ) { - std::map< Node, Node >::iterator itv = visited.find( n ); - if( itv==visited.end() ){ - Trace("cegqi-eager-debug") << "getEagerUnfold " << n << std::endl; - Node ret; - if( n.getKind()==APPLY_UF ){ - TypeNode tn = n[0].getType(); - Trace("cegqi-eager-debug") << "check " << n[0].getType() << std::endl; - if( tn.isDatatype() ){ - const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - if( dt.isSygus() ){ - Trace("cegqi-eager") << "Unfold eager : " << n << std::endl; - Node bTerm = d_quantEngine->getTermDatabaseSygus()->sygusToBuiltin( n[0], tn ); - Trace("cegqi-eager") << "Built-in term : " << bTerm << std::endl; - std::vector< Node > vars; - std::vector< Node > subs; - Node var_list = Node::fromExpr( dt.getSygusVarList() ); - Assert( var_list.getNumChildren()+1==n.getNumChildren() ); - for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ - vars.push_back( var_list[j] ); - } - for( unsigned j=1; j<n.getNumChildren(); j++ ){ - Node nc = getEagerUnfold( n[j], visited ); - subs.push_back( nc ); - Assert( subs[j-1].getType()==var_list[j-1].getType() ); - } - Assert( vars.size()==subs.size() ); - bTerm = bTerm.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); - Trace("cegqi-eager") << "Built-in term after subs : " << bTerm << std::endl; - Trace("cegqi-eager-debug") << "Types : " << bTerm.getType() << " " << n.getType() << std::endl; - Assert( n.getType()==bTerm.getType() ); - ret = bTerm; - } - } - } - if( ret.isNull() ){ - if( n.getKind()!=FORALL ){ - bool childChanged = false; - std::vector< Node > children; - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node nc = getEagerUnfold( n[i], visited ); - childChanged = childChanged || n[i]!=nc; - children.push_back( nc ); - } - if( childChanged ){ - if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ - children.insert( children.begin(), n.getOperator() ); + if( !cre_lem.isNull() ){ + if( std::find( lems.begin(), lems.end(), cre_lem )==lems.end() ){ + Trace("sygus-cref-eval") << "...produced lemma : " << cre_lem << std::endl; + lems.push_back( cre_lem ); + } } - ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); } } - if( ret.isNull() ){ - ret = n; - } } - visited[n] = ret; - return ret; - }else{ - return itv->second; } } @@ -1826,8 +940,10 @@ void CegInstantiation::printSynthSolution( std::ostream& out ) { for( unsigned j=0; j<svl.getNumChildren(); j++ ){ subs.push_back( Node::fromExpr( svl[j] ) ); } - bool templIsPost = false; - Node templ; + //bool templIsPost = false; + const CegConjectureSingleInv* ceg_si = d_conj->getCegConjectureSingleInv(); + Node templ = ceg_si->getTemplate( prog ); + /* if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ const CegConjectureSingleInv* ceg_si = d_conj->getCegConjectureSingleInv(); if(ceg_si->d_trans_pre.find( prog ) != ceg_si->d_trans_pre.end()){ @@ -1841,8 +957,11 @@ void CegInstantiation::printSynthSolution( std::ostream& out ) { templIsPost = true; } } + */ Trace("cegqi-inv") << "Template is " << templ << std::endl; if( !templ.isNull() ){ + TNode templa = ceg_si->getTemplateArg( prog ); + Assert( !templa.isNull() ); std::vector<Node>& templ_vars = d_conj->getProgTempVars(prog); std::vector< Node > vars; vars.insert( vars.end(), templ_vars.begin(), templ_vars.end() ); @@ -1860,7 +979,9 @@ void CegInstantiation::printSynthSolution( std::ostream& out ) { Trace("cegqi-inv") << "Builtin version of solution is : " << sol << ", type : " << sol.getType() << std::endl; - sol = NodeManager::currentNM()->mkNode( templIsPost ? AND : OR, sol, templ ); + //sol = NodeManager::currentNM()->mkNode( templIsPost ? AND : OR, sol, templ ); + TNode tsol = sol; + sol = templ.substitute( templa, tsol ); } if( sol==sygus_sol ){ sol = sygus_sol; diff --git a/src/theory/quantifiers/ce_guided_instantiation.h b/src/theory/quantifiers/ce_guided_instantiation.h index 56a71e43e..c5c865ff9 100644 --- a/src/theory/quantifiers/ce_guided_instantiation.h +++ b/src/theory/quantifiers/ce_guided_instantiation.h @@ -20,7 +20,9 @@ #include "context/cdchunk_list.h" #include "context/cdhashmap.h" #include "options/quantifiers_modes.h" +#include "options/datatypes_modes.h" #include "theory/quantifiers/ce_guided_single_inv.h" +#include "theory/quantifiers/ce_guided_pbe.h" #include "theory/quantifiers_engine.h" namespace CVC4 { @@ -42,36 +44,13 @@ private: std::vector< std::vector< Node > > d_inner_vars_disj; /** current extential quantifeirs whose couterexamples we must refine */ std::vector< std::vector< Node > > d_ce_sk; - /** the cardinality literals */ - std::map< int, Node > d_lits; - /** current cardinality */ - context::CDO< int > d_curr_lit; - /** active measure term */ - Node d_active_measure_term; /** refinement lemmas */ std::vector< Node > d_refinement_lemmas; std::vector< Node > d_refinement_lemmas_base; - //std::vector< Node > d_refinement_lemmas_cprogress; - //std::vector< std::vector< Node > > d_refinement_lemmas_cprogress_pts; -private: //for condition solutions - std::vector< std::vector< Node > > d_refinement_lemmas_aux_vars; - std::vector< std::vector< Node > > d_refinement_lemmas_ceval_ant; - std::vector< std::vector< Node > > d_refinement_lemmas_ceval_conc; - std::vector< Node > d_refinement_lemmas_ngr; //non-ground version - std::map< Node, bool > d_refinement_lemmas_reproc; - /** get candidate list recursively for conditional solutions */ - void getConditionalCandidateList( std::vector< Node >& clist, Node curr, bool reqAdd ); - Node constructConditionalCandidate( std::map< Node, Node >& cmv, Node curr ); - Node getActiveConditional( Node curr ); - void getContextConditionalNodes( Node curr, Node x, std::vector< Node >& nodes ); - Node mkConditionalEvalNode( Node c, std::vector< Node >& args ); - Node mkConditionalNode( Node v, std::vector< Node >& args, unsigned eindex ); - Node mkConditional( Node v, std::vector< Node >& args, bool pol = true ); - Node purifyConditionalEvaluations( Node n, std::map< Node, Node >& csol_cond, std::map< Node, Node >& psubs, - std::map< Node, Node >& visited ); - /** register candidate conditional */ - void registerCandidateConditional( Node v ); - bool inferIteTemplate( unsigned k, Node n, std::map< Node, unsigned >& templ_var_index, std::map< unsigned, unsigned >& templ_injection ); + /** get embedding */ + Node convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars, std::map< Node, Node >& visited ); + /** collect constants */ + void collectConstants( Node n, std::map< TypeNode, std::vector< Node > >& consts, std::map< Node, bool >& visited ); public: CegConjecture( QuantifiersEngine * qe, context::Context* c ); ~CegConjecture(); @@ -83,25 +62,12 @@ public: class CandidateInfo { public: - CandidateInfo() : d_csol_status(-1){} + CandidateInfo(){} /** list of terms we have instantiated candidates with */ std::vector< Node > d_inst; - /** conditional solutions */ - Node d_csol_op; - Node d_csol_cond; - Node d_csol_var[2]; - /** progress guard */ - Node d_csol_progress_guard; - /** solution status */ - int d_csol_status; - /** required for template solutions */ - std::map< unsigned, Node > d_template; - std::map< unsigned, Node > d_template_arg; }; std::map< Node, CandidateInfo > d_cinfo; - /** measure term */ - Node d_measure_term; /** measure sum size */ int d_measure_term_size; /** refine count */ @@ -113,7 +79,8 @@ public: bool needsRefinement(); void getCandidateList( std::vector< Node >& clist, bool forceOrig = false ); - bool constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values, std::vector< Node >& candidate_values ); + bool constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values, std::vector< Node >& candidate_values, + std::vector< Node >& lems ); void doCegConjectureSingleInvCheck(std::vector< Node >& lems); void doCegConjectureCheck(std::vector< Node >& lems, std::vector< Node >& model_values); @@ -146,28 +113,19 @@ public: private: /** single invocation utility */ CegConjectureSingleInv * d_ceg_si; + /** program by examples utility */ + CegConjecturePbe * d_ceg_pbe; public: //non-syntax guided (deprecated) /** guard */ bool d_syntax_guided; Node d_nsg_guard; public: - /** get current term size */ - int getCurrentTermSize() { return d_curr_lit.get(); } - /** increment current term size */ - void incrementCurrentTermSize() { d_curr_lit.set( d_curr_lit.get() + 1 ); } - /** set measure term */ - void setMeasureTerm( Node mt ); - /** get measure term */ - Node getMeasureTermFactor( Node v ); - Node getMeasureTerm() { return d_measure_term; } - /** allocate literal */ - Node getFairnessLiteral( int i ); /** get guard */ Node getGuard(); /** is ground */ bool isGround() { return d_inner_vars.empty(); } /** fairness */ - CegqiFairMode getCegqiFairMode(); + SygusFairMode getCegqiFairMode(); /** is single invocation */ bool isSingleInvocation() const; /** is single invocation */ @@ -176,8 +134,6 @@ public: bool needsCheck( std::vector< Node >& lem ); /** preregister conjecture */ void preregisterConjecture( Node q ); - /** initialize guard */ - void initializeGuard(); /** assign */ void assign( Node q ); /** is assigned */ @@ -192,23 +148,6 @@ public: Node getRefinementLemma( unsigned i ) { return d_refinement_lemmas[i]; } /** get refinement lemma */ Node getRefinementBaseLemma( unsigned i ) { return d_refinement_lemmas_base[i]; } - /** get num conditional evaluations */ - unsigned getNumConditionalEvaluations( unsigned i ) { return d_refinement_lemmas_ceval_ant[i].size(); } - /** get conditional evaluation */ - Node getConditionalEvaluationAntec( unsigned i, unsigned j ) { return d_refinement_lemmas_ceval_ant[i][j]; } - Node getConditionalEvaluationConc( unsigned i, unsigned j ) { return d_refinement_lemmas_ceval_conc[i][j]; } - /** get progress lemma */ - //Node getConditionalProgressLemma( unsigned i ) { return d_refinement_lemmas_cprogress[i]; } - /** get progress point */ - //void getConditionalProgressLemmaPoint( unsigned i, std::vector< Node >& pt ){ - // pt.insert( pt.end(), d_refinement_lemmas_cprogress_pts[i].begin(), d_refinement_lemmas_cprogress_pts[i].end() ); - //} -private: - Node getNextDecisionRequestConditional( Node v, unsigned& priority ); - // 1 : active, 0 : unknown, -1 : inactive, -2 : not applicable - int getProgressStatus( Node v ); -public: - Node getNextDecisionRequest( unsigned& priority ); }; @@ -221,26 +160,10 @@ private: /** last instantiation by single invocation module? */ bool d_last_inst_si; /** evaluation axioms */ - std::map< Node, bool > d_eval_axioms; -private: //for enforcing fairness - /** measure functions */ - std::map< TypeNode, Node > d_uf_measure; - /** register measured type */ - void registerMeasuredType( TypeNode tn ); - /** term -> size term */ - std::map< Node, Node > d_size_term; - /** get size term */ - Node getSizeTerm( Node n, TypeNode tn, std::vector< Node >& lems ); - /** term x constructor -> lemma */ - std::map< Node, std::map< int, Node > > d_size_term_lemma; - /** get measure lemmas */ - void getMeasureLemmas( Node n, Node v, std::vector< Node >& lems ); + //std::map< Node, bool > d_eval_axioms; private: //for direct evaluation /** get refinement evaluation */ void getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems ); - Node crefEvaluate( Node lem, std::map< Node, Node >& vtm, std::map< Node, Node >& visited, std::map< Node, std::vector< Node > >& exp ); - /** get eager unfolding */ - Node getEagerUnfold( Node n, std::map< Node, Node >& visited ); private: /** check conjecture */ void checkCegConjecture( CegConjecture * conj ); diff --git a/src/theory/quantifiers/ce_guided_pbe.cpp b/src/theory/quantifiers/ce_guided_pbe.cpp new file mode 100644 index 000000000..38b5af85f --- /dev/null +++ b/src/theory/quantifiers/ce_guided_pbe.cpp @@ -0,0 +1,1859 @@ +/********************* */ +/*! \file ce_guided_pbe.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2016 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 utility for processing programming by examples synthesis conjectures + ** + **/ +#include "theory/quantifiers/ce_guided_pbe.h" + +#include "expr/datatype.h" +#include "options/quantifiers_options.h" +#include "theory/quantifiers/term_database.h" +#include "theory/datatypes/datatypes_rewriter.h" + +using namespace CVC4; +using namespace CVC4::kind; +using namespace CVC4::theory; +using namespace CVC4::theory::quantifiers; +using namespace std; + +namespace CVC4 { + +void indent( const char * c, int ind ) { + if( Trace.isOn(c) ){ + for( int i=0; i<ind; i++ ){ + Trace(c) << " "; + } + } +} +void print_val( const char * c, std::vector< Node >& vals, bool pol = true ){ + if( Trace.isOn(c) ){ + for( unsigned i=0; i<vals.size(); i++ ){ + //Trace(c) << ( pol ? vals[i] : !vals[i] ); + Trace(c) << ( ( pol ? vals[i].getConst<bool>() : !vals[i].getConst<bool>() ) ? "1" : "0" ); + } + } +} +void print_strat( const char * c, unsigned s ){ + switch(s){ + case CegConjecturePbe::strat_ITE:Trace(c) << "ITE";break; + case CegConjecturePbe::strat_CONCAT:Trace(c) << "CONCAT";break; + case CegConjecturePbe::strat_ID:Trace(c) << "ID";break; + default:Trace(c) << "strat_" << s;break; + } +} +void print_role( const char * c, unsigned r ){ + switch(r){ + case CegConjecturePbe::enum_io:Trace(c) << "IO";break; + case CegConjecturePbe::enum_ite_condition:Trace(c) << "CONDITION";break; + case CegConjecturePbe::enum_concat_term:Trace(c) << "CTERM";break; + case CegConjecturePbe::enum_any:Trace(c) << "ANY";break; + default:Trace(c) << "role_" << r;break; + } +} + +CegConjecturePbe::CegConjecturePbe(QuantifiersEngine* qe, CegConjecture* p) + : d_qe(qe), + d_parent(p){ + d_tds = d_qe->getTermDatabaseSygus(); + d_true = NodeManager::currentNM()->mkConst(true); + d_false = NodeManager::currentNM()->mkConst(false); + d_is_pbe = false; +} + +CegConjecturePbe::~CegConjecturePbe() { + +} + +//--------------------------------- collecting finite input/output domain information + +void CegConjecturePbe::collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + Node neval; + Node n_output; + if( n.getKind()==APPLY_UF && n.getNumChildren()>0 ){ + neval = n; + if( hasPol ){ + n_output = !pol ? d_true : d_false; + } + }else if( n.getKind()==EQUAL && hasPol && !pol ){ + for( unsigned r=0; r<2; r++ ){ + if( n[r].getKind()==APPLY_UF && n[r].getNumChildren()>0 ){ + neval = n[r]; + if( n[1-r].isConst() ){ + n_output = n[1-r]; + } + } + } + } + if( !neval.isNull() ){ + if( neval.getKind()==APPLY_UF && neval.getNumChildren()>0 ){ + // is it an evaluation function? + if( d_examples.find( neval[0] )!=d_examples.end() ){ + std::map< Node, bool >::iterator itx = d_examples_invalid.find( neval[0] ); + if( itx==d_examples_invalid.end() ){ + //collect example + bool success = true; + std::vector< Node > ex; + for( unsigned j=1; j<neval.getNumChildren(); j++ ){ + if( !neval[j].isConst() ){ + success = false; + break; + }else{ + ex.push_back( neval[j] ); + } + } + if( success ){ + d_examples[neval[0]].push_back( ex ); + d_examples_out[neval[0]].push_back( n_output ); + d_examples_term[neval[0]].push_back( neval ); + if( n_output.isNull() ){ + d_examples_out_invalid[neval[0]] = true; + }else{ + Assert( n_output.isConst() ); + } + //finished processing this node + return; + }else{ + d_examples_invalid[neval[0]] = true; + d_examples_out_invalid[neval[0]] = true; + } + } + } + } + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + bool newHasPol; + bool newPol; + QuantPhaseReq::getPolarity( n, i, hasPol, pol, newHasPol, newPol ); + collectExamples( n[i], visited, newHasPol, newPol ); + } + } +} + +void CegConjecturePbe::initialize( Node n, std::vector< Node >& candidates, std::vector< Node >& lemmas ) { + Trace("sygus-pbe") << "Initialize PBE : " << n << std::endl; + + for( unsigned i=0; i<candidates.size(); i++ ){ + Node v = candidates[i]; + d_examples[v].clear(); + d_examples_out[v].clear(); + d_examples_term[v].clear(); + } + + std::map< Node, bool > visited; + collectExamples( n, visited, true, true ); + + for( unsigned i=0; i<candidates.size(); i++ ){ + Node v = candidates[i]; + Trace("sygus-pbe") << " examples for " << v << " : "; + if( d_examples_invalid.find( v )!=d_examples_invalid.end() ){ + Trace("sygus-pbe") << "INVALID" << std::endl; + }else{ + Trace("sygus-pbe") << std::endl; + for( unsigned j=0; j<d_examples[v].size(); j++ ){ + Trace("sygus-pbe") << " "; + for( unsigned k=0; k<d_examples[v][j].size(); k++ ){ + Trace("sygus-pbe") << d_examples[v][j][k] << " "; + } + if( !d_examples_out[v][j].isNull() ){ + Trace("sygus-pbe") << " -> " << d_examples_out[v][j]; + } + Trace("sygus-pbe") << std::endl; + } + } + } + + //register candidates + if( options::sygusUnifCondSol() ){ + if( candidates.size()==1 ){// conditional solutions for multiple function conjectures TODO? + // collect a pool of types over which we will enumerate terms + Node c = candidates[0]; + //the candidate must be input/output examples + if( d_examples_out_invalid.find( c )==d_examples_out_invalid.end() ){ + Assert( d_examples.find( c )!=d_examples.end() ); + Trace("sygus-unif") << "It is input/output examples..." << std::endl; + TypeNode ctn = c.getType(); + d_cinfo[c].initialize( c ); + // collect the enumerator types / form the strategy + collectEnumeratorTypes( c, ctn, enum_io ); + // if we have non-trivial strategies, then use pbe + if( d_cinfo[c].isNonTrivial() ){ + // static learning of redundant constructors + staticLearnRedundantOps( c, lemmas ); + d_is_pbe = true; + } + } + } + } + if( !d_is_pbe ){ + Trace("sygus-unif") << "Do not do PBE optimizations, register..." << std::endl; + for( unsigned i=0; i<candidates.size(); i++ ){ + d_qe->getTermDatabaseSygus()->registerMeasuredTerm( candidates[i], candidates[i] ); + } + } +} + +bool CegConjecturePbe::getPbeExamples( Node v, std::vector< std::vector< Node > >& exs, + std::vector< Node >& exos, std::vector< Node >& exts ) { + std::map< Node, bool >::iterator itx = d_examples_invalid.find( v ); + if( itx==d_examples_invalid.end() ){ + Assert( d_examples.find( v )!=d_examples.end() ); + exs = d_examples[v]; + Assert( d_examples_out.find( v )!=d_examples_out.end() ); + exos = d_examples_out[v]; + Assert( d_examples_term.find( v )!=d_examples_term.end() ); + exts = d_examples_term[v]; + return true; + } + return false; +} + + +// ----------------------------- establishing enumeration types + + +void CegConjecturePbe::registerEnumerator( Node et, Node c, TypeNode tn, unsigned enum_role, bool inSearch ) { + Trace("sygus-unif-debug") << "...register " << et << " for " << ((DatatypeType)tn.toType()).getDatatype().getName(); + Trace("sygus-unif-debug") << ", role = "; + print_role( "sygus-unif-debug", enum_role ); + Trace("sygus-unif-debug") << ", in search = " << inSearch << std::endl; + d_einfo[et].d_parent_candidate = c; + d_einfo[et].d_role = enum_role; + // if we are actually enumerating this (could be a compound node in the strategy) + if( inSearch ){ + std::map< TypeNode, Node >::iterator itn = d_cinfo[c].d_search_enum.find( tn ); + if( itn==d_cinfo[c].d_search_enum.end() ){ + // use this for the search + d_cinfo[c].d_search_enum[tn] = et; + d_cinfo[c].d_esym_list.push_back( et ); + d_einfo[et].d_enum_slave.push_back( et ); + //register measured term with database + d_qe->getTermDatabaseSygus()->registerMeasuredTerm( et, c, true ); + d_einfo[et].d_active_guard = d_qe->getTermDatabaseSygus()->getActiveGuardForMeasureTerm( et ); + }else{ + Trace("sygus-unif-debug") << "Make " << et << " a slave of " << itn->second << std::endl; + d_einfo[itn->second].d_enum_slave.push_back( et ); + } + } +} + +void CegConjecturePbe::collectEnumeratorTypes( Node e, TypeNode tn, unsigned enum_role ) { + if( d_cinfo[e].d_tinfo.find( tn )==d_cinfo[e].d_tinfo.end() ){ + // register type + Trace("sygus-unif") << "Register enumerating type : " << tn << std::endl; + d_cinfo[e].initializeType( tn ); + } + if( d_cinfo[e].d_tinfo[tn].d_enum.find( enum_role )==d_cinfo[e].d_tinfo[tn].d_enum.end() ){ + + Node ee = NodeManager::currentNM()->mkSkolem( "ee", tn ); + d_cinfo[e].d_tinfo[tn].d_enum[enum_role] = ee; + Trace("sygus-unif-debug") << "...enumerator " << ee << " for " << ((DatatypeType)tn.toType()).getDatatype().getName() << ", role = "; + print_role( "sygus-unif-debug", enum_role ); + Trace("sygus-unif-debug") << std::endl; + // wait to register : may or may not actually be enumerating it + + if( enum_role==enum_io ){ + // look at information on how we will construct solutions for this type + Assert( tn.isDatatype() ); + const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype(); + Assert( dt.isSygus() ); + std::map< Node, std::vector< TypeNode > > cop_to_child_types; + std::map< Node, std::map< unsigned, Node > > cop_to_child_templ; + std::map< Node, std::map< unsigned, Node > > cop_to_child_templ_arg; + std::map< Node, unsigned > cop_to_strat; + std::map< Node, unsigned > cop_to_cindex; + + for( unsigned r=0; r<2; r++ ){ + for( unsigned j=0; j<dt.getNumConstructors(); j++ ){ + bool success = false; + Node cop = Node::fromExpr( dt[j].getConstructor() ); + Node op = Node::fromExpr( dt[j].getSygusOp() ); + if( r==0 ){ + cop_to_cindex[cop] = j; + if( op.getKind() == kind::BUILTIN ){ + Kind sk = NodeManager::operatorToKind( op ); + if( sk==kind::ITE ){ + Trace("sygus-unif") << "...type " << dt.getName() << " has ITE, enumerate child types..." << std::endl; + // we can do unification + Assert( dt[j].getNumArgs()==3 ); + cop_to_strat[cop] = strat_ITE; + }else if( sk==kind::STRING_CONCAT ){ + if( dt[j].getNumArgs()==2 ) { + cop_to_strat[cop] = strat_CONCAT; + } + Trace("sygus-unif") << "...type " << dt.getName() << " has CONCAT, child types successful = " << success << std::endl; + } + if( cop_to_strat.find( cop )!=cop_to_strat.end() ){ + // add child types + for( unsigned k=0; k<dt[j].getNumArgs(); k++ ){ + TypeNode ct = TypeNode::fromType( dt[j][k].getRangeType() ); + Trace("sygus-unif") << " Child type " << k << " : " << ((DatatypeType)ct.toType()).getDatatype().getName() << std::endl; + cop_to_child_types[cop].push_back( ct ); + } + } + } + }else if( cop_to_strat.find( cop )==cop_to_strat.end() ){ + // could be a defined function (this is a hack for ICFP benchmarks) + std::vector< Node > utchildren; + utchildren.push_back( cop ); + std::vector< Node > sks; + std::vector< TypeNode > sktns; + for( unsigned k=0; k<dt[j].getNumArgs(); k++ ){ + Type t = dt[j][k].getRangeType(); + TypeNode ttn = TypeNode::fromType( t ); + Node kv = NodeManager::currentNM()->mkSkolem( "ut", ttn ); + sks.push_back( kv ); + sktns.push_back( ttn ); + utchildren.push_back( kv ); + } + Node ut = NodeManager::currentNM()->mkNode( kind::APPLY_CONSTRUCTOR, utchildren ); + std::vector< Node > echildren; + echildren.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); + echildren.push_back( ut ); + Node sbvl = Node::fromExpr( dt.getSygusVarList() ); + for( unsigned k=0; k<sbvl.getNumChildren(); k++ ){ + echildren.push_back( sbvl[k] ); + } + Node eut = NodeManager::currentNM()->mkNode( kind::APPLY_UF, echildren ); + Trace("sygus-unif-debug2") << "Test evaluation of " << eut << "..." << std::endl; + eut = d_qe->getTermDatabaseSygus()->unfold( eut ); + Trace("sygus-unif-debug2") << "...got " << eut << std::endl; + Trace("sygus-unif-debug2") << "Type : " << eut.getType() << std::endl; + + if( eut.getKind()==kind::ITE ){ + if( dt[j].getNumArgs()>=eut.getNumChildren() ){ + cop_to_strat[cop] = strat_ITE; + } + }else if( eut.getKind()==kind::STRING_CONCAT ){ + if( dt[j].getNumArgs()>=eut.getNumChildren() ){ + cop_to_strat[cop] = strat_CONCAT; + } + }else if( eut.getKind()==kind::APPLY_UF ){ + // identity operator? + if( dt[j].getNumArgs()==1 ){ + cop_to_strat[cop] = strat_ID; + } + } + + if( cop_to_strat.find( cop )!=cop_to_strat.end() ){ + std::map< unsigned, unsigned > templ_injection; + std::vector< Node > vs; + std::vector< Node > ss; + std::map< Node, unsigned > templ_var_index; + for( unsigned k=0; k<sks.size(); k++ ){ + Assert( sks[k].getType().isDatatype() ); + const Datatype& cdt = ((DatatypeType)sks[k].getType().toType()).getDatatype(); + echildren[0] = Node::fromExpr( cdt.getSygusEvaluationFunc() ); + echildren[1] = sks[k]; + Trace("sygus-unif-debug2") << "...set eval dt to " << sks[k] << std::endl; + Node esk = NodeManager::currentNM()->mkNode( kind::APPLY_UF, echildren ); + vs.push_back( esk ); + Node tvar = NodeManager::currentNM()->mkSkolem( "templ", esk.getType() ); + templ_var_index[tvar] = k; + Trace("sygus-unif-debug2") << "* template inference : looking for " << tvar << " for arg " << k << std::endl; + ss.push_back( tvar ); + Trace("sygus-unif-debug2") << "* substitute : " << esk << " -> " << tvar << std::endl; + } + eut = eut.substitute( vs.begin(), vs.end(), ss.begin(), ss.end() ); + Trace("sygus-unif-debug2") << "Defined constructor " << j << ", base term is " << eut << std::endl; + std::map< unsigned, Node > test_args; + if( cop_to_strat[cop] == strat_ID ){ + test_args[0] = eut; + }else{ + for( unsigned k=0; k<eut.getNumChildren(); k++ ){ + test_args[k] = eut[k]; + } + } + for( std::map< unsigned, Node >::iterator it = test_args.begin(); it != test_args.end(); ++it ){ + unsigned k = it->first; + Node eut_c = it->second; + //success if we can find a injection from args to sygus args + if( !inferTemplate( k, eut_c, templ_var_index, templ_injection ) ){ + Trace("sygus-unif-debug2") << "...failed to find injection (range)." << std::endl; + cop_to_strat.erase( cop ); + break; + } + if( templ_injection.find( k )==templ_injection.end() ){ + Trace("sygus-unif-debug2") << "...failed to find injection (domain)." << std::endl; + cop_to_strat.erase( cop ); + break; + } + } + if( cop_to_strat.find( cop )!=cop_to_strat.end() ){ + Trace("sygus-unif") << "...type " << dt.getName() << " has defined constructor matching strategy "; + Trace("sygus-unif") << cop_to_strat[cop] << ", enumerate child types..." << std::endl; + for( unsigned k=0; k<eut.getNumChildren(); k++ ){ + Assert( templ_injection.find( k )!=templ_injection.end() ); + unsigned sk_index = templ_injection[k]; + //also store the template information, if necessary + Node teut = eut[k]; + if( !teut.isVar() ){ + if( cop_to_strat[cop] == strat_ID ){ + Trace("sygus-unif-debug") << "...cannot use template with ID strategy." << std::endl; + cop_to_strat.erase( cop ); + }else{ + cop_to_child_templ[cop][k] = teut; + cop_to_child_templ_arg[cop][k] = ss[sk_index]; + Trace("sygus-unif") << " Arg " << k << " : template : " << teut << ", arg " << ss[sk_index] << std::endl; + } + }else{ + Assert( teut==ss[sk_index] ); + } + } + // collect children types + for( unsigned k=0; k<dt[j].getNumArgs(); k++ ){ + Trace("sygus-unif") << " Child type " << k << " : " << ((DatatypeType)sktns[k].toType()).getDatatype().getName() << std::endl; + cop_to_child_types[cop].push_back( sktns[k] ); + } + } + } + } + } + } + bool search_this = true; + for( std::map< Node, unsigned >::iterator itc = cop_to_strat.begin(); itc != cop_to_strat.end(); ++itc ){ + if( itc->second==strat_CONCAT || ( itc->second==strat_ID && dt.getNumConstructors()==1 ) ){ + search_this = false; + break; + } + } + Trace("sygus-unif-debug2") << "...this register..." << std::endl; + registerEnumerator( ee, e, tn, enum_role, search_this ); + + if( cop_to_child_types.empty() ){ + Trace("sygus-unif") << "...consider " << dt.getName() << " a basic type" << std::endl; + }else{ + for( std::map< Node, std::vector< TypeNode > >::iterator itct = cop_to_child_types.begin(); itct != cop_to_child_types.end(); ++itct ){ + Node cop = itct->first; + Assert( cop_to_strat.find( cop )!=cop_to_strat.end() ); + unsigned strat = cop_to_strat[cop]; + d_cinfo[e].d_tinfo[tn].d_strat[cop].d_this = strat; + Trace("sygus-unif-debug") << "Process strategy for operator : " << cop << " : "; + print_strat("sygus-unif-debug", strat ); + Trace("sygus-unif-debug") << std::endl; + + for( unsigned j=0; j<itct->second.size(); j++ ){ + //calculate if we should allocate a new enumerator : should be true if we have a new role + unsigned enum_role_c = enum_role; + if( strat==strat_ITE ){ + if( j==0 ){ + enum_role_c = enum_ite_condition; + }else{ + // role is the same as parent + } + }else if( strat==strat_CONCAT ){ + enum_role_c = enum_concat_term; + }else if( strat==strat_ID ){ + // role is the same as parent + } + + // register the child type + TypeNode ct = itct->second[j]; + d_cinfo[e].d_tinfo[tn].d_strat[cop].d_csol_cts.push_back( ct ); + + // make the enumerator + Node et; + if( cop_to_child_templ[cop].find( j )!=cop_to_child_templ[cop].end() ){ + // it is templated, allocate a fresh variable + et = NodeManager::currentNM()->mkSkolem( "et", ct ); + Trace("sygus-unif-debug") << "...enumerate " << et << " of type " << ((DatatypeType)ct.toType()).getDatatype().getName(); + Trace("sygus-unif-debug") << " for arg " << j << " of " << ((DatatypeType)tn.toType()).getDatatype().getName() << std::endl; + registerEnumerator( et, e, ct, enum_role_c, true ); + d_einfo[et].d_template = cop_to_child_templ[cop][j]; + d_einfo[et].d_template_arg = cop_to_child_templ_arg[cop][j]; + Assert( !d_einfo[et].d_template.isNull() ); + Assert( !d_einfo[et].d_template_arg.isNull() ); + }else{ + Trace("sygus-unif-debug") << "...child type enumerate " << ((DatatypeType)ct.toType()).getDatatype().getName() << ", role = "; + print_role( "sygus-unif-debug", enum_role_c ); + Trace("sygus-unif-debug") << std::endl; + collectEnumeratorTypes( e, ct, enum_role_c ); + // otherwise use the previous + Assert( d_cinfo[e].d_tinfo[ct].d_enum.find( enum_role_c )!=d_cinfo[e].d_tinfo[ct].d_enum.end() ); + et = d_cinfo[e].d_tinfo[ct].d_enum[enum_role_c]; + } + Trace("sygus-unif-debug") << "Register child enumerator " << et << ", arg " << j << " of " << cop << ", role = "; + print_role( "sygus-unif-debug", enum_role_c ); + Trace("sygus-unif-debug") << std::endl; + Assert( !et.isNull() ); + d_cinfo[e].d_tinfo[tn].d_strat[cop].d_cenum.push_back( et ); + // need to make this take into account template + //Assert( et.getType()==e.getType() || d_einfo[et].d_role!=enum_io ); + } + Trace("sygus-unif") << "Initialized strategy "; + print_strat( "sygus-unif", strat ); + Trace("sygus-unif") << " for " << ((DatatypeType)tn.toType()).getDatatype().getName() << ", operator " << cop; + Trace("sygus-unif") << ", #children = " << d_cinfo[e].d_tinfo[tn].d_strat[cop].d_cenum.size() << std::endl; + Assert( d_cinfo[e].d_tinfo[tn].d_strat[cop].d_cenum.size()==d_cinfo[e].d_tinfo[tn].d_strat[cop].d_csol_cts.size() ); + } + } + }else{ + Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl; + registerEnumerator( ee, e, tn, enum_role, true ); + } + } +} + +bool CegConjecturePbe::inferTemplate( unsigned k, Node n, std::map< Node, unsigned >& templ_var_index, std::map< unsigned, unsigned >& templ_injection ){ + if( n.getNumChildren()==0 ){ + std::map< Node, unsigned >::iterator itt = templ_var_index.find( n ); + if( itt!=templ_var_index.end() ){ + unsigned kk = itt->second; + std::map< unsigned, unsigned >::iterator itti = templ_injection.find( k ); + if( itti==templ_injection.end() ){ + Trace("sygus-unif-debug") << "...set template injection " << k << " -> " << kk << std::endl; + templ_injection[k] = kk; + }else if( itti->second!=kk ){ + return false; + } + } + return true; + }else{ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !inferTemplate( k, n[i], templ_var_index, templ_injection ) ){ + return false; + } + } + } + return true; +} + +void CegConjecturePbe::staticLearnRedundantOps( Node c, std::vector< Node >& lemmas ) { + for( unsigned i=0; i<d_cinfo[c].d_esym_list.size(); i++ ){ + Node e = d_cinfo[c].d_esym_list[i]; + std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e ); + Assert( itn!=d_einfo.end() ); + // see if there is anything we can eliminate + Trace("sygus-unif") << "* Search enumerator #" << i << " : type " << ((DatatypeType)e.getType().toType()).getDatatype().getName() << " : "; + Trace("sygus-unif") << e << " has " << itn->second.d_enum_slave.size() << " slaves:" << std::endl; + for( unsigned j=0; j<itn->second.d_enum_slave.size(); j++ ){ + Node es = itn->second.d_enum_slave[j]; + std::map< Node, EnumInfo >::iterator itns = d_einfo.find( es ); + Assert( itns!=d_einfo.end() ); + Trace("sygus-unif") << " " << es << ", role = "; + print_role( "sygus-unif", itns->second.d_role ); + Trace("sygus-unif") << std::endl; + } + } + Trace("sygus-unif") << std::endl; + Trace("sygus-unif") << "Strategy for candidate " << c << " is : " << std::endl; + std::map< Node, bool > visited; + std::vector< Node > redundant; + staticLearnRedundantOps( c, d_cinfo[c].getRootEnumerator(), visited, redundant, lemmas, 0 ); + for( unsigned i=0; i<lemmas.size(); i++ ){ + Trace("sygus-unif") << "...can exclude based on : " << lemmas[i] << std::endl; + } +} + +void CegConjecturePbe::staticLearnRedundantOps( Node c, Node e, std::map< Node, bool >& visited, std::vector< Node >& redundant, + std::vector< Node >& lemmas, int ind ) { + + std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e ); + Assert( itn!=d_einfo.end() ); + if( visited.find( e )==visited.end() ){ + visited[e] = true; + + indent("sygus-unif", ind); + Trace("sygus-unif") << e << " : role : "; + print_role("sygus-unif", itn->second.d_role); + Trace("sygus-unif") << " : "; + + if( itn->second.isTemplated() ){ + Trace("sygus-unif") << "basic, templated : \\ " << itn->second.d_template_arg << ". " << itn->second.d_template << std::endl; + }else{ + TypeNode etn = e.getType(); + std::map< TypeNode, EnumTypeInfo >::iterator itt = d_cinfo[c].d_tinfo.find( etn ); + Assert( itt!=d_cinfo[c].d_tinfo.end() ); + if( itt->second.d_strat.empty() ){ + Trace("sygus-unif") << "basic" << std::endl; + }else{ + Trace("sygus-unif") << "compound" << std::endl; + // various strategies + for( std::map< Node, EnumTypeInfoStrat >::iterator itts = itt->second.d_strat.begin(); itts!=itt->second.d_strat.end(); ++itts ){ + indent("sygus-unif", ind+1); + Trace("sygus-unif") << "Strategy : "; + unsigned strat = itts->second.d_this; + print_strat("sygus-unif", strat); + Trace("sygus-unif") << std::endl; + for( unsigned i=0; i<itts->second.d_cenum.size(); i++ ){ + std::vector< Node > redundant_c; + bool no_repeat_op = false; + // do not repeat operators that the strategy uses + if( itts->second.d_csol_cts[i]==etn ){ + if( strat==strat_ITE && i!=0 ){ + no_repeat_op = true; + }else if( strat==strat_CONCAT || strat==strat_ID ){ + no_repeat_op = true; + } + } + if( no_repeat_op ){ + redundant_c.push_back( itts->first ); + } + //do not use standard Boolean connectives in ITE conditions + if( strat==strat_ITE && i==0 && itts->second.d_csol_cts[1]==itts->second.d_csol_cts[2] ){ + TypeNode ctn = itts->second.d_csol_cts[0]; + const Datatype& cdt = ((DatatypeType)ctn.toType()).getDatatype(); + for( unsigned j=0; j<cdt.getNumConstructors(); j++ ){ + Kind ck = d_tds->getConsNumKind( ctn, j ); + if( ck!=UNDEFINED_KIND && TermDb::isBoolConnective( ck ) ){ + bool typeCorrect = true; + for( unsigned k=0; k<cdt[j].getNumArgs(); k++ ){ + if( d_tds->getArgType( cdt[j], k )!=ctn ){ + typeCorrect = false; + break; + } + } + if( typeCorrect ){ + Trace("sygus-unif-debug") << "Exclude Boolean connective in ITE conditional : " << ck << " in conditional type " << cdt.getName() << std::endl; + Node exc_cons = Node::fromExpr( cdt[j].getConstructor() ); + if( std::find( redundant_c.begin(), redundant_c.end(), exc_cons )==redundant_c.end() ){ + redundant_c.push_back( exc_cons ); + } + } + } + } + } + // recurse + staticLearnRedundantOps( c, itts->second.d_cenum[i], visited, redundant_c, lemmas, ind+2 ); + } + } + } + } + }else{ + indent("sygus-unif", ind); + Trace("sygus-unif") << e << std::endl; + } + if( !redundant.empty() ){ + // TODO : if this becomes more general, must get master enumerator here + if( itn->second.d_enum_slave.size()==1 ){ + for( unsigned i=0; i<redundant.size(); i++ ){ + int cindex = Datatype::indexOf( redundant[i].toExpr() ); + Assert( cindex!=-1 ); + const Datatype& dt = Datatype::datatypeOf( redundant[i].toExpr() ); + Node tst = datatypes::DatatypesRewriter::mkTester( e, cindex, dt ).negate(); + if( std::find( lemmas.begin(), lemmas.end(), tst )==lemmas.end() ){ + lemmas.push_back( tst ); + } + } + } + } +} + + +// ------------------------------------------- solution construction from enumeration + +void CegConjecturePbe::getCandidateList( std::vector< Node >& candidates, std::vector< Node >& clist ) { + for( unsigned i=0; i<candidates.size(); i++ ){ + Node v = candidates[i]; + std::map< Node, CandidateInfo >::iterator it = d_cinfo.find( v ); + if( it!=d_cinfo.end() ){ + for( unsigned j=0; j<it->second.d_esym_list.size(); j++ ){ + Node e = it->second.d_esym_list[j]; + std::map< Node, EnumInfo >::iterator it = d_einfo.find( e ); + Assert( it != d_einfo.end() ); + if( getGuardStatus( it->second.d_active_guard )==1 ){ + clist.push_back( e ); + } + } + } + } +} + +bool CegConjecturePbe::constructCandidates( std::vector< Node >& enums, std::vector< Node >& enum_values, + std::vector< Node >& candidates, std::vector< Node >& candidate_values, + std::vector< Node >& lems ) { + Assert( enums.size()==enum_values.size() ); + if( !enums.empty() ){ + unsigned min_term_size = 0; + std::vector< unsigned > enum_consider; + Trace("sygus-pbe-enum") << "Register new enumerated values : " << std::endl; + for( unsigned i=0; i<enums.size(); i++ ){ + Trace("sygus-pbe-enum") << " " << enums[i] << " -> " << enum_values[i] << std::endl; + unsigned sz = d_tds->getSygusTermSize( enum_values[i] ); + if( i==0 || sz<min_term_size ){ + enum_consider.clear(); + min_term_size = sz; + enum_consider.push_back( i ); + }else if( sz==min_term_size ){ + enum_consider.push_back( i ); + } + } + // only consider the enumerators that are at minimum size (for fairness) + Trace("sygus-pbe-enum") << "...register " << enum_consider.size() << " / " << enums.size() << std::endl; + for( unsigned i=0; i<enum_consider.size(); i++ ){ + unsigned j = enum_consider[i]; + addEnumeratedValue( enums[j], enum_values[j], lems ); + } + } + for( unsigned i=0; i<candidates.size(); i++ ){ + Node c = candidates[i]; + //build decision tree for candidate + Node vc = constructSolution( c ); + if( vc.isNull() ){ + return false; + }else{ + candidate_values.push_back( vc ); + } + } + return true; +} + +void CegConjecturePbe::addEnumeratedValue( Node x, Node v, std::vector< Node >& lems ) { + std::map< Node, EnumInfo >::iterator it = d_einfo.find( x ); + Assert( it != d_einfo.end() ); + if( getGuardStatus( it->second.d_active_guard )==1 ){ + Assert( std::find( it->second.d_enum_vals.begin(), it->second.d_enum_vals.end(), v )==it->second.d_enum_vals.end() ); + Node c = it->second.d_parent_candidate; + Node exp_exc; + if( d_examples_out_invalid.find( c )==d_examples_out_invalid.end() ){ + std::map< Node, CandidateInfo >::iterator itc = d_cinfo.find( c ); + Assert( itc != d_cinfo.end() ); + TypeNode xtn = x.getType(); + Node bv = d_tds->sygusToBuiltin( v, xtn ); + std::map< Node, std::vector< std::vector< Node > > >::iterator itx = d_examples.find( c ); + std::map< Node, std::vector< Node > >::iterator itxo = d_examples_out.find( c ); + Assert( itx!=d_examples.end() ); + Assert( itxo!=d_examples_out.end() ); + Assert( itx->second.size()==itxo->second.size() ); + // notify all slaves + Assert( !it->second.d_enum_slave.empty() ); + //explanation for why this value should be excluded + for( unsigned s=0; s<it->second.d_enum_slave.size(); s++ ){ + Node xs = it->second.d_enum_slave[s]; + std::map< Node, EnumInfo >::iterator itv = d_einfo.find( xs ); + Assert( itv!=d_einfo.end() ); + Trace("sygus-pbe-enum") << "Process " << xs << " from " << s << std::endl; + //bool prevIsCover = false; + if( itv->second.d_role==enum_io ){ + Trace("sygus-pbe-enum") << " IO-Eval of "; + //prevIsCover = itv->second.isFeasible(); + }else{ + Trace("sygus-pbe-enum") << "Evaluation of "; + } + Trace("sygus-pbe-enum") << xs << " : "; + //evaluate all input/output examples + std::vector< Node > results; + Node templ = itv->second.d_template; + TNode templ_var = itv->second.d_template_arg; + std::map< Node, bool > cond_vals; + for( unsigned j=0; j<itx->second.size(); j++ ){ + Node res = d_tds->evaluateBuiltin( xtn, bv, itx->second[j] ); + Trace("sygus-pbe-enum-debug") << "...got res = " << res << " from " << bv << std::endl; + Assert( res.isConst() ); + if( !templ.isNull() ){ + TNode tres = res; + res = templ.substitute( templ_var, res ); + res = Rewriter::rewrite( res ); + Assert( res.isConst() ); + } + Node resb; + if( itv->second.d_role==enum_io ){ + Node out = itxo->second[j]; + Assert( out.isConst() ); + resb = res==out ? d_true : d_false; + }else{ + resb = res; + } + cond_vals[resb] = true; + results.push_back( resb ); + if( Trace.isOn("sygus-pbe-enum") ){ + if( resb.getType().isBoolean() ){ + Trace("sygus-pbe-enum") << ( resb==d_true ? "1" : "0" ); + }else{ + Trace("sygus-pbe-enum") << "?"; + } + } + } + bool keep = false; + if( itv->second.d_role==enum_io ){ + if( cond_vals.find( d_true )!=cond_vals.end() || cond_vals.empty() ){ // latter is the degenerate case of no examples + //check subsumbed/subsuming + std::vector< Node > subsume; + if( cond_vals.find( d_false )==cond_vals.end() ){ + // it is the entire solution, we are done + Trace("sygus-pbe-enum") << " ...success, full solution added to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl; + if( !itv->second.isSolved() ){ + itv->second.setSolved( v ); + // it subsumes everything + itv->second.d_term_trie.clear(); + itv->second.d_term_trie.addTerm( this, v, results, true, subsume ); + } + keep = true; + }else{ + Node val = itv->second.d_term_trie.addTerm( this, v, results, true, subsume ); + if( val==v ){ + Trace("sygus-pbe-enum") << " ...success"; + if( !subsume.empty() ){ + itv->second.d_enum_subsume.insert( itv->second.d_enum_subsume.end(), subsume.begin(), subsume.end() ); + Trace("sygus-pbe-enum") << " and subsumed " << subsume.size() << " terms"; + } + Trace("sygus-pbe-enum") << "! add to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl; + keep = true; + }else{ + Assert( subsume.empty() ); + Trace("sygus-pbe-enum") << " ...fail : subsumed" << std::endl; + } + } + }else{ + Trace("sygus-pbe-enum") << " ...fail : it does not satisfy examples." << std::endl; + } + }else{ + // is it excluded for domain-specific reason? + std::vector< Node > exp_exc_vec; + if( getExplanationForEnumeratorExclude( c, x, v, results, it->second, exp_exc_vec ) ){ + Assert( !exp_exc_vec.empty() ); + exp_exc = exp_exc_vec.size()==1 ? exp_exc_vec[0] : NodeManager::currentNM()->mkNode( kind::AND, exp_exc_vec ); + Trace("sygus-pbe-enum") << " ...fail : term is excluded (domain-specific)" << std::endl; + }else{ + //if( cond_vals.size()!=2 ){ + // // must discriminate + // Trace("sygus-pbe-enum") << " ...fail : conditional is constant." << std::endl; + // keep = false; + //} + // must be unique up to examples + Node val = itv->second.d_term_trie.addCond( this, v, results, true ); + if( val==v ){ + Trace("sygus-pbe-enum") << " ...success! add to PBE pool : " << d_tds->sygusToBuiltin( v ) << std::endl; + keep = true; + }else{ + Trace("sygus-pbe-enum") << " ...fail : term is not unique" << std::endl; + } + itc->second.d_cond_count++; + } + } + if( keep ){ + // notify the parent to retry the build of PBE + itc->second.d_check_sol = true; + itv->second.addEnumValue( this, v, results ); + /* + if( Trace.isOn("sygus-pbe-enum") ){ + if( itv->second.d_role==enum_io ){ + if( !prevIsCover && itv->second.isFeasible() ){ + Trace("sygus-pbe-enum") << "...PBE : success : Evaluation of " << xs << " now covers all examples." << std::endl; + } + } + } + */ + } + } + }else{ + Trace("sygus-pbe-enum-debug") << " ...examples do not have output." << std::endl; + } + //exclude this value on subsequent iterations + Node g = it->second.d_active_guard; + if( exp_exc.isNull() ){ + // if we did not already explain why this should be excluded, use default + exp_exc = d_tds->getExplanationForConstantEquality( x, v ); + } + Node exlem = NodeManager::currentNM()->mkNode( kind::OR, g.negate(), exp_exc.negate() ); + Trace("sygus-pbe-enum-lemma") << "CegConjecturePbe : enumeration exclude lemma : " << exlem << std::endl; + lems.push_back( exlem ); + }else{ + Trace("sygus-pbe-enum-debug") << " ...guard is inactive." << std::endl; + } +} + + + +class NegContainsSygusInvarianceTest : public quantifiers::SygusInvarianceTest { +public: + NegContainsSygusInvarianceTest(){} + ~NegContainsSygusInvarianceTest(){} + Node d_ar; + std::vector< Node > d_exo; + std::vector< unsigned > d_neg_con_indices; + + void init( quantifiers::TermDbSygus * tds, Node ar, std::vector< Node >& exo, std::vector< unsigned >& ncind ) { + if( tds->hasPbeExamples( ar ) ){ + Assert( tds->getNumPbeExamples( ar )==exo.size() ); + d_ar = ar; + d_exo.insert( d_exo.end(), exo.begin(), exo.end() ); + d_neg_con_indices.insert( d_neg_con_indices.end(), ncind.begin(), ncind.end() ); + } + } +protected: + bool invariant( quantifiers::TermDbSygus * tds, Node nvn, Node x ){ + if( !d_ar.isNull() ){ + TypeNode tn = nvn.getType(); + Node nbv = tds->sygusToBuiltin( nvn, tn ); + Node nbvr = tds->extendedRewrite( nbv ); + // if for any of the examples, it is not contained, then we can exclude + for( unsigned i=0; i<d_neg_con_indices.size(); i++ ){ + unsigned ii = d_neg_con_indices[i]; + Assert( ii<d_exo.size() ); + Node nbvre = tds->evaluateBuiltin( tn, nbvr, d_ar, ii ); + Node out = d_exo[ii]; + Node cont = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, out, nbvre ); + Node contr = Rewriter::rewrite( cont ); + if( contr==tds->d_false ){ + if( Trace.isOn("sygus-pbe-cterm") ){ + Trace("sygus-pbe-cterm") << "PBE-cterm : enumerator : do not consider "; + Trace("sygus-pbe-cterm") << nbv << " for any " << tds->sygusToBuiltin( x ) << " since " << std::endl; + Trace("sygus-pbe-cterm") << " PBE-cterm : for input example : "; + std::vector< Node > ex; + tds->getPbeExample( d_ar, ii, ex ); + for( unsigned j=0; j<ex.size(); j++ ){ + Trace("sygus-pbe-cterm") << ex[j] << " "; + } + Trace("sygus-pbe-cterm") << std::endl; + Trace("sygus-pbe-cterm") << " PBE-cterm : this rewrites to : " << nbvre << std::endl; + Trace("sygus-pbe-cterm") << " PBE-cterm : and is not in output : " << out << std::endl; + } + return true; + } + } + } + return false; + } +}; + + +bool CegConjecturePbe::getExplanationForEnumeratorExclude( Node c, Node x, Node v, std::vector< Node >& results, EnumInfo& ei, std::vector< Node >& exp ) { + if( ei.d_enum_slave.size()==1 ){ + // this check whether the example evaluates to something that is larger than the output + // if so, then this term is never useful when using a concatenation strategy + if( ei.d_role==enum_concat_term ){ + if( Trace.isOn("sygus-pbe-cterm-debug") ){ + Trace("sygus-pbe-enum") << std::endl; + } + + // check if all examples had longer length that the output + std::map< Node, std::vector< Node > >::iterator itxo = d_examples_out.find( c ); + Assert( itxo!=d_examples_out.end() ); + Assert( itxo->second.size()==results.size() ); + Trace("sygus-pbe-cterm-debug") << "Check enumerator exclusion for " << x << " -> " << d_tds->sygusToBuiltin( v ) << " based on containment." << std::endl; + std::vector< unsigned > cmp_indices; + for( unsigned i=0; i<results.size(); i++ ){ + Assert( results[i].isConst() ); + Assert( itxo->second[i].isConst() ); + /* + unsigned vlen = results[i].getConst<String>().size(); + unsigned xlen = itxo->second[i].getConst<String>().size(); + Trace("sygus-pbe-cterm-debug") << " " << results[i] << " <> " << itxo->second[i]; + int index = vlen>xlen ? 1 : ( vlen<xlen ? -1 : 0 ); + Trace("sygus-pbe-cterm-debug") << "..." << index << std::endl; + cmp_indices[index].push_back( i ); + */ + Trace("sygus-pbe-cterm-debug") << " " << results[i] << " <> " << itxo->second[i]; + Node cont = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, itxo->second[i], results[i] ); + Node contr = Rewriter::rewrite( cont ); + if( contr==d_false ){ + cmp_indices.push_back( i ); + Trace("sygus-pbe-cterm-debug") << "...not contained." << std::endl; + }else{ + Trace("sygus-pbe-cterm-debug") << "...contained." << std::endl; + } + } + // TODO : stronger requirement if we incorporate ITE + CONCAT mixed strategy : must be longer than *all* examples + if( !cmp_indices.empty() ){ + //set up the inclusion set + NegContainsSygusInvarianceTest ncset; + ncset.init( d_tds, c, itxo->second, cmp_indices ); + d_tds->getExplanationFor( x, v, exp, ncset ); + Trace("sygus-pbe-cterm") << "PBE-cterm : enumerator exclude " << d_tds->sygusToBuiltin( v ) << " due to negative containment." << std::endl; + return true; + } + } + } + return false; +} + + + +void CegConjecturePbe::EnumInfo::addEnumValue( CegConjecturePbe * pbe, Node v, std::vector< Node >& results ) { + d_enum_val_to_index[v] = d_enum_vals.size(); + d_enum_vals.push_back( v ); + d_enum_vals_res.push_back( results ); + /* + if( d_role==enum_io ){ + // compute + if( d_enum_total.empty() ){ + d_enum_total = results; + }else if( !d_enum_total_true ){ + d_enum_total_true = true; + Assert( d_enum_total.size()==results.size() ); + for( unsigned i=0; i<results.size(); i++ ){ + if( d_enum_total[i]==pbe->d_true || results[i]==pbe->d_true ){ + d_enum_total[i] = pbe->d_true; + }else{ + d_enum_total[i] = pbe->d_false; + d_enum_total_true = false; + } + } + } + } + */ +} + +void CegConjecturePbe::EnumInfo::setSolved( Node slv ) { + d_enum_solved = slv; + //d_enum_total_true = true; +} + +void CegConjecturePbe::CandidateInfo::initialize( Node c ) { + d_this_candidate = c; + d_root = c.getType(); +} + +void CegConjecturePbe::CandidateInfo::initializeType( TypeNode tn ) { + d_tinfo[tn].d_this_type = tn; + d_tinfo[tn].d_parent = this; +} + +Node CegConjecturePbe::CandidateInfo::getRootEnumerator() { + std::map< unsigned, Node >::iterator it = d_tinfo[d_root].d_enum.find( enum_io ); + Assert( it!=d_tinfo[d_root].d_enum.end() ); + return it->second; +} + +bool CegConjecturePbe::CandidateInfo::isNonTrivial() { + //TODO + return true; +} + +// status : 0 : exact, -1 : vals is subset, 1 : vals is superset +Node CegConjecturePbe::SubsumeTrie::addTermInternal( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, + std::vector< Node >& subsumed, bool spol, IndexFilter * f, + unsigned index, int status, bool checkExistsOnly, bool checkSubsume ) { + if( index==vals.size() ){ + if( status==0 ){ + // set the term if checkExistsOnly = false + if( d_term.isNull() && !checkExistsOnly ){ + d_term = t; + } + }else if( status==1 ){ + Assert( checkSubsume ); + // found a subsumed term + if( !d_term.isNull() ){ + subsumed.push_back( d_term ); + if( !checkExistsOnly ){ + // remove it if checkExistsOnly = false + d_term = Node::null(); + } + } + }else{ + Assert( !checkExistsOnly && checkSubsume ); + } + return d_term; + }else{ + // the current value + Assert( pol || ( vals[index].isConst() && vals[index].getType().isBoolean() ) ); + Node cv = pol ? vals[index] : ( vals[index]==pbe->d_true ? pbe->d_false : pbe->d_true ); + // if checkExistsOnly = false, check if the current value is subsumed if checkSubsume = true, if so, don't add + if( !checkExistsOnly && checkSubsume ){ + std::vector< bool > check_subsumed_by; + if( status==0 ){ + if( cv==pbe->d_false ){ + check_subsumed_by.push_back( spol ); + } + }else if( status==-1 ){ + check_subsumed_by.push_back( spol ); + if( cv==pbe->d_false ){ + check_subsumed_by.push_back( !spol ); + } + } + // check for subsumed nodes + for( unsigned i=0; i<check_subsumed_by.size(); i++ ){ + Node csval = check_subsumed_by[i] ? pbe->d_true : pbe->d_false; + // check if subsumed + std::map< Node, SubsumeTrie >::iterator itc = d_children.find( csval ); + if( itc!=d_children.end() ){ + unsigned next_index = f ? f->next( index ) : index+1; + Node ret = itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, -1, checkExistsOnly, checkSubsume ); + // ret subsumes t + if( !ret.isNull() ){ + return ret; + } + } + } + } + Node ret; + std::vector< bool > check_subsume; + if( status==0 ){ + unsigned next_index = f ? f->next( index ) : index+1; + if( checkExistsOnly ){ + std::map< Node, SubsumeTrie >::iterator itc = d_children.find( cv ); + if( itc!=d_children.end() ){ + ret = itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 0, checkExistsOnly, checkSubsume ); + } + }else{ + Assert( spol ); + ret = d_children[cv].addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 0, checkExistsOnly, checkSubsume ); + if( ret!=t ){ + // we were subsumed by ret, return + return ret; + } + } + if( checkSubsume ){ + // check for subsuming + if( cv==pbe->d_true ){ + check_subsume.push_back( !spol ); + } + } + }else if( status==1 ){ + Assert( checkSubsume ); + check_subsume.push_back( !spol ); + if( cv==pbe->d_true ){ + check_subsume.push_back( spol ); + } + } + if( checkSubsume ){ + // check for subsumed terms + for( unsigned i=0; i<check_subsume.size(); i++ ){ + Node csval = check_subsume[i] ? pbe->d_true : pbe->d_false; + std::map< Node, SubsumeTrie >::iterator itc = d_children.find( csval ); + if( itc!=d_children.end() ){ + unsigned next_index = f ? f->next( index ) : index+1; + itc->second.addTermInternal( pbe, t, vals, pol, subsumed, spol, f, next_index, 1, checkExistsOnly, checkSubsume ); + // clean up + if( itc->second.isEmpty() ){ + Assert( !checkExistsOnly ); + d_children.erase( csval ); + } + } + } + } + return ret; + } +} + +Node CegConjecturePbe::SubsumeTrie::addTerm( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f ) { + unsigned start_index = f ? f->start() : 0; + return addTermInternal( pbe, t, vals, pol, subsumed, true, f, start_index, 0, false, true ); +} + +Node CegConjecturePbe::SubsumeTrie::addCond( CegConjecturePbe * pbe, Node c, std::vector< Node >& vals, bool pol, IndexFilter * f ) { + unsigned start_index = f ? f->start() : 0; + std::vector< Node > subsumed; + return addTermInternal( pbe, c, vals, pol, subsumed, true, f, start_index, 0, false, false ); +} + +void CegConjecturePbe::SubsumeTrie::getSubsumed( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f ){ + unsigned start_index = f ? f->start() : 0; + addTermInternal( pbe, Node::null(), vals, pol, subsumed, true, f, start_index, 1, true, true ); +} + +void CegConjecturePbe::SubsumeTrie::getSubsumedBy( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed_by, IndexFilter * f ){ + // flip polarities + unsigned start_index = f ? f->start() : 0; + addTermInternal( pbe, Node::null(), vals, !pol, subsumed_by, false, f, start_index, 1, true, true ); +} + +void CegConjecturePbe::SubsumeTrie::getLeavesInternal( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, + IndexFilter * f, unsigned index, int status ) { + if( index==vals.size() ){ + Assert( !d_term.isNull() ); + Assert( std::find( v[status].begin(), v[status].end(), d_term )==v[status].end() ); + v[status].push_back( d_term ); + }else{ + Assert( vals[index].isConst() && vals[index].getType().isBoolean() ); + // filter should be for cv + Assert( f==NULL || vals[index]==( pol ? pbe->d_true : pbe->d_false ) ); + for( std::map< Node, SubsumeTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + int new_status = status; + // if the current value is true + if( vals[index]==( pol ? pbe->d_true : pbe->d_false ) ){ + if( status!=0 ){ + new_status = ( it->first == pbe->d_true ? 1 : -1 ); + if( status!=-2 && new_status!=status ){ + new_status = 0; + } + } + } + unsigned next_index = f ? f->next( index ) : index+1; + it->second.getLeavesInternal( pbe, vals, pol, v, f, next_index, new_status ); + } + } +} + +void CegConjecturePbe::SubsumeTrie::getLeaves( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f ) { + unsigned start_index = f ? f->start() : 0; + getLeavesInternal( pbe, vals, pol, v, f, start_index, -2 ); +} + +void CegConjecturePbe::IndexFilter::mk( std::vector< Node >& vals, bool pol ) { + Trace("sygus-pbe-debug") << "Make for : "; + print_val( "sygus-pbe-debug", vals, pol ); + Trace("sygus-pbe-debug") << std::endl; + Node poln = NodeManager::currentNM()->mkConst( pol ); + + unsigned curr_index = 0; + while( curr_index<vals.size() && vals[curr_index]!=poln ){ + curr_index++; + } + d_next[0] = curr_index; + Trace("sygus-pbe-debug") << "0 -> " << curr_index << std::endl; + unsigned i = curr_index; + while( i<vals.size() ){ + while( i<vals.size() && vals[i]!=poln ){ + i++; + } + i++; + d_next[curr_index+1] = i; + Trace("sygus-pbe-debug") << curr_index+1 << " -> " << i << std::endl; + curr_index = i; + } + + // verify it is correct + unsigned j = start(); + for( unsigned k=0; k<j; k++ ){ + AlwaysAssert( vals[k]!=poln ); + } + Trace("sygus-pbe-debug") << "...start : " << j << std::endl; + unsigned counter = 0; + while( j<vals.size() ){ + Trace("sygus-pbe-debug") << "...at : " << j << std::endl; + AlwaysAssert( vals[j]==poln ); + unsigned jj = next( j ); + AlwaysAssert( jj>j ); + for( unsigned k=(j+1); k<jj; k++ ){ + AlwaysAssert( vals[k]!=poln ); + } + AlwaysAssert( counter<=vals.size() ); + counter++; + j = jj; + } + + +} + +unsigned CegConjecturePbe::IndexFilter::start() { + std::map< unsigned, unsigned >::iterator it = d_next.find( 0 ); + if( it==d_next.end() ){ + return 0; + }else{ + return it->second; + } +} + +unsigned CegConjecturePbe::IndexFilter::next( unsigned i ) { + std::map< unsigned, unsigned >::iterator it = d_next.find( i+1 ); + if( it==d_next.end() ){ + return i+1; + }else{ + return it->second; + } +} + +bool CegConjecturePbe::IndexFilter::isEq( std::vector< Node >& vals, Node v ) { + unsigned index = start(); + while( index<vals.size() ){ + if( vals[index]!=v ){ + return false; + } + index = next( index ); + } + return true; +} + +Node CegConjecturePbe::constructSolution( Node c ){ + std::map< Node, CandidateInfo >::iterator itc = d_cinfo.find( c ); + Assert( itc!=d_cinfo.end() ); + if( !itc->second.d_solution.isNull() ){ + // already has a solution + return itc->second.d_solution; + }else{ + // only check if an enumerator updated + if( itc->second.d_check_sol ){ + Trace("sygus-pbe") << "Construct solution, #iterations = " << itc->second.d_cond_count << std::endl; + itc->second.d_check_sol = false; + // try multiple times if we have done multiple conditions, due to non-determinism + Node vc; + for( unsigned i=0; i<=itc->second.d_cond_count; i++ ){ + Trace("sygus-pbe-dt") << "ConstructPBE for candidate: " << c << std::endl; + Node e = itc->second.getRootEnumerator(); + UnifContext x; + x.initialize( this, c ); + Node vcc = constructSolution( c, e, x, 1 ); + if( !vcc.isNull() ){ + if( vc.isNull() || ( !vc.isNull() && d_tds->getSygusTermSize( vcc )<d_tds->getSygusTermSize( vc ) ) ){ + Trace("sygus-pbe") << "**** PBE SOLVED : " << c << " = " << vcc << std::endl; + Trace("sygus-pbe") << "...solved at iteration " << i << std::endl; + vc = vcc; + } + } + } + if( !vc.isNull() ){ + itc->second.d_solution = vc; + return vc; + } + Trace("sygus-pbe") << "...failed to solve." << std::endl; + } + return Node::null(); + } +} + +Node CegConjecturePbe::constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x ){ + Assert( !solved.empty() ); + // TODO + return solved[0]; +} + +Node CegConjecturePbe::constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x ) { + Assert( !solved.empty() ); + // TODO + return solved[0]; +} + +Node CegConjecturePbe::constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x ){ + Assert( !solved.empty() ); + // TODO + return solved[0]; +} + +Node CegConjecturePbe::constructBestConditional( std::vector< Node >& conds, UnifContext& x ) { + Assert( !conds.empty() ); + // TODO + double r = (double)(rand())/((double)(RAND_MAX)); + unsigned cindex = r*conds.size(); + if( cindex>conds.size() ){ + cindex = conds.size() - 1; + } + return conds[cindex]; +} + +Node CegConjecturePbe::constructBestStringToConcat( std::vector< Node > strs, + std::map< Node, unsigned > total_inc, + std::map< Node, std::vector< unsigned > > incr, + UnifContext& x ) { + Assert( !strs.empty() ); + // TODO + double r = (double)(rand())/((double)(RAND_MAX)); + unsigned cindex = r*strs.size(); + if( cindex>strs.size() ){ + cindex = strs.size() - 1; + } + return strs[cindex]; +} + +Node CegConjecturePbe::constructSolution( Node c, Node e, UnifContext& x, int ind ) { + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << "ConstructPBE: enum: " << e << " in context "; + print_val("sygus-pbe-dt-debug", x.d_vals); + Trace("sygus-pbe-dt-debug") << std::endl; + std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e ); + Assert( itn!=d_einfo.end() ); + Node ret_dt; + if( itn->second.d_role==enum_any ){ + indent("sygus-pbe-dt", ind); + ret_dt = constructBestSolvedTerm( itn->second.d_enum_vals, x ); + Trace("sygus-pbe-dt") << "return PBE: success : use any " << d_tds->sygusToBuiltin( ret_dt ) << std::endl; + Assert( !ret_dt.isNull() ); + }else if( itn->second.d_role==enum_io && !x.isReturnValueModified() && itn->second.isSolved() ){ + // this type has a complete solution + ret_dt = itn->second.getSolved(); + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "return PBE: success : solved " << d_tds->sygusToBuiltin( ret_dt ) << std::endl; + Assert( !ret_dt.isNull() ); + }else{ + TypeNode etn = e.getType(); + std::map< TypeNode, EnumTypeInfo >::iterator itt = d_cinfo[c].d_tinfo.find( etn ); + Assert( itt!=d_cinfo[c].d_tinfo.end() ); + if( d_tds->sygusToBuiltinType( e.getType() ).isString() ){ + // check if a current value that closes all examples + + // get the term enumerator for this type + bool success = true; + std::map< Node, EnumInfo >::iterator itet; + if( itn->second.d_role==enum_concat_term ){ + itet = itn; + }else{ + std::map< unsigned, Node >::iterator itnt = itt->second.d_enum.find( enum_concat_term ); + if( itnt != itt->second.d_enum.end() ){ + Node et = itnt->second; + itet = d_einfo.find( et ); + }else{ + success = false; + } + } + if( success ){ + Assert( itet!=d_einfo.end() ); + + // get the current examples + std::map< Node, std::vector< Node > >::iterator itx = d_examples_out.find( c ); + Assert( itx!=d_examples_out.end() ); + std::vector< CVC4::String > ex_vals; + x.getCurrentStrings( this, itx->second, ex_vals ); + Assert( itn->second.d_enum_vals.size()==itn->second.d_enum_vals_res.size() ); + + // test each example in the term enumerator for the type + std::vector< Node > str_solved; + for( unsigned i=0; i<itet->second.d_enum_vals.size(); i++ ){ + if( x.isStringSolved( this, ex_vals, itet->second.d_enum_vals_res[i] ) ){ + str_solved.push_back( itet->second.d_enum_vals[i] ); + } + } + if( !str_solved.empty() ){ + ret_dt = constructBestStringSolvedTerm( str_solved, x ); + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "return PBE: success : string solved " << d_tds->sygusToBuiltin( ret_dt ) << std::endl; + }else{ + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << " ...not currently string solved." << std::endl; + } + } + }else if( itn->second.d_role==enum_io && !x.isReturnValueModified() ){ + // it has an enumerated value that is conditionally correct under the current assumptions + std::vector< Node > subsumed_by; + itn->second.d_term_trie.getSubsumedBy( this, x.d_vals, true, subsumed_by ); + if( !subsumed_by.empty() ){ + ret_dt = constructBestSolvedTerm( subsumed_by, x ); + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "return PBE: success : conditionally solved" << d_tds->sygusToBuiltin( ret_dt ) << std::endl; + }else{ + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << " ...not currently conditionally solved." << std::endl; + } + } + if( ret_dt.isNull() ){ + if( !itn->second.isTemplated() ){ + // try to construct a compound solution, if strategies are available + + // do various strategies + for( std::map< Node, EnumTypeInfoStrat >::iterator itts = itt->second.d_strat.begin(); itts!=itt->second.d_strat.end(); ++itts ){ + std::map< unsigned, Node > dt_children_cons; + unsigned strat = itts->second.d_this; + + bool success = true; + + // for ITE + std::map< unsigned, Node > look_ahead_solved_children; + Node split_cond_enum; + int split_cond_res_index = -1; + + //for CONCAT + Node incr_val; + int incr_type = 0; + std::map< Node, std::vector< unsigned > > incr; + + // construct the child order + std::vector< unsigned > corder; + if( strat==strat_CONCAT ){ + for( unsigned r=0; r<2; r++ ){ + unsigned sc = r==0 ? 0 : itts->second.d_cenum.size()-1; + Node ce = itts->second.d_cenum[sc]; + if( ce.getType()==etn ){ + // prefer simple recursion (self type) + Assert( d_einfo.find( ce )!=d_einfo.end() ); + Assert( d_einfo[ce].d_role==enum_concat_term ); + corder.push_back( sc ); + unsigned inc = r==0 ? 1 : -1; + unsigned scc = sc + inc; + while( scc>=0 && scc<itts->second.d_cenum.size() ){ + corder.push_back( scc ); + scc = scc + inc; + } + break; + } + } + }else{ + for( unsigned sc=0; sc<itts->second.d_cenum.size(); sc++ ){ + corder.push_back( sc ); + } + } + Assert( corder.size()==itts->second.d_cenum.size() ); + + + for( unsigned scc=0; scc<corder.size(); scc++ ){ + unsigned sc = corder[scc]; + Node rec_c; + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "construct PBE child #" << sc << "..." << std::endl; + std::map< unsigned, Node >::iterator itla = look_ahead_solved_children.find( sc ); + if( itla!=look_ahead_solved_children.end() ){ + rec_c = itla->second; + indent("sygus-pbe-dt-debug", ind+1); + Trace("sygus-pbe-dt-debug") << "ConstructPBE: look ahead solved : " << d_tds->sygusToBuiltin( rec_c ) << std::endl; + }else{ + // get the child enumerator + Node ce = itts->second.d_cenum[sc]; + if( strat==strat_ITE && scc==0 ){ + Assert( itts->second.d_cenum.size()==3 ); // for now, fix to 3 child ITEs + // choose a condition + + // register the condition enumerator + std::map< Node, EnumInfo >::iterator itnc = d_einfo.find( ce ); + Assert( itnc!=d_einfo.end() ); + // only used if the return value is not modified + if( !x.isReturnValueModified() ){ + if( x.d_uinfo.find( ce )==x.d_uinfo.end() ){ + Trace("sygus-pbe-dt-debug2") << " reg : PBE: Look for direct solutions for conditional enumerator " << ce << " ... " << std::endl; + x.d_uinfo[ce].d_status = 0; + Assert( itnc->second.d_enum_vals.size()==itnc->second.d_enum_vals_res.size() ); + for( unsigned i=1; i<=2; i++ ){ + Node te = itts->second.d_cenum[i]; + std::map< Node, EnumInfo >::iterator itnt = d_einfo.find( te ); + Assert( itnt!=d_einfo.end() ); + bool branch_pol = ( i==1 ); + // for each condition, get terms that satisfy it in this branch + for( unsigned k=0; k<itnc->second.d_enum_vals.size(); k++ ){ + Node cond = itnc->second.d_enum_vals[k]; + std::vector< Node > solved; + itnt->second.d_term_trie.getSubsumedBy( this, itnc->second.d_enum_vals_res[k], branch_pol, solved ); + Trace("sygus-pbe-dt-debug2") << " reg : PBE: " << d_tds->sygusToBuiltin( cond ) << " has " << solved.size() << " solutions in branch " << i << std::endl; + if( !solved.empty() ){ + Node slv = constructBestSolvedTerm( solved, x ); + Trace("sygus-pbe-dt-debug") << " reg : PBE: ..." << d_tds->sygusToBuiltin( slv ) << " is a solution under branch " << i; + Trace("sygus-pbe-dt-debug") << " of condition " << d_tds->sygusToBuiltin( cond ) << std::endl; + x.d_uinfo[ce].d_look_ahead_sols[cond][i] = slv; + } + } + } + } + } + + // get the conditionals in the current context : they must be distinguishable + std::map< int, std::vector< Node > > possible_cond; + std::map< Node, int > solved_cond; //stores branch + itnc->second.d_term_trie.getLeaves( this, x.d_vals, true, possible_cond ); + + std::map< int, std::vector< Node > >::iterator itpc = possible_cond.find( 0 ); + if( itpc!=possible_cond.end() ){ + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << "PBE : We have " << itpc->second.size() << " distinguishable conditionals:" << std::endl; + for( unsigned k=0; k<itpc->second.size(); k++ ){ + indent("sygus-pbe-dt-debug", ind+1); + Trace("sygus-pbe-dt-debug") << d_tds->sygusToBuiltin( itpc->second[k] ) << std::endl; + } + + + // static look ahead conditional : choose conditionals that have solved terms in at least one branch + // only applicable if we have not modified the return value + std::map< int, std::vector< Node > > solved_cond; + if( !x.isReturnValueModified() ){ + Assert( x.d_uinfo.find( ce )!=x.d_uinfo.end() ); + int solve_max = 0; + for( unsigned k=0; k<itpc->second.size(); k++ ){ + Node cond = itpc->second[k]; + std::map< Node, std::map< unsigned, Node > >::iterator itla = x.d_uinfo[ce].d_look_ahead_sols.find( cond ); + if( itla!=x.d_uinfo[ce].d_look_ahead_sols.end() ){ + int nsolved = itla->second.size(); + solve_max = nsolved > solve_max ? nsolved : solve_max; + solved_cond[nsolved].push_back( cond ); + } + } + int n = solve_max; + while( n>0 ){ + if( !solved_cond[n].empty() ){ + rec_c = constructBestSolvedConditional( solved_cond[n], x ); + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "PBE: ITE strategy : choose solved conditional " << d_tds->sygusToBuiltin( rec_c ) << " with " << n << " solved children..." << std::endl; + std::map< Node, std::map< unsigned, Node > >::iterator itla = x.d_uinfo[ce].d_look_ahead_sols.find( rec_c ); + Assert( itla!=x.d_uinfo[ce].d_look_ahead_sols.end() ); + for( std::map< unsigned, Node >::iterator itla2 = itla->second.begin(); itla2 != itla->second.end(); ++itla2 ){ + look_ahead_solved_children[ itla2->first ] = itla2->second; + } + break; + } + n--; + } + } + + // dynamic look ahead conditional : compute if there are any solved terms in this branch TODO + if( ind>0 ){ + + } + + // otherwise, guess a conditional + if( rec_c.isNull() ){ + rec_c = constructBestConditional( itpc->second, x ); + Assert( !rec_c.isNull() ); + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "PBE: ITE strategy : choose random conditional " << d_tds->sygusToBuiltin( rec_c ) << std::endl; + } + }else{ + // TODO : degenerate case where children have different types + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "return PBE: failed ITE strategy, cannot find a distinguishable condition" << std::endl; + } + if( !rec_c.isNull() ){ + Assert( itnc->second.d_enum_val_to_index.find( rec_c )!=itnc->second.d_enum_val_to_index.end() ); + split_cond_res_index = itnc->second.d_enum_val_to_index[rec_c]; + split_cond_enum = ce; + Assert( split_cond_res_index>=0 ); + Assert( split_cond_res_index<(int)itnc->second.d_enum_vals_res.size() ); + } + }else if( strat==strat_CONCAT && scc==0 ){ + std::map< Node, EnumInfo >::iterator itsc = d_einfo.find( ce ); + Assert( itsc!=d_einfo.end() ); + // ensured by the child order we set above + Assert( itsc->second.d_role==enum_concat_term ); + // check if each return value is a prefix/suffix of all open examples + incr_type = sc==0 ? -1 : 1; + if( x.d_has_string_pos==0 || x.d_has_string_pos==incr_type ){ + bool isPrefix = incr_type==-1; + std::map< Node, unsigned > total_inc; + std::vector< Node > inc_strs; + std::map< Node, std::vector< Node > >::iterator itx = d_examples_out.find( c ); + Assert( itx!=d_examples_out.end() ); + // make the value of the examples + std::vector< CVC4::String > ex_vals; + x.getCurrentStrings( this, itx->second, ex_vals ); + + // check if there is a value for which is a prefix/suffix of all active examples + Assert( itsc->second.d_enum_vals.size()==itsc->second.d_enum_vals_res.size() ); + + for( unsigned i=0; i<itsc->second.d_enum_vals.size(); i++ ){ + Node val_t = itsc->second.d_enum_vals[i]; + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << "increment string values : " << val_t << " : "; + Assert( itsc->second.d_enum_vals_res[i].size()==itx->second.size() ); + unsigned tot = 0; + bool exsuccess = x.getStringIncrement( this, isPrefix, ex_vals, itsc->second.d_enum_vals_res[i], incr[val_t], tot ); + if( tot==0 ){ + exsuccess = false; + } + if( !exsuccess ){ + incr.erase( val_t ); + Trace("sygus-pbe-dt-debug") << "...fail" << std::endl; + }else{ + total_inc[ val_t ] = tot; + inc_strs.push_back( val_t ); + Trace("sygus-pbe-dt-debug") << "...success, total increment = " << tot << std::endl; + } + } + + if( !incr.empty() ){ + rec_c = constructBestStringToConcat( inc_strs, total_inc, incr, x ); + incr_val = rec_c; + Assert( !rec_c.isNull() ); + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "PBE: CONCAT strategy : choose " << ( isPrefix ? "pre" : "suf" ) << "fix value " << d_tds->sygusToBuiltin( rec_c ) << std::endl; + }else{ + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "PBE: failed CONCAT strategy, no values are " << ( isPrefix ? "pre" : "suf" ) << "fix of all examples." << std::endl; + } + }else{ + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "PBE: failed CONCAT strategy, prefix/suffix mismatch." << std::endl; + } + }else{ + // a standard term + + // store previous values + std::vector< Node > prev; + std::vector< unsigned > prev_str_pos; + int prev_has_str_pos = false; + // update the context + bool ret = false; + if( strat==strat_ITE ){ + std::map< Node, EnumInfo >::iterator itnc = d_einfo.find( split_cond_enum ); + Assert( itnc!=d_einfo.end() ); + Assert( split_cond_res_index>=0 ); + Assert( split_cond_res_index<(int)itnc->second.d_enum_vals_res.size() ); + prev = x.d_vals; + ret = x.updateContext( this, itnc->second.d_enum_vals_res[split_cond_res_index], sc==1 ); + }else if( strat==strat_CONCAT ){ + prev_str_pos = x.d_str_pos; + prev_has_str_pos = x.d_has_string_pos; + Assert( incr.find( incr_val )!=incr.end() ); + ret = x.updateStringPosition( this, incr[incr_val] ); + x.d_has_string_pos = incr_type; + }else if( strat==strat_ID ){ + ret = true; + } + // must have updated the context + AlwaysAssert( ret ); + // recurse + rec_c = constructSolution( c, ce, x, ind+1 ); + if( !rec_c.isNull() ){ + //revert context + if( strat==strat_ITE ){ + x.d_vals = prev; + }else if( strat==strat_CONCAT ){ + x.d_str_pos = prev_str_pos; + x.d_has_string_pos = prev_has_str_pos; + } + } + } + } + if( !rec_c.isNull() ){ + dt_children_cons[sc] = rec_c; + }else{ + success = false; + break; + } + } + if( success ){ + std::vector< Node > dt_children; + Assert( !itts->first.isNull() ); + dt_children.push_back( itts->first ); + for( unsigned sc=0; sc<itts->second.d_cenum.size(); sc++ ){ + std::map< unsigned, Node >::iterator itdc = dt_children_cons.find( sc ); + Assert( itdc!=dt_children_cons.end() ); + dt_children.push_back( itdc->second ); + } + ret_dt = NodeManager::currentNM()->mkNode( kind::APPLY_CONSTRUCTOR, dt_children ); + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << "PBE: success : constructed for strategy "; + print_strat( "sygus-pbe-dt-debug", strat ); + Trace("sygus-pbe-dt-debug") << std::endl; + break; + }else{ + indent("sygus-pbe-dt-debug", ind); + Trace("sygus-pbe-dt-debug") << "PBE: failed for strategy "; + print_strat( "sygus-pbe-dt-debug", strat ); + Trace("sygus-pbe-dt-debug") << std::endl; + } + } + if( ret_dt.isNull() ){ + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "return PBE: fail : all strategies failed " << std::endl; + } + }else{ + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "return PBE: fail : non-basic" << std::endl; + } + } + } + if( !ret_dt.isNull() ){ + Assert( ret_dt.getType()==e.getType() ); + } + indent("sygus-pbe-dt", ind); + Trace("sygus-pbe-dt") << "ConstructPBE: returned " << ret_dt << std::endl; + return ret_dt; +} + +bool CegConjecturePbe::UnifContext::updateContext( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol ) { + Assert( d_vals.size()==vals.size() ); + bool changed = false; + Node poln = pol ? pbe->d_true : pbe->d_false; + for( unsigned i=0; i<vals.size(); i++ ){ + if( vals[i]!=poln ){ + if( d_vals[i]==pbe->d_true ){ + d_vals[i] = pbe->d_false; + changed = true; + } + } + } + return changed; +} + +bool CegConjecturePbe::UnifContext::updateStringPosition( CegConjecturePbe * pbe, std::vector< unsigned >& pos ) { + Assert( pos.size()==d_str_pos.size() ); + bool changed = false; + for( unsigned i=0; i<pos.size(); i++ ){ + if( pos[i]>0 ){ + d_str_pos[i] += pos[i]; + changed = true; + } + } + return changed; +} + +bool CegConjecturePbe::UnifContext::isReturnValueModified() { + if( d_has_string_pos!=0 ){ + return true; + } + return false; +} + +void CegConjecturePbe::UnifContext::initialize( CegConjecturePbe * pbe, Node c ) { + Assert( d_vals.empty() ); + Assert( d_str_pos.empty() ); + + // initialize with #examples + Assert( pbe->d_examples.find( c )!=pbe->d_examples.end() ); + unsigned sz = pbe->d_examples[c].size(); + for( unsigned i=0; i<sz; i++ ){ + d_vals.push_back( pbe->d_true ); + } + + if( !pbe->d_examples_out[c].empty() ){ + // output type of the examples + TypeNode exotn = pbe->d_examples_out[c][0].getType(); + + if( exotn.isString() ){ + for( unsigned i=0; i<sz; i++ ){ + d_str_pos.push_back( 0 ); + } + } + } +} + + +void CegConjecturePbe::UnifContext::getCurrentStrings( CegConjecturePbe * pbe, std::vector< Node >& vals, std::vector< CVC4::String >& ex_vals ) { + bool isPrefix = d_has_string_pos==-1; + CVC4::String dummy; + for( unsigned i=0; i<vals.size(); i++ ){ + if( d_vals[i]==pbe->d_true ){ + Assert( vals[i].isConst() ); + unsigned pos_value = d_str_pos[i]; + if( pos_value>0 ){ + Assert( d_has_string_pos!=0 ); + CVC4::String s = vals[i].getConst<String>(); + Assert( pos_value<=s.size() ); + ex_vals.push_back( isPrefix ? s.suffix( s.size()-pos_value ) : + s.prefix( s.size()-pos_value ) ); + }else{ + ex_vals.push_back( vals[i].getConst<String>() ); + } + }else{ + // irrelevant, add dummy + ex_vals.push_back( dummy ); + } + } +} + +bool CegConjecturePbe::UnifContext::getStringIncrement( CegConjecturePbe * pbe, bool isPrefix, std::vector< CVC4::String >& ex_vals, std::vector< Node >& vals, std::vector< unsigned >& inc, unsigned& tot ) { + for( unsigned j=0; j<vals.size(); j++ ){ + unsigned ival = 0; + if( d_vals[j]==pbe->d_true ){ + // example is active in this context + Assert( vals[j].isConst() ); + CVC4::String mystr = vals[j].getConst<String>(); + ival = mystr.size(); + if( mystr.size()<=ex_vals[j].size() ){ + if( !( isPrefix ? ex_vals[j].strncmp(mystr, ival) : ex_vals[j].rstrncmp(mystr, ival) ) ){ + Trace("sygus-pbe-dt-debug") << "X"; + return false; + } + }else{ + Trace("sygus-pbe-dt-debug") << "X"; + return false; + } + } + Trace("sygus-pbe-dt-debug") << ival; + tot += ival; + inc.push_back( ival ); + } + return true; +} +bool CegConjecturePbe::UnifContext::isStringSolved( CegConjecturePbe * pbe, std::vector< CVC4::String >& ex_vals, std::vector< Node >& vals ) { + for( unsigned j=0; j<vals.size(); j++ ){ + if( d_vals[j]==pbe->d_true ){ + // example is active in this context + Assert( vals[j].isConst() ); + CVC4::String mystr = vals[j].getConst<String>(); + if( ex_vals[j]!=mystr ){ + return false; + } + } + } + return true; +} + +int CegConjecturePbe::getGuardStatus( Node g ) { + bool value; + if( d_qe->getValuation().hasSatValue( g, value ) ) { + if( value ){ + return 1; + }else{ + return -1; + } + }else{ + return 0; + } +} + +} diff --git a/src/theory/quantifiers/ce_guided_pbe.h b/src/theory/quantifiers/ce_guided_pbe.h new file mode 100644 index 000000000..f40f29b2a --- /dev/null +++ b/src/theory/quantifiers/ce_guided_pbe.h @@ -0,0 +1,273 @@ +/********************* */ +/*! \file ce_guided_pbe.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2016 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 utility for processing programming by examples synthesis conjectures + **/ + +#include "cvc4_private.h" + +#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_PBE_H +#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_PBE_H + +#include "context/cdhashmap.h" +#include "context/cdchunk_list.h" +#include "theory/quantifiers_engine.h" + +namespace CVC4 { +namespace theory { +namespace quantifiers { + +class CegConjecture; +class CegConjecturePbe; +class CegEntailmentInfer; + +class CegConjecturePbe { +private: + QuantifiersEngine* d_qe; + quantifiers::TermDbSygus * d_tds; + CegConjecture* d_parent; + + std::map< Node, bool > d_examples_invalid; + std::map< Node, bool > d_examples_out_invalid; + std::map< Node, std::vector< std::vector< Node > > > d_examples; + std::map< Node, std::vector< Node > > d_examples_out; + std::map< Node, std::vector< Node > > d_examples_term; + + void collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol ); + bool d_is_pbe; +public: + Node d_true; + Node d_false; + enum { + enum_io, + enum_ite_condition, + enum_concat_term, + enum_any, + }; + enum { + strat_ITE, + strat_CONCAT, + strat_ID, + }; +public: + CegConjecturePbe( QuantifiersEngine * qe, CegConjecture * p ); + ~CegConjecturePbe(); + + void initialize( Node n, std::vector< Node >& candidates, std::vector< Node >& lemmas ); + bool getPbeExamples( Node v, std::vector< std::vector< Node > >& exs, + std::vector< Node >& exos, std::vector< Node >& exts); + bool isPbe() { return d_is_pbe; } +private: // for registration + void collectEnumeratorTypes( Node c, TypeNode tn, unsigned enum_role ); + void registerEnumerator( Node et, Node c, TypeNode tn, unsigned enum_role, bool inSearch ); + void staticLearnRedundantOps( Node c, std::vector< Node >& lemmas ); + void staticLearnRedundantOps( Node c, Node e, std::map< Node, bool >& visited, std::vector< Node >& redundant, + std::vector< Node >& lemmas, int ind ); + + /** register candidate conditional */ + bool inferTemplate( unsigned k, Node n, std::map< Node, unsigned >& templ_var_index, std::map< unsigned, unsigned >& templ_injection ); + /** get guard status */ + int getGuardStatus( Node g ); +public: + class IndexFilter { + public: + IndexFilter(){} + void mk( std::vector< Node >& vals, bool pol = true ); + std::map< unsigned, unsigned > d_next; + unsigned start(); + unsigned next( unsigned i ); + void clear() { d_next.clear(); } + bool isEq( std::vector< Node >& vs, Node v ); + }; +private: + // subsumption trie + class SubsumeTrie { + private: + Node addTermInternal( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, bool spol, IndexFilter * f, + unsigned index, int status, bool checkExistsOnly, bool checkSubsume ); + public: + SubsumeTrie(){} + Node d_term; + std::map< Node, SubsumeTrie > d_children; + // adds term to the trie, removes based on subsumption + Node addTerm( CegConjecturePbe * pbe, Node t, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f = NULL ); + // adds condition to the trie (does not do subsumption) + Node addCond( CegConjecturePbe * pbe, Node c, std::vector< Node >& vals, bool pol, IndexFilter * f = NULL ); + // returns the set of terms that are subsets of vals + void getSubsumed( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed, IndexFilter * f = NULL ); + // returns the set of terms that are supersets of vals + void getSubsumedBy( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::vector< Node >& subsumed_by, IndexFilter * f = NULL ); + private: + void getLeavesInternal( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f, + unsigned index, int status ); + public: + // v[-1,1,0] -> children always false, always true, both + void getLeaves( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol, std::map< int, std::vector< Node > >& v, IndexFilter * f = NULL ); + public: + bool isEmpty() { return d_term.isNull() && d_children.empty(); } + void clear() { + d_term = Node::null(); + d_children.clear(); + } + }; + + class EnumInfo { + private: + /** an OR of all in d_enum_res */ + //std::vector< Node > d_enum_total; + //bool d_enum_total_true; + Node d_enum_solved; + public: + EnumInfo() : d_role( enum_io ){} + Node d_parent_candidate; + + // for template + Node d_template; + Node d_template_arg; + + // TODO : make private + unsigned d_role; + + Node d_active_guard; + std::vector< Node > d_enum_slave; + /** values we have enumerated */ + std::vector< Node > d_enum_vals; + /** this either stores the values of f( I ) for inputs + or the value of f( I ) = O if d_role==enum_io + */ + std::vector< std::vector< Node > > d_enum_vals_res; + std::vector< Node > d_enum_subsume; + std::map< Node, unsigned > d_enum_val_to_index; + SubsumeTrie d_term_trie; + public: + bool isTemplated() { return !d_template.isNull(); } + void addEnumValue( CegConjecturePbe * pbe, Node v, std::vector< Node >& results ); + void setSolved( Node slv ); + bool isSolved() { return !d_enum_solved.isNull(); } + Node getSolved() { return d_enum_solved; } + }; + std::map< Node, EnumInfo > d_einfo; +private: + class CandidateInfo; + class EnumTypeInfoStrat { + public: + unsigned d_this; + /** conditional solutions */ + std::vector< TypeNode > d_csol_cts; + std::vector< Node > d_cenum; + }; + class EnumTypeInfo { + public: + EnumTypeInfo() : d_parent( NULL ){} + CandidateInfo * d_parent; + // role -> _ + std::map< unsigned, Node > d_enum; + TypeNode d_this_type; + // strategies for enum_io role + std::map< Node, EnumTypeInfoStrat > d_strat; + bool isSolved( CegConjecturePbe * pbe ); + }; + class CandidateInfo { + public: + CandidateInfo() : d_check_sol( false ), d_cond_count( 0 ){} + Node d_this_candidate; + TypeNode d_root; + std::map< TypeNode, EnumTypeInfo > d_tinfo; + std::vector< Node > d_esym_list; + // role -> sygus type -> enumerator + std::map< TypeNode, Node > d_search_enum; + bool d_check_sol; + unsigned d_cond_count; + Node d_solution; + void initialize( Node c ); + void initializeType( TypeNode tn ); + Node getRootEnumerator(); + bool isNonTrivial(); + }; + // candidate -> sygus type -> info + std::map< Node, CandidateInfo > d_cinfo; + + /** add enumerated value */ + void addEnumeratedValue( Node x, Node v, std::vector< Node >& lems ); + bool getExplanationForEnumeratorExclude( Node c, Node x, Node v, std::vector< Node >& results, EnumInfo& ei, std::vector< Node >& exp ); + +private: + // filtering verion + /* + class FilterSubsumeTrie { + public: + SubsumeTrie d_trie; + IndexFilter d_filter; + Node addTerm( Node t, std::vector< bool >& vals, std::vector< Node >& subsumed, bool checkExistsOnly = false ){ + return d_trie.addTerm( t, vals, subsumed, &d_filter, d_filter.start(), checkExistsOnly ); + } + }; + */ + class UnifContext { + public: + UnifContext() : d_has_string_pos(0) {} + //IndexFilter d_filter; + // the value of the context conditional + std::vector< Node > d_vals; + // update the examples + bool updateContext( CegConjecturePbe * pbe, std::vector< Node >& vals, bool pol ); + // the position in the strings + std::vector< unsigned > d_str_pos; + // 0 : pos not modified, 1 : pos indicates suffix incremented, -1 : pos indicates prefix incremented + int d_has_string_pos; + // update the string examples + bool updateStringPosition( CegConjecturePbe * pbe, std::vector< unsigned >& pos ); + // is return value modified + bool isReturnValueModified(); + class UEnumInfo { + public: + UEnumInfo() : d_status(-1){} + int d_status; + // enum val -> polarity -> solved + std::map< Node, std::map< unsigned, Node > > d_look_ahead_sols; + }; + // enumerator -> info + std::map< Node, UEnumInfo > d_uinfo; + void initialize( CegConjecturePbe * pbe, Node c ); + void getCurrentStrings( CegConjecturePbe * pbe, std::vector< Node >& vals, std::vector< CVC4::String >& ex_vals ); + bool getStringIncrement( CegConjecturePbe * pbe, bool isPrefix, std::vector< CVC4::String >& ex_vals, + std::vector< Node >& vals, std::vector< unsigned >& inc, unsigned& tot ); + bool isStringSolved( CegConjecturePbe * pbe, std::vector< CVC4::String >& ex_vals, std::vector< Node >& vals ); + }; + /** construct solution */ + Node constructSolution( Node c ); + Node constructSolution( Node c, Node e, UnifContext& x, int ind ); + Node constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x ); + Node constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x ); + Node constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x ); + Node constructBestConditional( std::vector< Node >& conds, UnifContext& x ); + Node constructBestStringToConcat( std::vector< Node > strs, + std::map< Node, unsigned > total_inc, + std::map< Node, std::vector< unsigned > > incr, + UnifContext& x ); + void getStringIncrement( bool isPrefix, Node c, Node v, + std::map< Node, unsigned > total_inc, + std::map< Node, std::vector< unsigned > > incr ); +public: + void registerCandidates( std::vector< Node >& candidates ); + void getCandidateList( std::vector< Node >& candidates, std::vector< Node >& clist ); + // lems and candidate values are outputs + bool constructCandidates( std::vector< Node >& enums, std::vector< Node >& enum_values, + std::vector< Node >& candidates, std::vector< Node >& candidate_values, + std::vector< Node >& lems ); +}; + + +}/* namespace CVC4::theory::quantifiers */ +}/* namespace CVC4::theory */ +}/* namespace CVC4 */ + +#endif diff --git a/src/theory/quantifiers/ce_guided_single_inv.cpp b/src/theory/quantifiers/ce_guided_single_inv.cpp index 41b262ddf..4db6df155 100644 --- a/src/theory/quantifiers/ce_guided_single_inv.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv.cpp @@ -174,8 +174,16 @@ void CegConjectureSingleInv::initialize( Node q ) { qq = removeDeepEmbedding( qq, progs, types, type_valid, visited ); Trace("cegqi-si-debug") << "- Remove deep embedding, got : " << qq << ", type valid = " << type_valid << std::endl; if( type_valid==0 ){ + std::vector< Node > prog_funcs; + for( unsigned j=0; j<progs.size(); j++ ){ + std::map< Node, Node >::iterator itns = d_nsi_op_map.find( progs[j] ); + if( itns != d_nsi_op_map.end() ){ + prog_funcs.push_back( itns->second ); + } + } + //process the single invocation-ness of the property - d_sip->init( types, qq ); + d_sip->init( prog_funcs, qq ); Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl; d_sip->debugPrint( "cegqi-si" ); //map from program to bound variables @@ -193,9 +201,12 @@ void CegConjectureSingleInv::initialize( Node q ) { Trace("cegqi-si") << " " << pv << ", " << inv << " is associated with program " << prog << std::endl; d_prog_to_sol_index[prog] = order_vars.size(); order_vars.push_back( pv ); + }else{ + Trace("cegqi-si") << " " << prog << " has no fo var." << std::endl; } }else{ //does not mention the function + Trace("cegqi-si") << " " << prog << " is not mentioned." << std::endl; } } //reorder the variables @@ -215,64 +226,71 @@ void CegConjectureSingleInv::initialize( Node q ) { singleInvocation = true; }else if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){ //if we are doing invariant templates, then construct the template - std::map< Node, bool > has_inv; - std::map< Node, std::vector< Node > > inv_pre_post[2]; - for( unsigned i=0; i<d_sip->d_conjuncts[2].size(); i++ ){ - std::vector< Node > disjuncts; - Node func; - int pol = -1; - Trace("cegqi-inv") << "INV process " << d_sip->d_conjuncts[2][i] << std::endl; - d_sip->extractInvariant( d_sip->d_conjuncts[2][i], func, pol, disjuncts ); - if( pol>=0 ){ - Assert( d_nsi_op_map_to_prog.find( func )!=d_nsi_op_map_to_prog.end() ); - Node prog = d_nsi_op_map_to_prog[func]; - Trace("cegqi-inv") << "..." << ( pol==0 ? "pre" : "post" ) << "-condition for " << prog << "." << std::endl; - Node c = disjuncts.empty() ? d_qe->getTermDatabase()->d_false : ( disjuncts.size()==1 ? disjuncts[0] : NodeManager::currentNM()->mkNode( OR, disjuncts ) ); - c = pol==0 ? TermDb::simpleNegate( c ) : c; - Trace("cegqi-inv-debug") << "...extracted : " << c << std::endl; - inv_pre_post[pol][prog].push_back( c ); - has_inv[prog] = true; - }else{ - Trace("cegqi-inv") << "...no status." << std::endl; - } - } - - Trace("cegqi-inv") << "Constructing invariant templates..." << std::endl; - //now, contruct the template for the invariant(s) + Trace("cegqi-si") << "- Do transition inference..." << std::endl; + d_ti[q].process( qq ); + Trace("cegqi-inv") << std::endl; std::map< Node, Node > prog_templ; - for( std::map< Node, bool >::iterator iti = has_inv.begin(); iti != has_inv.end(); ++iti ){ - Node prog = iti->first; - Trace("cegqi-inv") << "...for " << prog << "..." << std::endl; - Trace("cegqi-inv") << " args : "; - for( unsigned j=0; j<d_sip->d_si_vars.size(); j++ ){ - std::stringstream ss; - ss << "i_" << j; - Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), d_sip->d_si_vars[j].getType() ); - d_prog_templ_vars[prog].push_back( v ); - Trace("cegqi-inv") << v << " "; - } - Trace("cegqi-inv") << std::endl; - Node pre = inv_pre_post[0][prog].empty() ? NodeManager::currentNM()->mkConst( false ) : - ( inv_pre_post[0][prog].size()==1 ? inv_pre_post[0][prog][0] : NodeManager::currentNM()->mkNode( OR, inv_pre_post[0][prog] ) ); - d_trans_pre[prog] = pre.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); - Node post = inv_pre_post[1][prog].empty() ? NodeManager::currentNM()->mkConst( true ) : - ( inv_pre_post[1][prog].size()==1 ? inv_pre_post[1][prog][0] : NodeManager::currentNM()->mkNode( AND, inv_pre_post[1][prog] ) ); - d_trans_post[prog] = post.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); + if( !d_ti[q].d_func.isNull() ){ + // map the program back via non-single invocation map + Assert( d_nsi_op_map_to_prog.find( d_ti[q].d_func )!=d_nsi_op_map_to_prog.end() ); + Node prog = d_nsi_op_map_to_prog[d_ti[q].d_func]; + Assert( d_prog_templ_vars[prog].empty() ); + d_prog_templ_vars[prog].insert( d_prog_templ_vars[prog].end(), d_ti[q].d_vars.begin(), d_ti[q].d_vars.end() ); + d_trans_pre[prog] = d_ti[q].getComponent( 1 ); + d_trans_post[prog] = d_ti[q].getComponent( -1 ); Trace("cegqi-inv") << " precondition : " << d_trans_pre[prog] << std::endl; Trace("cegqi-inv") << " postcondition : " << d_trans_post[prog] << std::endl; Node invariant = single_inv_app_map[prog]; invariant = invariant.substitute( d_sip->d_si_vars.begin(), d_sip->d_si_vars.end(), d_prog_templ_vars[prog].begin(), d_prog_templ_vars[prog].end() ); Trace("cegqi-inv") << " invariant : " << invariant << std::endl; + //construct template - Node templ; - if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ - //templ = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] ); - templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ); - }else{ - Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST ); - //templ = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) ); - templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ); + d_templ_arg[prog] = NodeManager::currentNM()->mkSkolem( "I", invariant.getType() ); + if( options::sygusInvAutoUnfold() ){ + if( d_ti[q].isComplete() ){ + Trace("cegqi-inv-auto-unfold") << "Automatic deterministic unfolding... " << std::endl; + // auto-unfold + DetTrace dt; + int init_dt = d_ti[q].initializeTrace( dt ); + if( init_dt==0 ){ + Trace("cegqi-inv-auto-unfold") << " Init : "; + dt.print("cegqi-inv-auto-unfold"); + Trace("cegqi-inv-auto-unfold") << std::endl; + unsigned counter = 0; + unsigned status = 0; + while( counter<100 && status==0 ){ + status = d_ti[q].incrementTrace( dt ); + counter++; + Trace("cegqi-inv-auto-unfold") << " #" << counter << " : "; + dt.print("cegqi-inv-auto-unfold"); + Trace("cegqi-inv-auto-unfold") << "...status = " << status << std::endl; + } + if( status==1 ){ + // we have a trivial invariant + d_templ[prog] = d_ti[q].constructFormulaTrace( dt ); + Trace("cegqi-inv") << "By finite deterministic terminating trace, a solution invariant is : " << std::endl; + Trace("cegqi-inv") << " " << d_templ[prog] << std::endl; + // FIXME : this should be uncessary + d_templ[prog] = NodeManager::currentNM()->mkNode( AND, d_templ[prog], d_templ_arg[prog] ); + } + }else{ + Trace("cegqi-inv-auto-unfold") << "...failed initialize." << std::endl; + } + } } + if( d_templ[prog].isNull() ){ + if( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_PRE ){ + //d_templ[prog] = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], invariant ), d_trans_post[prog] ); + d_templ[prog] = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], d_templ_arg[prog] ); + }else{ + Assert( options::sygusInvTemplMode() == SYGUS_INV_TEMPL_MODE_POST ); + //d_templ[prog] = NodeManager::currentNM()->mkNode( OR, d_trans_pre[prog], NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], invariant ) ); + d_templ[prog] = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], d_templ_arg[prog] ); + } + } + TNode iv = d_templ_arg[prog]; + TNode is = invariant; + Node templ = d_templ[prog].substitute( iv, is ); visited.clear(); templ = addDeepEmbedding( templ, visited ); Trace("cegqi-inv") << " template : " << templ << std::endl; @@ -447,7 +465,7 @@ Node CegConjectureSingleInv::removeDeepEmbedding( Node n, std::vector< Node >& p op = it->second; } children[0] = d_nsi_op_map[n[0]]; - ret = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + ret = children.size()>1 ? NodeManager::currentNM()->mkNode( APPLY_UF, children ) : children[0]; } } if( ret.isNull() ){ @@ -591,6 +609,7 @@ bool CegConjectureSingleInv::addLemma( Node n ) { bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { if( !d_single_inv.isNull() ) { + Trace("cegqi-si-debug") << "CegConjectureSingleInv::check..." << std::endl; if( !d_ns_guard.isNull() ){ //if partially single invocation, check if we have constructed a candidate by refutation bool value; @@ -636,6 +655,7 @@ bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { Trace("cegqi-lemma") << "Cegqi::Lemma : verification, refinement lemma : " << inst << std::endl; d_qe->addLemma( finst_lem ); */ + Trace("cegqi-si-debug") << "CegConjectureSingleInv::check returned verification lemma (nsi)..." << std::endl; return true; }else{ //currently trying to construct candidate by refutation (by d_cinst->check below) @@ -650,9 +670,12 @@ bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { //construct d_single_inv d_single_inv = Node::null(); initializeNextSiConjecture(); + Trace("cegqi-si-debug") << "CegConjectureSingleInv::check initialized next si conjecture..." << std::endl; return true; } + Trace("cegqi-si-debug") << "CegConjectureSingleInv::check consulting ceg instantiation..." << std::endl; d_curr_lemmas.clear(); + Assert( d_cinst!=NULL ); //call check for instantiator d_cinst->check(); //add lemmas @@ -667,6 +690,7 @@ bool CegConjectureSingleInv::check( std::vector< Node >& lems ) { } return !lems.empty(); }else{ + // not single invocation return false; } } @@ -706,7 +730,6 @@ struct sortSiInstanceIndices { Node CegConjectureSingleInv::postProcessSolution( Node n ){ - /* ////remove boolean ITE (not allowed for sygus comp 2015) if( n.getKind()==ITE && n.getType().isBoolean() ){ Node n1 = postProcessSolution( n[1] ); @@ -714,29 +737,29 @@ Node CegConjectureSingleInv::postProcessSolution( Node n ){ return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n[0], n1 ), NodeManager::currentNM()->mkNode( AND, n[0].negate(), n2 ) ); }else{ - */ - bool childChanged = false; - Kind k = n.getKind(); - if( n.getKind()==INTS_DIVISION_TOTAL ){ - k = INTS_DIVISION; - childChanged = true; - }else if( n.getKind()==INTS_MODULUS_TOTAL ){ - k = INTS_MODULUS; - childChanged = true; - } - std::vector< Node > children; - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Node nn = postProcessSolution( n[i] ); - children.push_back( nn ); - childChanged = childChanged || nn!=n[i]; - } - if( childChanged ){ - if( n.hasOperator() && k==n.getKind() ){ - children.insert( children.begin(), n.getOperator() ); + bool childChanged = false; + Kind k = n.getKind(); + if( n.getKind()==INTS_DIVISION_TOTAL ){ + k = INTS_DIVISION; + childChanged = true; + }else if( n.getKind()==INTS_MODULUS_TOTAL ){ + k = INTS_MODULUS; + childChanged = true; + } + std::vector< Node > children; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node nn = postProcessSolution( n[i] ); + children.push_back( nn ); + childChanged = childChanged || nn!=n[i]; + } + if( childChanged ){ + if( n.hasOperator() && k==n.getKind() ){ + children.insert( children.begin(), n.getOperator() ); + } + return NodeManager::currentNM()->mkNode( k, children ); + }else{ + return n; } - return NodeManager::currentNM()->mkNode( k, children ); - }else{ - return n; } } @@ -817,7 +840,6 @@ Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& Node CegConjectureSingleInv::reconstructToSyntax( Node s, TypeNode stn, int& reconstructed, bool rconsSygus ) { d_solution = s; const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype(); - Trace("csi-sol") << "Reconstruct to syntax " << s << ", allow all = " << dt.getSygusAllowAll() << " " << stn << ", reconstruct = " << rconsSygus << std::endl; //reconstruct the solution into sygus if necessary reconstructed = 0; @@ -895,20 +917,21 @@ bool SingleInvocationPartition::init( Node n ) { //first, get types of arguments for functions std::vector< TypeNode > typs; std::map< Node, bool > visited; + std::vector< Node > funcs; if( inferArgTypes( n, typs, visited ) ){ - return init( typs, n ); + return init( funcs, typs, n, false ); }else{ Trace("si-prt") << "Could not infer argument types." << std::endl; return false; } } +// gets the argument type list for the first APPLY_UF we see bool SingleInvocationPartition::inferArgTypes( Node n, std::vector< TypeNode >& typs, std::map< Node, bool >& visited ) { if( visited.find( n )==visited.end() ){ visited[n] = true; if( n.getKind()!=FORALL ){ - //if( TermDb::hasBoundVarAttr( n ) ){ - if( n.getKind()==d_checkKind ){ + if( n.getKind()==APPLY_UF ){ for( unsigned i=0; i<n.getNumChildren(); i++ ){ typs.push_back( n[i].getType() ); } @@ -920,16 +943,41 @@ bool SingleInvocationPartition::inferArgTypes( Node n, std::vector< TypeNode >& } } } - //} } } return false; } -bool SingleInvocationPartition::init( std::vector< TypeNode >& typs, Node n ){ +bool SingleInvocationPartition::init( std::vector< Node >& funcs, Node n ) { + Trace("si-prt") << "Initialize with " << funcs.size() << " input functions..." << std::endl; + std::vector< TypeNode > typs; + if( !funcs.empty() ){ + TypeNode tn0 = funcs[0].getType(); + for( unsigned i=1; i<funcs.size(); i++ ){ + if( funcs[i].getType()!=tn0 ){ + // can't anti-skolemize functions of different sort + Trace("si-prt") << "...type mismatch" << std::endl; + return false; + } + } + if( tn0.getNumChildren()>1 ){ + for( unsigned j=0; j<tn0.getNumChildren()-1; j++ ){ + typs.push_back( tn0[j] ); + } + } + } + Trace("si-prt") << "#types = " << typs.size() << std::endl; + return init( funcs, typs, n, true ); +} + +bool SingleInvocationPartition::init( std::vector< Node >& funcs, std::vector< TypeNode >& typs, Node n, bool has_funcs ){ Assert( d_arg_types.empty() ); + Assert( d_input_funcs.empty() ); Assert( d_si_vars.empty() ); + d_has_input_funcs = has_funcs; d_arg_types.insert( d_arg_types.end(), typs.begin(), typs.end() ); + d_input_funcs.insert( d_input_funcs.end(), funcs.begin(), funcs.end() ); + Trace("si-prt") << "Initialize..." << std::endl; for( unsigned j=0; j<d_arg_types.size(); j++ ){ std::stringstream ss; ss << "s_" << j; @@ -968,7 +1016,9 @@ void SingleInvocationPartition::process( Node n ) { if( processConjunct( cr, visited, args, terms, subs ) ){ for( unsigned j=0; j<terms.size(); j++ ){ si_terms.push_back( subs[j] ); - si_subs.push_back( d_func_fo_var[subs[j].getOperator()] ); + Node op = subs[j].hasOperator() ? subs[j].getOperator() : subs[j]; + Assert( d_func_fo_var.find( op )!=d_func_fo_var.end() ); + si_subs.push_back( d_func_fo_var[op] ); } std::map< Node, Node > subs_map; std::map< Node, Node > subs_map_rev; @@ -1090,9 +1140,21 @@ bool SingleInvocationPartition::processConjunct( Node n, std::map< Node, bool >& } } if( ret ){ - if( n.getKind()==d_checkKind ){ + Node f; + bool success = false; + if( d_has_input_funcs ){ + f = n.hasOperator() ? n.getOperator() : n; + if( std::find( d_input_funcs.begin(), d_input_funcs.end(), f )!=d_input_funcs.end() ){ + success = true; + } + }else{ + if( n.getKind()==kind::APPLY_UF ){ + f = n.getOperator(); + success = true; + } + } + if( success ){ if( std::find( terms.begin(), terms.end(), n )==terms.end() ){ - Node f = n.getOperator(); //check if it matches the type requirement if( isAntiSkolemizableType( f ) ){ if( args.empty() ){ @@ -1134,7 +1196,7 @@ bool SingleInvocationPartition::isAntiSkolemizableType( Node f ) { }else{ TypeNode tn = f.getType(); bool ret = false; - if( tn.getNumChildren()==d_arg_types.size()+1 ){ + if( tn.getNumChildren()==d_arg_types.size()+1 || ( d_arg_types.empty() && tn.getNumChildren()==0 ) ){ ret = true; std::vector< Node > children; children.push_back( f ); @@ -1147,12 +1209,23 @@ bool SingleInvocationPartition::isAntiSkolemizableType( Node f ) { } } if( ret ){ - Node t = NodeManager::currentNM()->mkNode( d_checkKind, children ); + Node t; + if( children.size()>1 ){ + t = NodeManager::currentNM()->mkNode( kind::APPLY_UF, children ); + }else{ + t = children[0]; + } d_func_inv[f] = t; d_inv_to_func[t] = f; std::stringstream ss; ss << "F_" << f; - Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), tn.getRangeType() ); + TypeNode rt; + if( d_arg_types.empty() ){ + rt = tn; + }else{ + rt = tn.getRangeType(); + } + Node v = NodeManager::currentNM()->mkBoundVar( ss.str(), rt ); d_func_fo_var[f] = v; d_fo_var_to_func[v] = f; d_func_vars.push_back( v ); @@ -1181,8 +1254,21 @@ Node SingleInvocationPartition::getSpecificationInst( Node n, std::map< Node, No childChanged = childChanged || ( nn!=n[i] ); } Node ret; - if( n.getKind()==d_checkKind ){ - std::map< Node, Node >::iterator itl = lam.find( n.getOperator() ); + Node f; + bool success = false; + if( d_has_input_funcs ){ + f = n.hasOperator() ? n.getOperator() : n; + if( std::find( d_input_funcs.begin(), d_input_funcs.end(), f )!=d_input_funcs.end() ){ + success = true; + } + }else{ + if( n.getKind()==APPLY_UF ){ + f = n.getOperator(); + success = true; + } + } + if( success ){ + std::map< Node, Node >::iterator itl = lam.find( f ); if( itl!=lam.end() ){ Assert( itl->second[0].getNumChildren()==children.size() ); std::vector< Node > terms; @@ -1214,55 +1300,6 @@ Node SingleInvocationPartition::getSpecificationInst( int index, std::map< Node, return getSpecificationInst( conj, lam, visited ); } -void SingleInvocationPartition::extractInvariant( Node n, Node& func, int& pol, std::vector< Node >& disjuncts ) { - std::map< Node, bool > visited; - extractInvariant2( n, func, pol, disjuncts, true, visited ); -} - -void SingleInvocationPartition::extractInvariant2( Node n, Node& func, int& pol, std::vector< Node >& disjuncts, bool hasPol, std::map< Node, bool >& visited ) { - if( visited.find( n )==visited.end() && pol!=-2 ){ - Trace("cegqi-inv-debug2") << "Extract : " << n << " " << hasPol << ", pol = " << pol << std::endl; - visited[n] = true; - if( n.getKind()==OR && hasPol ){ - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - extractInvariant2( n[i], func, pol, disjuncts, true, visited ); - } - }else{ - if( hasPol ){ - bool lit_pol = n.getKind()!=NOT; - Node lit = n.getKind()==NOT ? n[0] : n; - std::map< Node, Node >::iterator it = d_inv_to_func.find( lit ); - if( it!=d_inv_to_func.end() ){ - if( pol==-1 ){ - pol = lit_pol ? 0 : 1; - func = it->second; - }else{ - //mixing multiple invariants - pol = -2; - } - return; - }else{ - disjuncts.push_back( n ); - } - } - //if another part mentions UF or a free variable, then fail - if( n.getKind()==APPLY_UF ){ - Node op = n.getOperator(); - if( d_funcs.find( op )!=d_funcs.end() ){ - pol = -2; - return; - } - }else if( n.getKind()==BOUND_VARIABLE && std::find( d_si_vars.begin(), d_si_vars.end(), n )==d_si_vars.end() ){ - pol = -2; - return; - } - for( unsigned i=0; i<n.getNumChildren(); i++ ){ - extractInvariant2( n[i], func, pol, disjuncts, false, visited ); - } - } - } -} - void SingleInvocationPartition::debugPrint( const char * c ) { Trace(c) << "Single invocation variables : "; for( unsigned i=0; i<d_si_vars.size(); i++ ){ @@ -1288,4 +1325,391 @@ void SingleInvocationPartition::debugPrint( const char * c ) { Trace(c) << std::endl; } + +bool DetTrace::DetTraceTrie::add( Node loc, std::vector< Node >& val, unsigned index ){ + if( index==val.size() ){ + if( d_children.empty() ){ + d_children[loc].clear(); + return true; + }else{ + return false; + } + }else{ + return d_children[val[index]].add( loc, val, index+1 ); + } +} + +Node DetTrace::DetTraceTrie::constructFormula( std::vector< Node >& vars, unsigned index ){ + if( index==vars.size() ){ + return NodeManager::currentNM()->mkConst( true ); + }else{ + std::vector< Node > disj; + for( std::map< Node, DetTraceTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + Node eq = vars[index].eqNode( it->first ); + if( index<vars.size()-1 ){ + Node conc = it->second.constructFormula( vars, index+1 ); + disj.push_back( NodeManager::currentNM()->mkNode( kind::AND, eq, conc ) ); + }else{ + disj.push_back( eq ); + } + } + Assert( !disj.empty() ); + return disj.size()==1 ? disj[0] : NodeManager::currentNM()->mkNode( kind::OR, disj ); + } +} + +bool DetTrace::increment( Node loc, std::vector< Node >& vals ){ + if( d_trie.add( loc, vals ) ){ + for( unsigned i=0; i<vals.size(); i++ ){ + d_curr[i] = vals[i]; + } + return true; + }else{ + return false; + } +} + +Node DetTrace::constructFormula( std::vector< Node >& vars ) { + return d_trie.constructFormula( vars ); +} + + +void DetTrace::print( const char* c ) { + for( unsigned i=0; i<d_curr.size(); i++ ){ + Trace(c) << d_curr[i] << " "; + } +} + +void TransitionInference::initialize( Node f, std::vector< Node >& vars ) { + Assert( d_vars.empty() ); + d_func = f; + d_vars.insert( d_vars.end(), vars.begin(), vars.end() ); } + + +void TransitionInference::getConstantSubstitution( std::vector< Node >& vars, std::vector< Node >& disjuncts, std::vector< Node >& const_var, std::vector< Node >& const_subs, bool reqPol ) { + for( unsigned j=0; j<disjuncts.size(); j++ ){ + Node sn; + if( !const_var.empty() ){ + sn = disjuncts[j].substitute( const_var.begin(), const_var.end(), const_subs.begin(), const_subs.end() ); + sn = Rewriter::rewrite( sn ); + }else{ + sn = disjuncts[j]; + } + bool slit_pol = sn.getKind()!=NOT; + Node slit = sn.getKind()==NOT ? sn[0] : sn; + if( slit.getKind()==EQUAL && slit_pol==reqPol ){ + // check if it is a variable equality + TNode v; + Node s; + for( unsigned r=0; r<2; r++ ){ + if( std::find( vars.begin(), vars.end(), slit[r] )!=vars.end() ){ + if( !TermDb::containsTerm( slit[1-r], slit[r] ) ){ + v = slit[r]; + s = slit[1-r]; + break; + } + } + } + if( v.isNull() ){ + //solve for var + std::map< Node, Node > msum; + if( QuantArith::getMonomialSumLit( slit, msum ) ){ + for( std::map< Node, Node >::iterator itm = msum.begin(); itm != msum.end(); ++itm ){ + if( std::find( vars.begin(), vars.end(), itm->first )!=vars.end() ){ + Node veq_c; + Node val; + int ires = QuantArith::isolate( itm->first, msum, veq_c, val, EQUAL ); + if( ires!=0 && veq_c.isNull() && !TermDb::containsTerm( val, itm->first ) ){ + v = itm->first; + s = val; + } + } + } + } + } + if( !v.isNull() ){ + TNode ts = s; + for( unsigned k=0; k<const_subs.size(); k++ ){ + const_subs[k] = Rewriter::rewrite( const_subs[k].substitute( v, ts ) ); + } + Trace("cegqi-inv-debug2") << "...substitution : " << v << " -> " << s << std::endl; + const_var.push_back( v ); + const_subs.push_back( s ); + } + } + } +} + +void TransitionInference::process( Node n ) { + d_complete = true; + std::vector< Node > n_check; + if( n.getKind()==AND ){ + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + n_check.push_back( n[i] ); + } + }else{ + n_check.push_back( n ); + } + for( unsigned i=0; i<n_check.size(); i++ ){ + Node nn = n_check[i]; + std::map< Node, bool > visited; + std::map< bool, Node > terms; + std::vector< Node > disjuncts; + Trace("cegqi-inv") << "TransitionInference : Process disjunct : " << nn << std::endl; + if( processDisjunct( nn, terms, disjuncts, visited, true ) ){ + if( !terms.empty() ){ + Node norm_args; + int comp_num; + std::map< bool, Node >::iterator itt = terms.find( false ); + if( itt!=terms.end() ){ + norm_args = itt->second; + if( terms.find( true )!=terms.end() ){ + comp_num = 0; + }else{ + comp_num = -1; + } + }else{ + norm_args = terms[true]; + comp_num = 1; + } + std::vector< Node > subs; + for( unsigned j=0; j<norm_args.getNumChildren(); j++ ){ + subs.push_back( norm_args[j] ); + } + Trace("cegqi-inv-debug2") << " normalize based on " << norm_args << std::endl; + Assert( d_vars.size()==subs.size() ); + for( unsigned j=0; j<disjuncts.size(); j++ ){ + disjuncts[j] = Rewriter::rewrite( disjuncts[j].substitute( subs.begin(), subs.end(), d_vars.begin(), d_vars.end() ) ); + Trace("cegqi-inv-debug2") << " ..." << disjuncts[j] << std::endl; + } + std::vector< Node > const_var; + std::vector< Node > const_subs; + if( comp_num==0 ){ + //transition + Assert( terms.find( true )!=terms.end() ); + Node next = terms[true]; + next = Rewriter::rewrite( next.substitute( subs.begin(), subs.end(), d_vars.begin(), d_vars.end() ) ); + Trace("cegqi-inv-debug") << "transition next predicate : " << next << std::endl; + // normalize the other direction + std::vector< Node > rvars; + for( unsigned i=0; i<next.getNumChildren(); i++ ){ + rvars.push_back( next[i] ); + } + if( d_prime_vars.size()<next.getNumChildren() ){ + for( unsigned i=0; i<next.getNumChildren(); i++ ){ + Node v = NodeManager::currentNM()->mkSkolem( "ir", next[i].getType(), "template inference rev argument" ); + d_prime_vars.push_back( v ); + } + } + Trace("cegqi-inv-debug2") << " normalize based on " << next << std::endl; + Assert( d_vars.size()==subs.size() ); + for( unsigned j=0; j<disjuncts.size(); j++ ){ + disjuncts[j] = Rewriter::rewrite( disjuncts[j].substitute( rvars.begin(), rvars.end(), d_prime_vars.begin(), d_prime_vars.end() ) ); + Trace("cegqi-inv-debug2") << " ..." << disjuncts[j] << std::endl; + } + getConstantSubstitution( d_prime_vars, disjuncts, const_var, const_subs, false ); + }else{ + getConstantSubstitution( d_vars, disjuncts, const_var, const_subs, false ); + } + Node res; + if( disjuncts.empty() ){ + res = NodeManager::currentNM()->mkConst( false ); + }else if( disjuncts.size()==1 ){ + res = disjuncts[0]; + }else{ + res = NodeManager::currentNM()->mkNode( kind::OR, disjuncts ); + } + if( !res.hasBoundVar() ){ + Trace("cegqi-inv") << "*** inferred " << ( comp_num==1 ? "pre" : ( comp_num==-1 ? "post" : "trans" ) ) << "-condition : " << res << std::endl; + d_com[comp_num].d_conjuncts.push_back( res ); + if( !const_var.empty() ){ + bool has_const_eq = const_var.size()==d_vars.size(); + Trace("cegqi-inv") << " with constant substitution, complete = " << has_const_eq << " : " << std::endl; + for( unsigned i=0; i<const_var.size(); i++ ){ + Trace("cegqi-inv") << " " << const_var[i] << " -> " << const_subs[i] << std::endl; + if( has_const_eq ){ + d_com[comp_num].d_const_eq[res][const_var[i]] = const_subs[i]; + } + } + Trace("cegqi-inv") << "...size = " << const_var.size() << ", #vars = " << d_vars.size() << std::endl; + } + }else{ + Trace("cegqi-inv-debug2") << "...failed, free variable." << std::endl; + d_complete = false; + } + } + }else{ + d_complete = false; + } + } + + // finalize the components + for( int i=-1; i<=1; i++ ){ + Node ret; + if( d_com[i].d_conjuncts.empty() ){ + ret = NodeManager::currentNM()->mkConst( true ); + }else if( d_com[i].d_conjuncts.size()==1 ){ + ret = d_com[i].d_conjuncts[0]; + }else{ + ret = NodeManager::currentNM()->mkNode( kind::AND, d_com[i].d_conjuncts ); + } + if( i==0 || i==1 ){ + // pre-condition and transition are negated + ret = TermDb::simpleNegate( ret ); + } + d_com[i].d_this = ret; + } +} + +bool TransitionInference::processDisjunct( Node n, std::map< bool, Node >& terms, std::vector< Node >& disjuncts, + std::map< Node, bool >& visited, bool topLevel ) { + if( visited.find( n )==visited.end() ){ + visited[n] = true; + bool childTopLevel = n.getKind()==OR && topLevel; + //if another part mentions UF or a free variable, then fail + bool lit_pol = n.getKind()!=NOT; + Node lit = n.getKind()==NOT ? n[0] : n; + if( lit.getKind()==APPLY_UF ){ + Node op = lit.getOperator(); + if( d_func.isNull() ){ + d_func = op; + Trace("cegqi-inv-debug") << "Use " << op << " with args "; + for( unsigned i=0; i<lit.getNumChildren(); i++ ){ + Node v = NodeManager::currentNM()->mkSkolem( "i", lit[i].getType(), "template inference argument" ); + d_vars.push_back( v ); + Trace("cegqi-inv-debug") << v << " "; + } + Trace("cegqi-inv-debug") << std::endl; + } + if( op!=d_func ){ + Trace("cegqi-inv-debug") << "...failed, free function : " << n << std::endl; + return false; + }else if( topLevel ){ + if( terms.find( lit_pol )==terms.end() ){ + terms[lit_pol] = lit; + return true; + }else{ + Trace("cegqi-inv-debug") << "...failed, repeated inv-app : " << lit << std::endl; + return false; + } + }else{ + Trace("cegqi-inv-debug") << "...failed, non-entailed inv-app : " << lit << std::endl; + return false; + } + }else if( topLevel && !childTopLevel ){ + disjuncts.push_back( n ); + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( !processDisjunct( n[i], terms, disjuncts, visited, childTopLevel ) ){ + return false; + } + } + } + return true; +} + +Node TransitionInference::getComponent( int i ) { + return d_com[i].d_this; +} + +int TransitionInference::initializeTrace( DetTrace& dt, Node loc, bool fwd ) { + int index = fwd ? 1 : -1; + Assert( d_com[index].has( loc ) ); + std::map< Node, std::map< Node, Node > >::iterator it = d_com[index].d_const_eq.find( loc ); + if( it!=d_com[index].d_const_eq.end() ){ + std::vector< Node > next; + for( unsigned i=0; i<d_vars.size(); i++ ){ + Node v = d_vars[i]; + Assert( it->second.find( v )!=it->second.end() ); + next.push_back( it->second[v] ); + dt.d_curr.push_back( it->second[v] ); + } + Trace("cegqi-inv-debug2") << "dtrace : initial increment" << std::endl; + bool ret = dt.increment( loc, next ); + AlwaysAssert( ret ); + return 0; + } + return -1; +} + +int TransitionInference::incrementTrace( DetTrace& dt, Node loc, bool fwd ) { + Assert( d_com[0].has( loc ) ); + // check if it satisfies the pre/post condition + int check_index = fwd ? -1 : 1; + Node cc = getComponent( check_index ); + Assert( !cc.isNull() ); + Node ccr = Rewriter::rewrite( cc.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) ); + if( ccr.isConst() ){ + if( ccr.getConst<bool>()==( fwd ? false : true ) ){ + Trace("cegqi-inv-debug2") << "dtrace : counterexample" << std::endl; + return 2; + } + } + + + // terminates? + Node c = getComponent( 0 ); + Assert( !c.isNull() ); + + Assert( d_vars.size()==dt.d_curr.size() ); + Node cr = Rewriter::rewrite( c.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) ); + if( cr.isConst() ){ + if( !cr.getConst<bool>() ){ + Trace("cegqi-inv-debug2") << "dtrace : terminated" << std::endl; + return 1; + }else{ + return -1; + } + } + if( fwd ){ + std::map< Node, std::map< Node, Node > >::iterator it = d_com[0].d_const_eq.find( loc ); + if( it!=d_com[0].d_const_eq.end() ){ + std::vector< Node > next; + for( unsigned i=0; i<d_prime_vars.size(); i++ ){ + Node pv = d_prime_vars[i]; + Assert( it->second.find( pv )!=it->second.end() ); + Node pvs = it->second[pv]; + Assert( d_vars.size()==dt.d_curr.size() ); + Node pvsr = Rewriter::rewrite( pvs.substitute( d_vars.begin(), d_vars.end(), dt.d_curr.begin(), dt.d_curr.end() ) ); + next.push_back( pvsr ); + } + if( dt.increment( loc, next ) ){ + Trace("cegqi-inv-debug2") << "dtrace : success increment" << std::endl; + return 0; + }else{ + // looped + Trace("cegqi-inv-debug2") << "dtrace : looped" << std::endl; + return 1; + } + } + }else{ + //TODO + } + return -1; +} + +int TransitionInference::initializeTrace( DetTrace& dt, bool fwd ) { + Trace("cegqi-inv-debug2") << "Initialize trace" << std::endl; + int index = fwd ? 1 : -1; + if( d_com[index].d_conjuncts.size()==1 ){ + return initializeTrace( dt, d_com[index].d_conjuncts[0], fwd ); + }else{ + return -1; + } +} + +int TransitionInference::incrementTrace( DetTrace& dt, bool fwd ) { + if( d_com[0].d_conjuncts.size()==1 ){ + return incrementTrace( dt, d_com[0].d_conjuncts[0], fwd ); + }else{ + return -1; + } +} + +Node TransitionInference::constructFormulaTrace( DetTrace& dt ) { + return dt.constructFormula( d_vars ); +} + +} //namespace CVC4 + diff --git a/src/theory/quantifiers/ce_guided_single_inv.h b/src/theory/quantifiers/ce_guided_single_inv.h index daa298610..e22d5fb53 100644 --- a/src/theory/quantifiers/ce_guided_single_inv.h +++ b/src/theory/quantifiers/ce_guided_single_inv.h @@ -44,6 +44,58 @@ public: class SingleInvocationPartition; +class DetTrace { +private: + class DetTraceTrie { + public: + std::map< Node, DetTraceTrie > d_children; + bool add( Node loc, std::vector< Node >& val, unsigned index = 0 ); + void clear() { d_children.clear(); } + Node constructFormula( std::vector< Node >& vars, unsigned index = 0 ); + }; + DetTraceTrie d_trie; +public: + std::vector< Node > d_curr; + bool increment( Node loc, std::vector< Node >& vals ); + Node constructFormula( std::vector< Node >& vars ); + void print( const char* c ); +}; + +class TransitionInference { +private: + bool processDisjunct( Node n, std::map< bool, Node >& terms, std::vector< Node >& disjuncts, std::map< Node, bool >& visited, bool topLevel ); + void getConstantSubstitution( std::vector< Node >& vars, std::vector< Node >& disjuncts, std::vector< Node >& const_var, std::vector< Node >& const_subs, bool reqPol ); + bool d_complete; +public: + TransitionInference() : d_complete( false ) {} + std::vector< Node > d_vars; + std::vector< Node > d_prime_vars; + Node d_func; + + class Component { + public: + Component(){} + Node d_this; + std::vector< Node > d_conjuncts; + std::map< Node, std::map< Node, Node > > d_const_eq; + bool has( Node c ) { return std::find( d_conjuncts.begin(), d_conjuncts.end(), c )!=d_conjuncts.end(); } + }; + std::map< int, Component > d_com; + + void initialize( Node f, std::vector< Node >& vars ); + void process( Node n ); + Node getComponent( int i ); + bool isComplete() { return d_complete; } + + // 0 : success, 1 : terminated, 2 : counterexample, -1 : invalid + int initializeTrace( DetTrace& dt, Node loc, bool fwd = true ); + int incrementTrace( DetTrace& dt, Node loc, bool fwd = true ); + int initializeTrace( DetTrace& dt, bool fwd = true ); + int incrementTrace( DetTrace& dt, bool fwd = true ); + Node constructFormulaTrace( DetTrace& dt ); +}; + + class CegConjectureSingleInv { private: friend class CegqiOutputSingleInv; @@ -72,6 +124,7 @@ class CegConjectureSingleInv { QuantifiersEngine* d_qe; CegConjecture* d_parent; SingleInvocationPartition* d_sip; + std::map< Node, TransitionInference > d_ti; CegConjectureSingleInvSol* d_sol; CegEntailmentInfer* d_ei; // the instantiator @@ -130,6 +183,8 @@ class CegConjectureSingleInv { std::map< Node, Node > d_trans_pre; std::map< Node, Node > d_trans_post; std::map< Node, std::vector< Node > > d_prog_templ_vars; + std::map< Node, Node > d_templ; + std::map< Node, Node > d_templ_arg; //the non-single invocation portion of the quantified formula std::map< Node, Node > d_nsi_op_map; std::map< Node, Node > d_nsi_op_map_to_prog; @@ -170,6 +225,22 @@ class CegConjectureSingleInv { std::map<Node, Node>::const_iterator location = d_trans_post.find(prog); return location->second; } + Node getTemplate(Node prog) const { + std::map<Node, Node>::const_iterator tmpl = d_templ.find(prog); + if( tmpl!=d_templ.end() ){ + return tmpl->second; + }else{ + return Node::null(); + } + } + Node getTemplateArg(Node prog) const { + std::map<Node, Node>::const_iterator tmpla = d_templ_arg.find(prog); + if( tmpla != d_templ_arg.end() ){ + return tmpla->second; + }else{ + return Node::null(); + } + } }; @@ -178,21 +249,22 @@ class CegConjectureSingleInv { // "d_arg_types", and all invocations are in the same order across all // functions class SingleInvocationPartition { - private: +private: + bool d_has_input_funcs; + std::vector< Node > d_input_funcs; //options - Kind d_checkKind; bool inferArgTypes( Node n, std::vector< TypeNode >& typs, std::map< Node, bool >& visited ); void process( Node n ); bool collectConjuncts( Node n, bool pol, std::vector< Node >& conj ); bool processConjunct( Node n, std::map< Node, bool >& visited, std::vector< Node >& args, std::vector< Node >& terms, std::vector< Node >& subs ); Node getSpecificationInst( Node n, std::map< Node, Node >& lam, std::map< Node, Node >& visited ); - void extractInvariant2( Node n, Node& func, int& pol, std::vector< Node >& disjuncts, bool hasPol, std::map< Node, bool >& visited ); + bool init( std::vector< Node >& funcs, std::vector< TypeNode >& typs, Node n, bool has_funcs ); public: - SingleInvocationPartition( Kind checkKind = kind::APPLY_UF ) : d_checkKind( checkKind ){} + SingleInvocationPartition() : d_has_input_funcs( false ){} ~SingleInvocationPartition(){} bool init( Node n ); - bool init( std::vector< TypeNode >& typs, Node n ); + bool init( std::vector< Node >& funcs, Node n ); //outputs (everything is with bound var) std::vector< TypeNode > d_arg_types; @@ -216,14 +288,13 @@ public: Node getSpecificationInst( int index, std::map< Node, Node >& lam ); - void extractInvariant( Node n, Node& func, int& pol, std::vector< Node >& disjuncts ); - bool isPurelySingleInvocation() { return d_conjuncts[1].empty(); } bool isNonGroundSingleInvocation() { return d_conjuncts[3].size()==d_conjuncts[1].size(); } void debugPrint( const char * c ); }; + }/* namespace CVC4::theory::quantifiers */ }/* namespace CVC4::theory */ }/* namespace CVC4 */ diff --git a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp index 79b4f2c0e..aea1e4efc 100644 --- a/src/theory/quantifiers/ce_guided_single_inv_sol.cpp +++ b/src/theory/quantifiers/ce_guided_single_inv_sol.cpp @@ -383,7 +383,7 @@ Node CegConjectureSingleInvSol::simplifySolutionNode( Node sol, TypeNode stn, st d_qe->getTermDatabaseSygus()->registerSygusType( stn ); std::map< int, TypeNode > stnc; if( !stn.isNull() ){ - int karg = d_qe->getTermDatabaseSygus()->getKindArg( stn, sol.getKind() ); + int karg = d_qe->getTermDatabaseSygus()->getKindConsNum( stn, sol.getKind() ); if( karg!=-1 ){ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype(); if( dt[karg].getNumArgs()==sol.getNumChildren() ){ @@ -741,14 +741,14 @@ int CegConjectureSingleInvSol::collectReconstructNodes( Node t, TypeNode stn, in Node min_t = d_qe->getTermDatabaseSygus()->minimizeBuiltinTerm( t ); Trace("csi-rcons-debug") << "Minimized term is : " << min_t << std::endl; //check if op is in syntax sort - carg = d_qe->getTermDatabaseSygus()->getOpArg( stn, min_t ); + carg = d_qe->getTermDatabaseSygus()->getOpConsNum( stn, min_t ); if( carg!=-1 ){ Trace("csi-rcons-debug") << " Type has operator." << std::endl; d_reconstruct[id] = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, Node::fromExpr( dt[carg].getConstructor() ) ); status = 0; }else{ //check if kind is in syntax sort - karg = d_qe->getTermDatabaseSygus()->getKindArg( stn, min_t.getKind() ); + karg = d_qe->getTermDatabaseSygus()->getKindConsNum( stn, min_t.getKind() ); if( karg!=-1 ){ //collect the children of min_t std::vector< Node > tchildren; @@ -880,7 +880,7 @@ int CegConjectureSingleInvSol::collectReconstructNodes( Node t, TypeNode stn, in } //get decompositions for( unsigned i=0; i<dt.getNumConstructors(); i++ ){ - Kind k = d_qe->getTermDatabaseSygus()->getArgKind( stn, i ); + Kind k = d_qe->getTermDatabaseSygus()->getConsNumKind( stn, i ); getEquivalentTerms( k, min_t, equiv ); } //assign ids to terms diff --git a/src/theory/quantifiers/ceg_t_instantiator.cpp b/src/theory/quantifiers/ceg_t_instantiator.cpp index 23b0b85fe..cd541a2a6 100644 --- a/src/theory/quantifiers/ceg_t_instantiator.cpp +++ b/src/theory/quantifiers/ceg_t_instantiator.cpp @@ -688,7 +688,7 @@ Node DtInstantiator::solve_dt( Node v, Node a, Node b, Node sa, Node sb ) { TypeNode tn = a.getType(); const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); for( unsigned i=0; i<a.getNumChildren(); i++ ){ - Node nn = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex][i].getSelector() ), sb ); + Node nn = NodeManager::currentNM()->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; @@ -720,7 +720,7 @@ bool DtInstantiator::processEqualTerms( CegInstantiator * ci, SolvedForm& sf, No unsigned cindex = Datatype::indexOf( n.getOperator().toExpr() ); //now must solve for selectors applied to pv for( unsigned j=0; j<dt[cindex].getNumArgs(); j++ ){ - Node c = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex][j].getSelector() ), pv ); + Node c = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( d_type.toType(), j ) ), pv ); ci->pushStackVariable( c ); children.push_back( c ); } diff --git a/src/theory/quantifiers/inst_match_generator.cpp b/src/theory/quantifiers/inst_match_generator.cpp index 9202062a4..79a412b3c 100644 --- a/src/theory/quantifiers/inst_match_generator.cpp +++ b/src/theory/quantifiers/inst_match_generator.cpp @@ -16,6 +16,7 @@ #include "expr/datatype.h" #include "options/quantifiers_options.h" +#include "options/datatypes_options.h" #include "theory/quantifiers/candidate_generator.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/trigger.h" @@ -116,7 +117,8 @@ void InstMatchGenerator::initialize( Node q, QuantifiersEngine* qe, std::vector< break; } } - }else if( d_match_pattern.getKind()==APPLY_SELECTOR_TOTAL && d_match_pattern[0].getKind()==INST_CONSTANT && options::purifyDtTriggers() ){ + }else if( d_match_pattern.getKind()==APPLY_SELECTOR_TOTAL && d_match_pattern[0].getKind()==INST_CONSTANT && + options::purifyDtTriggers() && !options::dtSharedSelectors() ){ d_match_pattern = d_match_pattern[0]; } d_match_pattern_type = d_match_pattern.getType(); diff --git a/src/theory/quantifiers/inst_strategy_cbqi.cpp b/src/theory/quantifiers/inst_strategy_cbqi.cpp index 0c2822cd9..8e53c97dc 100644 --- a/src/theory/quantifiers/inst_strategy_cbqi.cpp +++ b/src/theory/quantifiers/inst_strategy_cbqi.cpp @@ -561,6 +561,9 @@ bool InstStrategyCbqi::doCbqi( Node q ){ } } } + if( d_quantEngine->getTermDatabase()->isQAttrSygus( q ) ){ + ret = 0; + } if( ret!=0 ){ //if quantifier has a non-handled variable, then do not use cbqi //if quantifier has an APPLY_UF term, then do not use cbqi unless EPR diff --git a/src/theory/quantifiers/quant_util.cpp b/src/theory/quantifiers/quant_util.cpp index 21ad032f3..b34abba13 100644 --- a/src/theory/quantifiers/quant_util.cpp +++ b/src/theory/quantifiers/quant_util.cpp @@ -531,3 +531,75 @@ Node QuantEPR::mkEPRAxiom( TypeNode tn ) { } } + +void TermRecBuild::addTerm( Node n ) { + d_term.push_back( n ); + std::vector< Node > currc; + d_kind.push_back( n.getKind() ); + if( n.getMetaKind()==kind::metakind::PARAMETERIZED ){ + currc.push_back( n.getOperator() ); + d_has_op.push_back( true ); + }else{ + d_has_op.push_back( false ); + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + currc.push_back( n[i] ); + } + d_children.push_back( currc ); +} + +void TermRecBuild::init( Node n ) { + Assert( d_term.empty() ); + addTerm( n ); +} + +void TermRecBuild::push( unsigned p ) { + Assert( !d_term.empty() ); + unsigned curr = d_term.size()-1; + Assert( d_pos.size()==curr ); + Assert( d_pos.size()+1==d_children.size() ); + Assert( p<d_term[curr].getNumChildren() ); + addTerm( d_term[curr][p] ); + d_pos.push_back( p ); +} + +void TermRecBuild::pop() { + Assert( !d_pos.empty() ); + d_pos.pop_back(); + d_kind.pop_back(); + d_has_op.pop_back(); + d_children.pop_back(); + d_term.pop_back(); +} + +void TermRecBuild::replaceChild( unsigned i, Node n ) { + Assert( !d_term.empty() ); + unsigned curr = d_term.size()-1; + unsigned o = d_has_op[curr] ? 1 : 0; + d_children[curr][i+o] = n; +} + +Node TermRecBuild::getChild( unsigned i ) { + unsigned curr = d_term.size()-1; + unsigned o = d_has_op[curr] ? 1 : 0; + return d_children[curr][i+o]; +} + +Node TermRecBuild::build( unsigned d ) { + Assert( d_pos.size()+1==d_term.size() ); + Assert( d<d_term.size() ); + int p = d<d_pos.size() ? d_pos[d] : -2; + std::vector< Node > children; + unsigned o = d_has_op[d] ? 1 : 0; + for( unsigned i=0; i<d_children[d].size(); i++ ){ + Node nc; + if( p+o==i ){ + nc = build( d+1 ); + }else{ + nc = d_children[d][i]; + } + children.push_back( nc ); + } + return NodeManager::currentNM()->mkNode( d_kind[d], children ); +} + diff --git a/src/theory/quantifiers/quant_util.h b/src/theory/quantifiers/quant_util.h index 42fb7381c..40a5b5849 100644 --- a/src/theory/quantifiers/quant_util.h +++ b/src/theory/quantifiers/quant_util.h @@ -207,6 +207,24 @@ public: bool hasEPRAxiom( TypeNode tn ) const { return d_epr_axiom.find( tn )!=d_epr_axiom.end(); } }; +class TermRecBuild { +private: + std::vector< Node > d_term; + std::vector< std::vector< Node > > d_children; + std::vector< Kind > d_kind; + std::vector< bool > d_has_op; + std::vector< unsigned > d_pos; + void addTerm( Node n ); +public: + TermRecBuild(){} + void init( Node n ); + void push( unsigned p ); + void pop(); + void replaceChild( unsigned i, Node n ); + Node getChild( unsigned i ); + Node build( unsigned p=0 ); +}; + } } diff --git a/src/theory/quantifiers/quantifiers_attributes.cpp b/src/theory/quantifiers/quantifiers_attributes.cpp index 2261fd036..a9b1470fd 100644 --- a/src/theory/quantifiers/quantifiers_attributes.cpp +++ b/src/theory/quantifiers/quantifiers_attributes.cpp @@ -42,6 +42,16 @@ void QuantifiersAttributes::setUserAttribute( const std::string& attr, Node n, s Trace("quant-attr-debug") << "Set sygus " << n << std::endl; SygusAttribute ca; n.setAttribute( ca, true ); + }else if( attr=="sygus-synth-fun" ){ + Assert( node_values.size()==1 ); + Trace("quant-attr-debug") << "Set sygus synth fun " << n << " to " << node_values[0] << std::endl; + SygusSynthFunAttribute ssfa; + n.setAttribute( ssfa, node_values[0] ); + }else if( attr=="sygus-synth-fun-var-list" ){ + Assert( node_values.size()==1 ); + Trace("quant-attr-debug") << "Set sygus synth fun var list to " << n << " to " << node_values[0] << std::endl; + SygusSynthFunVarListAttribute ssfvla; + n.setAttribute( ssfvla, node_values[0] ); }else if( attr=="synthesis" ){ Trace("quant-attr-debug") << "Set synthesis " << n << std::endl; SynthesisAttribute ca; diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 2d4a6e99c..4cb41e19e 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -1188,9 +1188,15 @@ Node QuantifiersRewriter::computePrenexAgg( Node n, bool topLevel, std::map< uns children[i] = children[i][1]; } } - // TODO : keep the pattern + // keep the pattern + std::vector< Node > iplc; + if( n.getNumChildren()==3 ){ + for( unsigned i=0; i<n[2].getNumChildren(); i++ ){ + iplc.push_back( n[2][i] ); + } + } Node nb = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( OR, children ); - ret = mkForall( args, nb, true ); + ret = mkForall( args, nb, iplc, true ); }else{ std::vector< Node > args; std::vector< Node > nargs; @@ -1347,14 +1353,19 @@ Node QuantifiersRewriter::mkForAll( std::vector< Node >& args, Node body, QAttri return NodeManager::currentNM()->mkNode( kind::FORALL, children ); } } + Node QuantifiersRewriter::mkForall( std::vector< Node >& args, Node body, bool marked ) { + std::vector< Node > iplc; + return mkForall( args, body, iplc, marked ); +} + +Node QuantifiersRewriter::mkForall( std::vector< Node >& args, Node body, std::vector< Node >& iplc, bool marked ) { if( args.empty() ){ return body; }else{ std::vector< Node > children; children.push_back( NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, args ) ); children.push_back( body ); - std::vector< Node > iplc; if( marked ){ Node avar = NodeManager::currentNM()->mkSkolem( "id", NodeManager::currentNM()->booleanType() ); QuantIdNumAttribute ida; diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 0edea5fae..d1f819726 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -94,6 +94,7 @@ public: static Node preprocess( Node n, bool isInst = false ); static Node mkForAll( std::vector< Node >& args, Node body, QAttributes& qa ); static Node mkForall( std::vector< Node >& args, Node body, bool marked = false ); + static Node mkForall( std::vector< Node >& args, Node body, std::vector< Node >& iplc, bool marked = false ); };/* class QuantifiersRewriter */ }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index 7d4f5b433..9e7f174c1 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -17,6 +17,7 @@ #include "expr/datatype.h" #include "options/base_options.h" #include "options/quantifiers_options.h" +#include "options/datatypes_options.h" #include "theory/quantifiers/ce_guided_instantiation.h" #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/fun_def_engine.h" @@ -30,6 +31,8 @@ #include "smt/smt_engine_scope.h" #include "theory/bv/theory_bv_utils.h" #include "util/bitvector.h" +#include "theory/datatypes/datatypes_rewriter.h" +#include "theory/strings/theory_strings_rewriter.h" using namespace std; using namespace CVC4::kind; @@ -1102,7 +1105,10 @@ void getSelfSel( const Datatype& dt, const DatatypeConstructor& dc, Node n, Type } */ for( unsigned k=0; k<ssc.size(); k++ ){ - selfSel.push_back( NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, dc[j].getSelector(), n ) ); + Node ss = NodeManager::currentNM()->mkNode( APPLY_SELECTOR_TOTAL, dc.getSelectorInternal( n.getType().toType(), j ), n ); + if( std::find( selfSel.begin(), selfSel.end(), ss )==selfSel.end() ){ + selfSel.push_back( ss ); + } } } } @@ -1843,13 +1849,16 @@ Node TermDb::ensureType( Node n, TypeNode tn ) { void TermDb::getRelevancyCondition( Node n, std::vector< Node >& cond ) { if( n.getKind()==APPLY_SELECTOR_TOTAL ){ - unsigned scindex = Datatype::cindexOf(n.getOperator().toExpr()); - const Datatype& dt = ((DatatypeType)(n[0].getType()).toType()).getDatatype(); - Node rc = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[scindex].getTester() ), n[0] ).negate(); - if( std::find( cond.begin(), cond.end(), rc )==cond.end() ){ - cond.push_back( rc ); + // don't worry about relevancy conditions if using shared selectors + if( !options::dtSharedSelectors() ){ + unsigned scindex = Datatype::cindexOf(n.getOperator().toExpr()); + const Datatype& dt = ((DatatypeType)(n[0].getType()).toType()).getDatatype(); + Node rc = NodeManager::currentNM()->mkNode( APPLY_TESTER, Node::fromExpr( dt[scindex].getTester() ), n[0] ).negate(); + if( std::find( cond.begin(), cond.end(), rc )==cond.end() ){ + cond.push_back( rc ); + } + getRelevancyCondition( n[0], cond ); } - getRelevancyCondition( n[0], cond ); } } @@ -1956,6 +1965,10 @@ bool TermDb::isComm( Kind k ) { k==BITVECTOR_PLUS || k==BITVECTOR_MULT || k==BITVECTOR_AND || k==BITVECTOR_OR || k==BITVECTOR_XOR || k==BITVECTOR_XNOR; } +bool TermDb::isNonAdditive( Kind k ) { + return k==AND || k==OR || k==BITVECTOR_AND || k==BITVECTOR_OR; +} + bool TermDb::isBoolConnective( Kind k ) { return k==OR || k==AND || k==EQUAL || k==ITE || k==FORALL || k==NOT || k==SEP_STAR; } @@ -2278,6 +2291,22 @@ Node TermDb::getQAttrQuantIdNumNode( Node q ) { } } + +bool EvalSygusInvarianceTest::invariant( quantifiers::TermDbSygus * tds, Node nvn, Node x ){ + TNode tnvn = nvn; + Node conj_subs = d_conj.substitute( d_var, tnvn ); + Node conj_subs_unfold = tds->evaluateWithUnfolding( conj_subs, d_visited ); + Trace("sygus-cref-eval2-debug") << " ...check unfolding : " << conj_subs_unfold << std::endl; + Trace("sygus-cref-eval2-debug") << " ......from : " << conj_subs << std::endl; + if( conj_subs_unfold==d_result ){ + Trace("sygus-cref-eval2") << "Evaluation min explain : " << conj_subs << " still evaluates to " << d_result << " regardless of "; + Trace("sygus-cref-eval2") << x << std::endl; + return true; + }else{ + return false; + } +} + TermDbSygus::TermDbSygus( context::Context* c, QuantifiersEngine* qe ) : d_quantEngine( qe ){ d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); @@ -2287,16 +2316,23 @@ bool TermDbSygus::reset( Theory::Effort e ) { return true; } -TNode TermDbSygus::getVar( TypeNode tn, int i ) { - while( i>=(int)d_fv[tn].size() ){ - std::stringstream ss; - TypeNode vtn = tn; +TNode TermDbSygus::getFreeVar( TypeNode tn, int i, bool useSygusType ) { + unsigned sindex = 0; + TypeNode vtn = tn; + if( useSygusType ){ if( tn.isDatatype() ){ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - ss << "fv_" << dt.getName() << "_" << i; if( !dt.getSygusType().isNull() ){ vtn = TypeNode::fromType( dt.getSygusType() ); - } + sindex = 1; + } + } + } + while( i>=(int)d_fv[sindex][tn].size() ){ + std::stringstream ss; + if( tn.isDatatype() ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + ss << "fv_" << dt.getName() << "_" << i; }else{ ss << "fv_" << tn << "_" << i; } @@ -2304,23 +2340,43 @@ TNode TermDbSygus::getVar( TypeNode tn, int i ) { Node v = NodeManager::currentNM()->mkSkolem( ss.str(), vtn, "for sygus normal form testing" ); d_fv_stype[v] = tn; d_fv_num[v] = i; - d_fv[tn].push_back( v ); + d_fv[sindex][tn].push_back( v ); } - return d_fv[tn][i]; + return d_fv[sindex][tn][i]; } -TNode TermDbSygus::getVarInc( TypeNode tn, std::map< TypeNode, int >& var_count ) { +TNode TermDbSygus::getFreeVarInc( TypeNode tn, std::map< TypeNode, int >& var_count, bool useSygusType ) { std::map< TypeNode, int >::iterator it = var_count.find( tn ); if( it==var_count.end() ){ var_count[tn] = 1; - return getVar( tn, 0 ); + return getFreeVar( tn, 0, useSygusType ); }else{ int index = it->second; var_count[tn]++; - return getVar( tn, index ); + return getFreeVar( tn, index, useSygusType ); + } +} + +bool TermDbSygus::hasFreeVar( Node n, std::map< Node, bool >& visited ){ + if( visited.find( n )==visited.end() ){ + visited[n] = true; + if( isFreeVar( n ) ){ + return true; + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( hasFreeVar( n[i], visited ) ){ + return true; + } + } } + return false; } +bool TermDbSygus::hasFreeVar( Node n ) { + std::map< Node, bool > visited; + return hasFreeVar( n, visited ); +} + TypeNode TermDbSygus::getSygusTypeForVar( Node v ) { Assert( d_fv_stype.find( v )!=d_fv_stype.end() ); return d_fv_stype[v]; @@ -2433,7 +2489,7 @@ bool TermDbSygus::getMatch( Node t, TypeNode st, int& index_found, std::vector< Node TermDbSygus::getGenericBase( TypeNode tn, const Datatype& dt, int c ) { std::map< int, Node >::iterator it = d_generic_base[tn].find( c ); if( it==d_generic_base[tn].end() ){ - registerSygusType( tn ); + Assert( isRegistered( tn ) ); std::map< TypeNode, int > var_count; std::map< int, Node > pre; Node g = mkGeneric( dt, c, var_count, pre ); @@ -2441,7 +2497,7 @@ Node TermDbSygus::getGenericBase( TypeNode tn, const Datatype& dt, int c ) { Node gr = Rewriter::rewrite( g ); Trace("sygus-db-debug") << "Sygus DB : Generic rewritten is " << gr << std::endl; gr = Node::fromExpr( smt::currentSmtEngine()->expandDefinitions( gr.toExpr() ) ); - Trace("sygus-db") << "Sygus DB : Generic base " << dt[c].getName() << " : " << gr << std::endl; + Trace("sygus-db-debug") << "Sygus DB : Generic base " << dt[c].getName() << " : " << gr << std::endl; d_generic_base[tn][c] = gr; return gr; }else{ @@ -2458,7 +2514,7 @@ Node TermDbSygus::mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int if( op.getKind()!=BUILTIN ){ children.push_back( op ); } - Trace("sygus-db") << "mkGeneric " << dt.getName() << " " << op << " " << op.getKind() << "..." << std::endl; + Trace("sygus-db-debug") << "mkGeneric " << dt.getName() << " " << op << " " << op.getKind() << "..." << std::endl; for( int i=0; i<(int)dt[c].getNumArgs(); i++ ){ TypeNode tna = getArgType( dt[c], i ); Node a; @@ -2466,7 +2522,7 @@ Node TermDbSygus::mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int if( it!=pre.end() ){ a = it->second; }else{ - a = getVarInc( tna, var_count ); + a = getFreeVarInc( tna, var_count, true ); } Assert( !a.isNull() ); children.push_back( a ); @@ -2476,7 +2532,7 @@ Node TermDbSygus::mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int ret = NodeManager::currentNM()->mkNode( op, children ); }else{ Kind ok = getOperatorKind( op ); - Trace("sygus-db") << "Operator kind is " << ok << std::endl; + Trace("sygus-db-debug") << "Operator kind is " << ok << std::endl; if( children.size()==1 && ok==kind::UNDEFINED_KIND ){ ret = children[0]; }else{ @@ -2490,34 +2546,50 @@ Node TermDbSygus::mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int */ } } - Trace("sygus-db") << "...returning " << ret << std::endl; + Trace("sygus-db-debug") << "...returning " << ret << std::endl; return ret; } Node TermDbSygus::sygusToBuiltin( Node n, TypeNode tn ) { + Assert( n.getType()==tn ); + Assert( tn.isDatatype() ); std::map< Node, Node >::iterator it = d_sygus_to_builtin[tn].find( n ); if( it==d_sygus_to_builtin[tn].end() ){ Trace("sygus-db-debug") << "SygusToBuiltin : compute for " << n << ", type = " << tn << std::endl; + Node ret; const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); - Assert( n.getKind()==APPLY_CONSTRUCTOR ); - unsigned i = Datatype::indexOf( n.getOperator().toExpr() ); - Assert( n.getNumChildren()==dt[i].getNumArgs() ); - std::map< TypeNode, int > var_count; - std::map< int, Node > pre; - for( unsigned j=0; j<n.getNumChildren(); j++ ){ - pre[j] = sygusToBuiltin( n[j], getArgType( dt[i], j ) ); + if( n.getKind()==APPLY_CONSTRUCTOR ){ + unsigned i = Datatype::indexOf( n.getOperator().toExpr() ); + Assert( n.getNumChildren()==dt[i].getNumArgs() ); + std::map< TypeNode, int > var_count; + std::map< int, Node > pre; + for( unsigned j=0; j<n.getNumChildren(); j++ ){ + pre[j] = sygusToBuiltin( n[j], getArgType( dt[i], j ) ); + } + ret = mkGeneric( dt, i, var_count, pre ); + Trace("sygus-db-debug") << "SygusToBuiltin : Generic is " << ret << std::endl; + ret = Node::fromExpr( smt::currentSmtEngine()->expandDefinitions( ret.toExpr() ) ); + Trace("sygus-db-debug") << "SygusToBuiltin : After expand definitions " << ret << std::endl; + d_sygus_to_builtin[tn][n] = ret; + }else{ + Assert( isFreeVar( n ) ); + //map to builtin variable type + int fv_num = getVarNum( n ); + Assert( !dt.getSygusType().isNull() ); + TypeNode vtn = TypeNode::fromType( dt.getSygusType() ); + ret = getFreeVar( vtn, fv_num ); } - Node ret = mkGeneric( dt, i, var_count, pre ); - Trace("sygus-db-debug") << "SygusToBuiltin : Generic is " << ret << std::endl; - ret = Node::fromExpr( smt::currentSmtEngine()->expandDefinitions( ret.toExpr() ) ); - Trace("sygus-db-debug") << "SygusToBuiltin : After expand definitions " << ret << std::endl; - d_sygus_to_builtin[tn][n] = ret; return ret; }else{ return it->second; } } +Node TermDbSygus::sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args ) { + Assert( d_var_list[tn].size()==args.size() ); + return n.substitute( d_var_list[tn].begin(), d_var_list[tn].end(), args.begin(), args.end() ); +} + //rcons_depth limits the number of recursive calls when doing accelerated constant reconstruction (currently limited to 1000) //this is hacky : depending upon order of calls, constant rcons may succeed, e.g. 1001, 999 vs. 999, 1001 Node TermDbSygus::builtinToSygusConst( Node c, TypeNode tn, int rcons_depth ) { @@ -2537,7 +2609,7 @@ Node TermDbSygus::builtinToSygusConst( Node c, TypeNode tn, int rcons_depth ) { k.setAttribute(spa,c); sc = k; }else{ - int carg = getOpArg( tn, c ); + int carg = getOpConsNum( tn, c ); if( carg!=-1 ){ //sc = Node::fromExpr( dt[carg].getSygusOp() ); sc = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, Node::fromExpr( dt[carg].getConstructor() ) ); @@ -2560,7 +2632,7 @@ Node TermDbSygus::builtinToSygusConst( Node c, TypeNode tn, int rcons_depth ) { //accelerated, recursive reconstruction of constants Kind pk = getPlusKind( TypeNode::fromType( dt.getSygusType() ) ); if( pk!=UNDEFINED_KIND ){ - int arg = getKindArg( tn, pk ); + int arg = getKindConsNum( tn, pk ); if( arg!=-1 ){ Kind ck = getComparisonKind( TypeNode::fromType( dt.getSygusType() ) ); Kind pkm = getPlusKind( TypeNode::fromType( dt.getSygusType() ), true ); @@ -2663,11 +2735,11 @@ Node TermDbSygus::getNormalized( TypeNode t, Node prog, bool do_pre_norm, bool d } } -int TermDbSygus::getSygusTermSize( Node n ){ - if( isVar( n ) ){ +unsigned TermDbSygus::getSygusTermSize( Node n ){ + if( n.getNumChildren()==0 ){ return 0; }else{ - int sum = 0; + unsigned sum = 0; for( unsigned i=0; i<n.getNumChildren(); i++ ){ sum += getSygusTermSize( n[i] ); } @@ -2675,6 +2747,23 @@ int TermDbSygus::getSygusTermSize( Node n ){ } } +unsigned TermDbSygus::getSygusConstructors( Node n, std::vector< Node >& cons ) { + Assert( n.getKind()==APPLY_CONSTRUCTOR ); + Node op = n.getOperator(); + if( std::find( cons.begin(), cons.end(), op )==cons.end() ){ + cons.push_back( op ); + } + if( n.getNumChildren()==0 ){ + return 0; + }else{ + unsigned sum = 0; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + sum += getSygusConstructors( n[i], cons ); + } + return 1+sum; + } +} + bool TermDbSygus::isAntisymmetric( Kind k, Kind& dk ) { if( k==GT ){ dk = LT; @@ -2700,17 +2789,22 @@ bool TermDbSygus::isAntisymmetric( Kind k, Kind& dk ) { } bool TermDbSygus::isIdempotentArg( Node n, Kind ik, int arg ) { + // these should all be binary operators + //Assert( ik!=DIVISION && ik!=INTS_DIVISION && ik!=INTS_MODULUS && ik!=BITVECTOR_UDIV ); TypeNode tn = n.getType(); if( n==getTypeValue( tn, 0 ) ){ - if( ik==PLUS || ik==OR || ik==XOR || ik==BITVECTOR_PLUS || ik==BITVECTOR_OR || ik==BITVECTOR_XOR ){ + if( ik==PLUS || ik==OR || ik==XOR || ik==BITVECTOR_PLUS || ik==BITVECTOR_OR || ik==BITVECTOR_XOR || ik==STRING_CONCAT ){ return true; - }else if( ik==MINUS || ik==BITVECTOR_SHL || ik==BITVECTOR_LSHR || ik==BITVECTOR_SUB ){ + }else if( ik==MINUS || ik==BITVECTOR_SHL || ik==BITVECTOR_LSHR || ik==BITVECTOR_ASHR || ik==BITVECTOR_SUB || + ik==BITVECTOR_UREM || ik==BITVECTOR_UREM_TOTAL ){ return arg==1; } }else if( n==getTypeValue( tn, 1 ) ){ if( ik==MULT || ik==BITVECTOR_MULT ){ return true; - }else if( ik==DIVISION || ik==BITVECTOR_UDIV || ik==BITVECTOR_SDIV ){ + }else if( ik==DIVISION || ik==DIVISION_TOTAL || ik==INTS_DIVISION || ik==INTS_DIVISION_TOTAL || + ik==INTS_MODULUS || ik==INTS_MODULUS_TOTAL || + ik==BITVECTOR_UDIV_TOTAL || ik==BITVECTOR_UDIV || ik==BITVECTOR_SDIV ){ return arg==1; } }else if( n==getTypeMaxValue( tn ) ){ @@ -2722,20 +2816,60 @@ bool TermDbSygus::isIdempotentArg( Node n, Kind ik, int arg ) { } -bool TermDbSygus::isSingularArg( Node n, Kind ik, int arg ) { +Node TermDbSygus::isSingularArg( Node n, Kind ik, int arg ) { TypeNode tn = n.getType(); if( n==getTypeValue( tn, 0 ) ){ if( ik==AND || ik==MULT || ik==BITVECTOR_AND || ik==BITVECTOR_MULT ){ - return true; - }else if( ik==DIVISION || ik==BITVECTOR_UDIV || ik==BITVECTOR_SDIV ){ - return arg==0; + return n; + }else if( ik==BITVECTOR_SHL || ik==BITVECTOR_LSHR || ik==BITVECTOR_ASHR || + ik==BITVECTOR_UREM || ik==BITVECTOR_UREM_TOTAL ){ + if( arg==0 ){ + return n; + } + }else if( ik==BITVECTOR_UDIV_TOTAL || ik==BITVECTOR_UDIV || ik==BITVECTOR_SDIV ){ + if( arg==0 ){ + return n; + }else if( arg==1 ){ + return getTypeMaxValue( tn ); + } + }else if( ik==DIVISION || ik==DIVISION_TOTAL || ik==INTS_DIVISION || ik==INTS_DIVISION_TOTAL || + ik==INTS_MODULUS || ik==INTS_MODULUS_TOTAL ){ + if( arg==0 ){ + return n; + }else{ + //TODO? + } + }else if( ik==STRING_SUBSTR ){ + if( arg==0 ){ + return n; + }else if( arg==2 ){ + return getTypeValue( NodeManager::currentNM()->stringType(), 0 ); + } + }else if( ik==STRING_STRIDOF ){ + if( arg==0 || arg==1 ){ + return getTypeValue( NodeManager::currentNM()->integerType(), -1 ); + } + } + }else if( n==getTypeValue( tn, 1 ) ){ + if( ik==BITVECTOR_UREM_TOTAL ){ + return getTypeValue( tn, 0 ); } }else if( n==getTypeMaxValue( tn ) ){ if( ik==OR || ik==BITVECTOR_OR ){ - return true; + return n; + } + }else{ + if( n.getType().isReal() && n.getConst<Rational>().sgn()<0 ){ + // negative arguments + if( ik==STRING_SUBSTR || ik==STRING_CHARAT ){ + return getTypeValue( NodeManager::currentNM()->stringType(), 0 ); + }else if( ik==STRING_STRIDOF ){ + Assert( arg==2 ); + return getTypeValue( NodeManager::currentNM()->integerType(), -1 ); + } } } - return false; + return Node::null(); } bool TermDbSygus::hasOffsetArg( Kind ik, int arg, int& offset, Kind& ok ) { @@ -2759,6 +2893,392 @@ bool TermDbSygus::hasOffsetArg( Kind ik, int arg, int& offset, Kind& ok ) { } + +class ReqTrie { +public: + ReqTrie() : d_req_kind( UNDEFINED_KIND ){} + std::map< unsigned, ReqTrie > d_children; + Kind d_req_kind; + TypeNode d_req_type; + Node d_req_const; + void print( const char * c, int indent = 0 ){ + if( d_req_kind!=UNDEFINED_KIND ){ + Trace(c) << d_req_kind << " "; + }else if( !d_req_type.isNull() ){ + Trace(c) << d_req_type; + }else if( !d_req_const.isNull() ){ + Trace(c) << d_req_const; + }else{ + Trace(c) << "_"; + } + Trace(c) << std::endl; + for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + for( int i=0; i<=indent; i++ ) { Trace(c) << " "; } + Trace(c) << it->first << " : "; + it->second.print( c, indent+1 ); + } + } + bool satisfiedBy( quantifiers::TermDbSygus * tdb, TypeNode tn ){ + if( !d_req_const.isNull() ){ + if( !tdb->hasConst( tn, d_req_const ) ){ + return false; + } + } + if( !d_req_type.isNull() ){ + if( tn!=d_req_type ){ + return false; + } + } + if( d_req_kind!=UNDEFINED_KIND ){ + int c = tdb->getKindConsNum( tn, d_req_kind ); + if( c!=-1 ){ + bool ret = true; + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + for( std::map< unsigned, ReqTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + if( it->first<dt[c].getNumArgs() ){ + TypeNode tnc = tdb->getArgType( dt[c], it->first ); + if( !it->second.satisfiedBy( tdb, tnc ) ){ + ret = false; + break; + } + }else{ + ret = false; + break; + } + } + if( !ret ){ + return false; + } + // TODO : commutative operators try both? + }else{ + return false; + } + } + return true; + } + bool empty() { + return d_req_kind==UNDEFINED_KIND && d_req_const.isNull() && d_req_type.isNull(); + } +}; + +//this function gets all easy redundant cases, before consulting rewriters +bool TermDbSygus::considerArgKind( TypeNode tn, TypeNode tnp, Kind k, Kind pk, int arg ) { + const Datatype& pdt = ((DatatypeType)(tnp).toType()).getDatatype(); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( hasKind( tn, k ) ); + Assert( hasKind( tnp, pk ) ); + Trace("sygus-sb-debug") << "Consider sygus arg kind " << k << ", pk = " << pk << ", arg = " << arg << "?" << std::endl; + int c = getKindConsNum( tn, k ); + int pc = getKindConsNum( tnp, pk ); + if( k==pk ){ + //check for associativity + if( quantifiers::TermDb::isAssoc( k ) ){ + //if the operator is associative, then a repeated occurrence should only occur in the leftmost argument position + int firstArg = getFirstArgOccurrence( pdt[pc], tn ); + Assert( firstArg!=-1 ); + if( arg!=firstArg ){ + Trace("sygus-sb-simple") << " sb-simple : do not consider " << k << " at child arg " << arg << " of " << k << " since it is associative, with first arg = " << firstArg << std::endl; + return false; + }else{ + return true; + } + } + } + //describes the shape of an alternate term to construct + // we check whether this term is in the sygus grammar below + ReqTrie rt; + Assert( rt.empty() ); + + //construct rt by cases + if( pk==NOT || pk==BITVECTOR_NOT || pk==UMINUS || pk==BITVECTOR_NEG ){ + //negation normal form + if( pk==k ){ + rt.d_req_type = getArgType( dt[c], 0 ); + }else{ + Kind reqk = UNDEFINED_KIND; //required kind for all children + std::map< unsigned, Kind > reqkc; //required kind for some children + if( pk==NOT ){ + if( k==AND ) { + rt.d_req_kind = OR;reqk = NOT; + }else if( k==OR ){ + rt.d_req_kind = AND;reqk = NOT; + //AJR : eliminate this if we eliminate xor + }else if( k==EQUAL ) { + rt.d_req_kind = XOR; + }else if( k==XOR ) { + rt.d_req_kind = EQUAL; + }else if( k==ITE ){ + rt.d_req_kind = ITE;reqkc[1] = NOT;reqkc[2] = NOT; + rt.d_children[0].d_req_type = getArgType( dt[c], 0 ); + }else if( k==LEQ || k==GT ){ + // (not (~ x y)) -----> (~ (+ y 1) x) + rt.d_req_kind = k; + rt.d_children[0].d_req_kind = PLUS; + rt.d_children[0].d_children[0].d_req_type = getArgType( dt[c], 1 ); + rt.d_children[0].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + rt.d_children[1].d_req_type = getArgType( dt[c], 0 ); + //TODO: other possibilities? + }else if( k==LT || k==GEQ ){ + // (not (~ x y)) -----> (~ y (+ x 1)) + rt.d_req_kind = k; + rt.d_children[0].d_req_type = getArgType( dt[c], 1 ); + rt.d_children[1].d_req_kind = PLUS; + rt.d_children[1].d_children[0].d_req_type = getArgType( dt[c], 0 ); + rt.d_children[1].d_children[1].d_req_const = NodeManager::currentNM()->mkConst( Rational( 1 ) ); + } + }else if( pk==BITVECTOR_NOT ){ + if( k==BITVECTOR_AND ) { + rt.d_req_kind = BITVECTOR_OR;reqk = BITVECTOR_NOT; + }else if( k==BITVECTOR_OR ){ + rt.d_req_kind = BITVECTOR_AND;reqk = BITVECTOR_NOT; + }else if( k==BITVECTOR_XNOR ) { + rt.d_req_kind = BITVECTOR_XOR; + }else if( k==BITVECTOR_XOR ) { + rt.d_req_kind = BITVECTOR_XNOR; + } + }else if( pk==UMINUS ){ + if( k==PLUS ){ + rt.d_req_kind = PLUS;reqk = UMINUS; + } + }else if( pk==BITVECTOR_NEG ){ + if( k==PLUS ){ + rt.d_req_kind = PLUS;reqk = BITVECTOR_NEG; + } + } + if( !rt.empty() && ( reqk!=UNDEFINED_KIND || !reqkc.empty() ) ){ + int pcr = getKindConsNum( tnp, rt.d_req_kind ); + if( pcr!=-1 ){ + Assert( pcr<(int)pdt.getNumConstructors() ); + //must have same number of arguments + if( pdt[pcr].getNumArgs()==dt[c].getNumArgs() ){ + for( unsigned i=0; i<pdt[pcr].getNumArgs(); i++ ){ + Kind rk = reqk; + if( reqk==UNDEFINED_KIND ){ + std::map< unsigned, Kind >::iterator itr = reqkc.find( i ); + if( itr!=reqkc.end() ){ + rk = itr->second; + } + } + if( rk!=UNDEFINED_KIND ){ + rt.d_children[i].d_req_kind = rk; + rt.d_children[i].d_children[0].d_req_type = getArgType( dt[c], i ); + } + } + } + } + } + } + }else if( k==MINUS || k==BITVECTOR_SUB ){ + if( pk==EQUAL || + pk==MINUS || pk==BITVECTOR_SUB || + pk==LEQ || pk==LT || pk==GEQ || pk==GT ){ + int oarg = arg==0 ? 1 : 0; + // (~ x (- y z)) ----> (~ (+ x z) y) + // (~ (- y z) x) ----> (~ y (+ x z)) + rt.d_req_kind = pk; + rt.d_children[arg].d_req_type = getArgType( dt[c], 0 ); + rt.d_children[oarg].d_req_kind = k==MINUS ? PLUS : BITVECTOR_PLUS; + rt.d_children[oarg].d_children[0].d_req_type = getArgType( pdt[pc], oarg ); + rt.d_children[oarg].d_children[1].d_req_type = getArgType( dt[c], 1 ); + }else if( pk==PLUS || pk==BITVECTOR_PLUS ){ + // (+ x (- y z)) -----> (- (+ x y) z) + // (+ (- y z) x) -----> (- (+ x y) z) + rt.d_req_kind = pk==PLUS ? MINUS : BITVECTOR_SUB; + int oarg = arg==0 ? 1 : 0; + rt.d_children[0].d_req_kind = pk; + rt.d_children[0].d_children[0].d_req_type = getArgType( pdt[pc], oarg ); + rt.d_children[0].d_children[1].d_req_type = getArgType( dt[c], 0 ); + rt.d_children[1].d_req_type = getArgType( dt[c], 1 ); + // TODO : this is subsumbed by solving for MINUS + } + }else if( k==ITE ){ + if( pk!=ITE ){ + // (o X (ite y z w) X') -----> (ite y (o X z X') (o X w X')) + rt.d_req_kind = ITE; + rt.d_children[0].d_req_type = getArgType( dt[c], 0 ); + unsigned n_args = pdt[pc].getNumArgs(); + for( unsigned r=1; r<=2; r++ ){ + rt.d_children[r].d_req_kind = pk; + for( unsigned q=0; q<n_args; q++ ){ + if( (int)q==arg ){ + rt.d_children[r].d_children[q].d_req_type = getArgType( dt[c], r ); + }else{ + rt.d_children[r].d_children[q].d_req_type = getArgType( pdt[pc], q ); + } + } + } + //TODO: this increases term size but is probably a good idea + } + }else if( k==NOT ){ + if( pk==ITE ){ + // (ite (not y) z w) -----> (ite y w z) + rt.d_req_kind = ITE; + rt.d_children[0].d_req_type = getArgType( dt[c], 0 ); + rt.d_children[1].d_req_type = getArgType( pdt[pc], 2 ); + rt.d_children[2].d_req_type = getArgType( pdt[pc], 1 ); + } + } + Trace("sygus-sb-debug") << "Consider sygus arg kind " << k << ", pk = " << pk << ", arg = " << arg << "?" << std::endl; + if( !rt.empty() ){ + rt.print("sygus-sb-debug"); + //check if it meets the requirements + if( rt.satisfiedBy( this, tnp ) ){ + Trace("sygus-sb-debug") << "...success!" << std::endl; + Trace("sygus-sb-simple") << " sb-simple : do not consider " << k << " as arg " << arg << " of " << pk << std::endl; + //do not need to consider the kind in the search since there are ways to construct equivalent terms + return false; + }else{ + Trace("sygus-sb-debug") << "...failed." << std::endl; + } + Trace("sygus-sb-debug") << std::endl; + } + //must consider this kind in the search + return true; +} + +bool TermDbSygus::considerConst( TypeNode tn, TypeNode tnp, Node c, Kind pk, int arg ) { + const Datatype& pdt = ((DatatypeType)(tnp).toType()).getDatatype(); + // child grammar-independent + if( !considerConst( pdt, tnp, c, pk, arg ) ){ + return false; + } + // TODO : this can probably be made child grammar independent + int pc = getKindConsNum( tnp, pk ); + if( pdt[pc].getNumArgs()==2 ){ + Kind ok; + int offset; + if( hasOffsetArg( pk, arg, offset, ok ) ){ + Trace("sygus-sb-simple-debug") << pk << " has offset arg " << ok << " " << offset << std::endl; + int ok_arg = getKindConsNum( tnp, ok ); + if( ok_arg!=-1 ){ + Trace("sygus-sb-simple-debug") << "...at argument " << ok_arg << std::endl; + //other operator be the same type + if( isTypeMatch( pdt[ok_arg], pdt[arg] ) ){ + int status; + Node co = getTypeValueOffset( c.getType(), c, offset, status ); + Trace("sygus-sb-simple-debug") << c << " with offset " << offset << " is " << co << ", status=" << status << std::endl; + if( status==0 && !co.isNull() ){ + if( hasConst( tn, co ) ){ + Trace("sygus-sb-simple") << " sb-simple : by offset reasoning, do not consider const " << c; + Trace("sygus-sb-simple") << " as arg " << arg << " of " << pk << " since we can use " << co << " under " << ok << " " << std::endl; + return false; + } + } + } + } + } + } + return true; +} + +bool TermDbSygus::considerConst( const Datatype& pdt, TypeNode tnp, Node c, Kind pk, int arg ) { + Assert( hasKind( tnp, pk ) ); + int pc = getKindConsNum( tnp, pk ); + bool ret = true; + Trace("sygus-sb-debug") << "Consider sygus const " << c << ", parent = " << pk << ", arg = " << arg << "?" << std::endl; + if( isIdempotentArg( c, pk, arg ) ){ + if( pdt[pc].getNumArgs()==2 ){ + int oarg = arg==0 ? 1 : 0; + TypeNode otn = TypeNode::fromType( ((SelectorType)pdt[pc][oarg].getType()).getRangeType() ); + if( otn==tnp ){ + Trace("sygus-sb-simple") << " sb-simple : " << c << " is idempotent arg " << arg << " of " << pk << "..." << std::endl; + ret = false; + } + } + }else{ + Node sc = isSingularArg( c, pk, arg ); + if( !sc.isNull() ){ + if( hasConst( tnp, sc ) ){ + Trace("sygus-sb-simple") << " sb-simple : " << c << " is singular arg " << arg << " of " << pk << ", evaluating to " << sc << "..." << std::endl; + ret = false; + } + } + } + if( ret ){ + ReqTrie rt; + Assert( rt.empty() ); + Node max_c = getTypeMaxValue( c.getType() ); + Node zero_c = getTypeValue( c.getType(), 0 ); + Node one_c = getTypeValue( c.getType(), 1 ); + if( pk==XOR || pk==BITVECTOR_XOR ){ + if( c==max_c ){ + rt.d_req_kind = pk==XOR ? NOT : BITVECTOR_NOT; + } + }else if( pk==ITE ){ + if( arg==0 ){ + if( c==max_c ){ + rt.d_children[2].d_req_type = tnp; + }else if( c==zero_c ){ + rt.d_children[1].d_req_type = tnp; + } + } + }else if( pk==STRING_SUBSTR ){ + if( c==one_c ){ + rt.d_req_kind = STRING_CHARAT; + rt.d_children[0].d_req_type = getArgType( pdt[pc], 0 ); + rt.d_children[1].d_req_type = getArgType( pdt[pc], 1 ); + } + } + if( !rt.empty() ){ + //check if satisfied + if( rt.satisfiedBy( this, tnp ) ){ + Trace("sygus-sb-simple") << " sb-simple : do not consider const " << c << " as arg " << arg << " of " << pk; + Trace("sygus-sb-simple") << " in " << ((DatatypeType)tnp.toType()).getDatatype().getName() << std::endl; + //do not need to consider the constant in the search since there are ways to construct equivalent terms + ret = false; + } + } + } + // TODO : cache? + return ret; +} + +int TermDbSygus::solveForArgument( TypeNode tn, unsigned cindex, unsigned arg ) { + // FIXME + return -1; // TODO : if using, modify considerArgKind above + Assert( isRegistered( tn ) ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( cindex<dt.getNumConstructors() ); + Assert( arg<dt[cindex].getNumArgs() ); + Kind nk = getConsNumKind( tn, cindex ); + TypeNode tnc = getArgType( dt[cindex], arg ); + const Datatype& cdt = ((DatatypeType)(tnc).toType()).getDatatype(); + + ReqTrie rt; + Assert( rt.empty() ); + int solve_ret = -1; + if( nk==MINUS || nk==BITVECTOR_SUB ){ + if( dt[cindex].getNumArgs()==2 && arg==0 ){ + TypeNode tnco = getArgType( dt[cindex], 1 ); + Node builtin = getTypeValue( sygusToBuiltinType( tnc ), 0 ); + solve_ret = getConstConsNum( tn, builtin ); + if( solve_ret!=-1 ){ + // t - s -----> ( 0 - s ) + t + rt.d_req_kind = MINUS ? PLUS : BITVECTOR_PLUS; + rt.d_children[0].d_req_type = tn; // avoid? + rt.d_children[0].d_req_kind = nk; + rt.d_children[0].d_children[0].d_req_const = builtin; + rt.d_children[0].d_children[0].d_req_type = tnco; + rt.d_children[1].d_req_type = tnc; + // TODO : this can be made more general for multiple type grammars to remove MINUS entirely + } + } + } + + if( !rt.empty() ){ + Assert( solve_ret>=0 ); + Assert( solve_ret<=(int)cdt.getNumConstructors() ); + //check if satisfied + if( rt.satisfiedBy( this, tn ) ){ + Trace("sygus-sb-simple") << " sb-simple : ONLY consider " << cdt[solve_ret].getSygusOp() << " as arg " << arg << " of " << nk; + Trace("sygus-sb-simple") << " in " << ((DatatypeType)tn.toType()).getDatatype().getName() << std::endl; + return solve_ret; + } + } + + return -1; +} + Node TermDbSygus::getTypeValue( TypeNode tn, int val ) { std::map< int, Node >::iterator it = d_type_value[tn].find( val ); if( it==d_type_value[tn].end() ){ @@ -2774,6 +3294,10 @@ Node TermDbSygus::getTypeValue( TypeNode tn, int val ) { if( val==0 ){ n = d_false; } + }else if( tn.isString() ){ + if( val==0 ){ + n = NodeManager::currentNM()->mkConst( ::CVC4::String("") ); + } } d_type_value[tn][val] = n; return n; @@ -2810,6 +3334,7 @@ Node TermDbSygus::getTypeValueOffset( TypeNode tn, Node val, int offset, int& st status = 0; }else if( tn.isBitVector() ){ val_o = Rewriter::rewrite( NodeManager::currentNM()->mkNode( BITVECTOR_PLUS, val, offset_val ) ); + // TODO : enable? watch for overflows } } d_type_value_offset[tn][val][offset] = val_o; @@ -2833,17 +3358,71 @@ struct sortConstants { } }; -void TermDbSygus::registerSygusType( TypeNode tn ){ - if( d_register.find( tn )==d_register.end() ){ - if( !tn.isDatatype() ){ - d_register[tn] = TypeNode::null(); +class ReconstructTrie { +public: + std::map< Node, ReconstructTrie > d_children; + std::vector< Node > d_reconstruct; + void add( std::vector< Node >& cons, Node r, unsigned index = 0 ){ + if( index==cons.size() ){ + d_reconstruct.push_back( r ); }else{ + d_children[cons[index]].add( cons, r, index+1 ); + } + } + Node getReconstruct( std::map< Node, int >& rcons, unsigned depth ) { + if( !d_reconstruct.empty() ){ + for( unsigned i=0; i<d_reconstruct.size(); i++ ){ + Node r = d_reconstruct[i]; + if( rcons[r]==0 ){ + Trace("sygus-static-enum") << "...eliminate constructor " << r << std::endl; + rcons[r] = 1; + return r; + } + } + } + if( depth>0 ){ + for( unsigned w=0; w<2; w++ ){ + for( std::map< Node, ReconstructTrie >::iterator it = d_children.begin(); it != d_children.end(); ++it ){ + Node n = it->first; + if( ( w==0 && rcons[n]!=0 ) || ( w==1 && rcons[n]==0 ) ){ + Node r = it->second.getReconstruct( rcons, depth - w ); + if( !r.isNull() ){ + if( w==1 ){ + Trace("sygus-static-enum") << "...use " << n << " to eliminate constructor " << r << std::endl; + rcons[n] = -1; + } + return r; + } + } + } + } + } + return Node::null(); + } +}; + +void TermDbSygus::registerSygusType( TypeNode tn ) { + std::map< TypeNode, TypeNode >::iterator itr = d_register.find( tn ); + if( itr==d_register.end() ){ + d_register[tn] = TypeNode::null(); + if( tn.isDatatype() ){ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); Trace("sygus-db") << "Register type " << dt.getName() << "..." << std::endl; - d_register[tn] = TypeNode::fromType( dt.getSygusType() ); - if( d_register[tn].isNull() ){ - Trace("sygus-db") << "...not sygus." << std::endl; - }else{ + TypeNode btn = TypeNode::fromType( dt.getSygusType() ); + d_register[tn] = btn; + if( !d_register[tn].isNull() ){ + // get the sygus variable list + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + if( !var_list.isNull() ){ + for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ + Node sv = var_list[j]; + SygusVarNumAttribute svna; + sv.setAttribute( svna, j ); + d_var_list[tn].push_back( sv ); + } + }else{ + // no arguments to synthesis functions + } //for constant reconstruction Kind ck = getComparisonKind( TypeNode::fromType( dt.getSygusType() ) ); Node z = getTypeValue( TypeNode::fromType( dt.getSygusType() ), 0 ); @@ -2896,11 +3475,250 @@ void TermDbSygus::registerSygusType( TypeNode tn ){ registerSygusType( getArgType( dt[i], j ) ); } } + + //compute the redundant operators + if( options::sygusMinGrammar() ){ + for( unsigned i=0; i<dt.getNumConstructors(); i++ ){ + bool nred = true; + Trace("sygus-split-debug") << "Is " << dt[i].getName() << " a redundant operator?" << std::endl; + Kind ck = getConsNumKind( tn, i ); + if( ck!=UNDEFINED_KIND ){ + Kind dk; + if( isAntisymmetric( ck, dk ) ){ + int j = getKindConsNum( tn, dk ); + if( j!=-1 ){ + Trace("sygus-split-debug") << "Possible redundant operator : " << ck << " with " << dk << std::endl; + //check for type mismatches + bool success = true; + for( unsigned k=0; k<2; k++ ){ + unsigned ko = k==0 ? 1 : 0; + TypeNode tni = TypeNode::fromType( ((SelectorType)dt[i][k].getType()).getRangeType() ); + TypeNode tnj = TypeNode::fromType( ((SelectorType)dt[j][ko].getType()).getRangeType() ); + if( tni!=tnj ){ + Trace("sygus-split-debug") << "Argument types " << tni << " and " << tnj << " are not equal." << std::endl; + success = false; + break; + } + } + if( success ){ + Trace("sygus-nf") << "* Sygus norm " << dt.getName() << " : do not consider any " << ck << " terms." << std::endl; + nred = false; + } + } + } + } + if( nred ){ + Trace("sygus-split-debug") << "Check " << dt[i].getName() << " based on generic rewriting" << std::endl; + std::map< TypeNode, int > var_count; + std::map< int, Node > pre; + Node g = mkGeneric( dt, i, var_count, pre ); + nred = !computeGenericRedundant( tn, g ); + Trace("sygus-split-debug") << "...done check " << dt[i].getName() << " based on generic rewriting" << std::endl; + } + d_sygus_red_status[tn].push_back( nred ? 0 : 1 ); + } + // run an enumerator for this type + if( options::sygusMinGrammarAgg() ){ + TypeEnumerator te(tn); + unsigned count = 0; + std::map< Node, std::vector< Node > > builtin_to_orig; + Trace("sygus-static-enum") << "Static enumerate " << dt.getName() << "..." << std::endl; + while( !te.isFinished() && count<1000 ){ + Node n = *te; + Node bn = sygusToBuiltin( n, tn ); + Trace("sygus-static-enum") << " " << bn; + Node bnr = Rewriter::rewrite( bn ); + Trace("sygus-static-enum") << " ..." << bnr << std::endl; + builtin_to_orig[bnr].push_back( n ); + ++te; + count++; + } + std::map< Node, bool > reserved; + for( unsigned i=0; i<=2; i++ ){ + Node rsv = i==2 ? getTypeMaxValue( btn ) : getTypeValue( btn, i ); + if( !rsv.isNull() ){ + reserved[ rsv ] = true; + } + } + Trace("sygus-static-enum") << "...make the reconstruct index data structure..." << std::endl; + ReconstructTrie rt; + std::map< Node, int > rcons; + unsigned max_depth = 0; + for( std::map< Node, std::vector< Node > >::iterator itb = builtin_to_orig.begin(); itb != builtin_to_orig.end(); ++itb ){ + if( itb->second.size()>0 ){ + std::map< Node, std::vector< Node > > clist; + Node single_cons; + for( unsigned j=0; j<itb->second.size(); j++ ){ + Node e = itb->second[j]; + getSygusConstructors( e, clist[e] ); + if( clist[e].size()>max_depth ){ + max_depth = clist[e].size(); + } + for( unsigned k=0; k<clist[e].size(); k++ ){ + /* + unsigned cindex = Datatype::indexOf( clist[e][k].toExpr() ); + if( isGenericRedundant( tn, cindex ) ){ + is_gen_redundant = true; + break; + }else{ + */ + rcons[clist[e][k]] = 0; + } + //if( is_gen_redundant ){ + // clist.erase( e ); + //}else{ + if( clist[e].size()==1 ){ + Trace("sygus-static-enum") << "...single constructor term : " << e << ", builtin is " << itb->first << ", cons is " << clist[e][0] << std::endl; + if( single_cons.isNull() ){ + single_cons = clist[e][0]; + }else{ + Trace("sygus-static-enum") << "*** already can eliminate constructor " << clist[e][0] << std::endl; + unsigned cindex = Datatype::indexOf( clist[e][0].toExpr() ); + d_sygus_red_status[tn][cindex] = 1; + } + } + //} + } + // do not eliminate 0, 1, or max + if( !single_cons.isNull() && reserved.find( itb->first )==reserved.end() ){ + Trace("sygus-static-enum") << "...possibly elim " << single_cons << std::endl; + for( std::map< Node, std::vector< Node > >::iterator itc = clist.begin(); itc != clist.end(); ++itc ){ + if( std::find( itc->second.begin(), itc->second.end(), single_cons )==itc->second.end() ){ + rt.add( itc->second, single_cons ); + } + } + } + } + } + Trace("sygus-static-enum") << "...compute reconstructions..." << std::endl; + Node next_rcons; + do { + unsigned depth = 0; + do{ + next_rcons = rt.getReconstruct( rcons, depth ); + depth++; + }while( next_rcons.isNull() && depth<=max_depth ); + // if we found a constructor to eliminate + if( !next_rcons.isNull() ){ + Trace("sygus-static-enum") << "*** eliminate constructor " << next_rcons << std::endl; + unsigned cindex = Datatype::indexOf( next_rcons.toExpr() ); + d_sygus_red_status[tn][cindex] = 2; + } + }while( !next_rcons.isNull() ); + Trace("sygus-static-enum") << "...finished..." << std::endl; + } + }else{ + // assume all are non-redundant + for( unsigned i=0; i<dt.getNumConstructors(); i++ ){ + d_sygus_red_status[tn].push_back( 0 ); + } + } } } } } +void TermDbSygus::registerMeasuredTerm( Node e, Node root, bool mkActiveGuard ) { + Assert( d_measured_term.find( e )==d_measured_term.end() ); + Trace("sygus-db") << "Register measured term : " << e << " with root " << root << std::endl; + d_measured_term[e] = root; + if( mkActiveGuard ){ + // make the guard + Node eg = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "eG", NodeManager::currentNM()->booleanType() ) ); + eg = d_quantEngine->getValuation().ensureLiteral( eg ); + AlwaysAssert( !eg.isNull() ); + d_quantEngine->getOutputChannel().requirePhase( eg, true ); + //add immediate lemma + Node lem = NodeManager::currentNM()->mkNode( OR, eg, eg.negate() ); + Trace("cegqi-lemma") << "Cegqi::Lemma : enumerator : " << lem << std::endl; + d_quantEngine->getOutputChannel().lemma( lem ); + d_measured_term_active_guard[e] = eg; + } +} + +void TermDbSygus::registerPbeExamples( Node e, std::vector< std::vector< Node > >& exs, + std::vector< Node >& exos, std::vector< Node >& exts ) { + Trace("sygus-db") << "Register " << exs.size() << " PBE examples with " << e << std::endl; + Assert( d_measured_term.find( e )==d_measured_term.end() || isMeasuredTerm( e )==e ); + Assert( d_pbe_exs.find( e )==d_pbe_exs.end() ); + Assert( exs.size()==exos.size() ); + d_pbe_exs[e] = exs; + d_pbe_exos[e] = exos; + for( unsigned i=0; i<exts.size(); i++ ){ + Trace("sygus-db-debug") << " # " << i << " : " << exts[i] << std::endl; + Assert( exts[i].getKind()==APPLY_UF ); + Assert( exts[i][0]==e ); + d_pbe_term_id[exts[i]] = i; + } +} + +Node TermDbSygus::isMeasuredTerm( Node e ) { + std::map< Node, Node >::iterator itm = d_measured_term.find( e ); + if( itm!=d_measured_term.end() ){ + return itm->second; + }else{ + return Node::null(); + } +} + +Node TermDbSygus::getActiveGuardForMeasureTerm( Node e ) { + std::map< Node, Node >::iterator itag = d_measured_term_active_guard.find( e ); + if( itag!=d_measured_term_active_guard.end() ){ + return itag->second; + }else{ + return Node::null(); + } +} + +void TermDbSygus::getMeasuredTerms( std::vector< Node >& mts ) { + for( std::map< Node, Node >::iterator itm = d_measured_term.begin(); itm != d_measured_term.end(); ++itm ){ + mts.push_back( itm->first ); + } +} + +bool TermDbSygus::hasPbeExamples( Node e ) { + return d_pbe_exs.find( e )!=d_pbe_exs.end(); +} + +unsigned TermDbSygus::getNumPbeExamples( Node e ) { + std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_pbe_exs.find( e ); + if( it!=d_pbe_exs.end() ){ + return it->second.size(); + }else{ + return 0; + } +} + +void TermDbSygus::getPbeExample( Node e, unsigned i, std::vector< Node >& ex ) { + std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_pbe_exs.find( e ); + if( it!=d_pbe_exs.end() ){ + Assert( i<it->second.size() ); + Assert( i<d_pbe_exos[e].size() ); + ex.insert( ex.end(), it->second[i].begin(), it->second[i].end() ); + }else{ + Assert( false ); + } +} +Node TermDbSygus::getPbeExampleOut( Node e, unsigned i ) { + std::map< Node, std::vector< Node > >::iterator it = d_pbe_exos.find( e ); + if( it!=d_pbe_exos.end() ){ + Assert( i<it->second.size() ); + return it->second[i]; + }else{ + Assert( false ); + return Node::null(); + } +} + +int TermDbSygus::getPbeExampleId( Node n ) { + std::map< Node, unsigned >::iterator it = d_pbe_term_id.find( n ); + if( it!=d_pbe_term_id.end() ){ + return it->second; + }else{ + return -1; + } +} + bool TermDbSygus::isRegistered( TypeNode tn ) { return d_register.find( tn )!=d_register.end(); } @@ -2910,7 +3728,76 @@ TypeNode TermDbSygus::sygusToBuiltinType( TypeNode tn ) { return d_register[tn]; } -int TermDbSygus::getKindArg( TypeNode tn, Kind k ) { +void TermDbSygus::computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth ) { + std::map< TypeNode, unsigned >::iterator it = d_min_type_depth[root_tn].find( tn ); + if( it==d_min_type_depth[root_tn].end() || type_depth<it->second ){ + d_min_type_depth[root_tn][tn] = type_depth; + Assert( tn.isDatatype() ); + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + //compute for connected types + for( unsigned i=0; i<dt.getNumConstructors(); i++ ){ + for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){ + computeMinTypeDepthInternal( root_tn, getArgType( dt[i], j ), type_depth+1 ); + } + } + } +} + +unsigned TermDbSygus::getMinTypeDepth( TypeNode root_tn, TypeNode tn ){ + std::map< TypeNode, unsigned >::iterator it = d_min_type_depth[root_tn].find( tn ); + if( it==d_min_type_depth[root_tn].end() ){ + computeMinTypeDepthInternal( root_tn, root_tn, 0 ); + Assert( d_min_type_depth[root_tn].find( tn )!=d_min_type_depth[root_tn].end() ); + return d_min_type_depth[root_tn][tn]; + }else{ + return it->second; + } +} + +unsigned TermDbSygus::getMinTermSize( TypeNode tn ) { + Assert( isRegistered( tn ) ); + std::map< TypeNode, unsigned >::iterator it = d_min_term_size.find( tn ); + if( it==d_min_term_size.end() ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + for( unsigned i=0; i<dt.getNumConstructors(); i++ ){ + if( !isGenericRedundant( tn, i ) ){ + if( dt[i].getNumArgs()==0 ){ + d_min_term_size[tn] = 0; + return 0; + } + } + } + // TODO : improve + d_min_term_size[tn] = 1; + return 1; + }else{ + return it->second; + } +} + +unsigned TermDbSygus::getMinConsTermSize( TypeNode tn, unsigned cindex ) { + Assert( isRegistered( tn ) ); + Assert( !isGenericRedundant( tn, cindex ) ); + std::map< unsigned, unsigned >::iterator it = d_min_cons_term_size[tn].find( cindex ); + if( it==d_min_cons_term_size[tn].end() ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + Assert( cindex<dt.getNumConstructors() ); + unsigned ret = 0; + if( dt[cindex].getNumArgs()>0 ){ + ret = 1; + for( unsigned i=0; i<dt[cindex].getNumArgs(); i++ ){ + ret += getMinTermSize( getArgType( dt[cindex], i ) ); + } + } + d_min_cons_term_size[tn][cindex] = ret; + return ret; + }else{ + return it->second; + } +} + + +int TermDbSygus::getKindConsNum( TypeNode tn, Kind k ) { Assert( isRegistered( tn ) ); std::map< TypeNode, std::map< Kind, int > >::iterator itt = d_kinds.find( tn ); if( itt!=d_kinds.end() ){ @@ -2922,7 +3809,7 @@ int TermDbSygus::getKindArg( TypeNode tn, Kind k ) { return -1; } -int TermDbSygus::getConstArg( TypeNode tn, Node n ){ +int TermDbSygus::getConstConsNum( TypeNode tn, Node n ){ Assert( isRegistered( tn ) ); std::map< TypeNode, std::map< Node, int > >::iterator itt = d_consts.find( tn ); if( itt!=d_consts.end() ){ @@ -2934,7 +3821,7 @@ int TermDbSygus::getConstArg( TypeNode tn, Node n ){ return -1; } -int TermDbSygus::getOpArg( TypeNode tn, Node n ) { +int TermDbSygus::getOpConsNum( TypeNode tn, Node n ) { std::map< Node, int >::iterator it = d_ops[tn].find( n ); if( it!=d_ops[tn].end() ){ return it->second; @@ -2944,16 +3831,16 @@ int TermDbSygus::getOpArg( TypeNode tn, Node n ) { } bool TermDbSygus::hasKind( TypeNode tn, Kind k ) { - return getKindArg( tn, k )!=-1; + return getKindConsNum( tn, k )!=-1; } bool TermDbSygus::hasConst( TypeNode tn, Node n ) { - return getConstArg( tn, n )!=-1; + return getConstConsNum( tn, n )!=-1; } bool TermDbSygus::hasOp( TypeNode tn, Node n ) { - return getOpArg( tn, n )!=-1; + return getOpConsNum( tn, n )!=-1; } -Node TermDbSygus::getArgOp( TypeNode tn, int i ) { +Node TermDbSygus::getConsNumOp( TypeNode tn, int i ) { Assert( isRegistered( tn ) ); std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_ops.find( tn ); if( itt!=d_arg_ops.end() ){ @@ -2965,7 +3852,7 @@ Node TermDbSygus::getArgOp( TypeNode tn, int i ) { return Node::null(); } -Node TermDbSygus::getArgConst( TypeNode tn, int i ) { +Node TermDbSygus::getConsNumConst( TypeNode tn, int i ) { Assert( isRegistered( tn ) ); std::map< TypeNode, std::map< int, Node > >::iterator itt = d_arg_const.find( tn ); if( itt!=d_arg_const.end() ){ @@ -2977,7 +3864,7 @@ Node TermDbSygus::getArgConst( TypeNode tn, int i ) { return Node::null(); } -Kind TermDbSygus::getArgKind( TypeNode tn, int i ) { +Kind TermDbSygus::getConsNumKind( TypeNode tn, int i ) { Assert( isRegistered( tn ) ); std::map< TypeNode, std::map< int, Kind > >::iterator itt = d_arg_kind.find( tn ); if( itt!=d_arg_kind.end() ){ @@ -2990,7 +3877,7 @@ Kind TermDbSygus::getArgKind( TypeNode tn, int i ) { } bool TermDbSygus::isKindArg( TypeNode tn, int i ) { - return getArgKind( tn, i )!=UNDEFINED_KIND; + return getConsNumKind( tn, i )!=UNDEFINED_KIND; } bool TermDbSygus::isConstArg( TypeNode tn, int i ) { @@ -3016,6 +3903,30 @@ TypeNode TermDbSygus::getArgType( const DatatypeConstructor& c, int i ) { return TypeNode::fromType( ((SelectorType)c[i].getType()).getRangeType() ); } +/** get first occurrence */ +int TermDbSygus::getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn ) { + for( unsigned i=0; i<c.getNumArgs(); i++ ){ + TypeNode tni = getArgType( c, i ); + if( tni==tn ){ + return i; + } + } + return -1; +} + +bool TermDbSygus::isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 ) { + if( c1.getNumArgs()!=c2.getNumArgs() ){ + return false; + }else{ + for( unsigned i=0; i<c1.getNumArgs(); i++ ){ + if( getArgType( c1, i )!=getArgType( c2, i ) ){ + return false; + } + } + return true; + } +} + Node TermDbSygus::minimizeBuiltinTerm( Node n ) { if( ( n.getKind()==EQUAL || n.getKind()==LEQ || n.getKind()==LT || n.getKind()==GEQ || n.getKind()==GT ) && ( n[0].getType().isInteger() || n[0].getType().isReal() ) ){ @@ -3101,6 +4012,49 @@ bool TermDbSygus::doCompare( Node a, Node b, Kind k ) { return com==d_true; } +Node TermDbSygus::getSemanticSkolem( TypeNode tn, Node n, bool doMk ){ + std::map< Node, Node >::iterator its = d_semantic_skolem[tn].find( n ); + if( its!=d_semantic_skolem[tn].end() ){ + return its->second; + }else if( doMk ){ + Node ss = NodeManager::currentNM()->mkSkolem( "sem", tn, "semantic skolem for sygus" ); + d_semantic_skolem[tn][n] = ss; + return ss; + }else{ + return Node::null(); + } +} + +bool TermDbSygus::involvesDivByZero( Node n, std::map< Node, bool >& visited ){ + if( visited.find( n )==visited.end() ){ + visited[n] = true; + Kind k = n.getKind(); + if( k==DIVISION || k==DIVISION_TOTAL || k==INTS_DIVISION || k==INTS_DIVISION_TOTAL || + k==INTS_MODULUS || k==INTS_MODULUS_TOTAL ){ + if( n[1].isConst() ){ + if( n[1]==getTypeValue( n[1].getType(), 0 ) ){ + return true; + } + }else{ + // if it has free variables it might be a non-zero constant + if( !hasFreeVar( n[1] ) ){ + return true; + } + } + } + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + if( involvesDivByZero( n[i], visited ) ){ + return true; + } + } + } + return false; +} + +bool TermDbSygus::involvesDivByZero( Node n ) { + std::map< Node, bool > visited; + return involvesDivByZero( n, visited ); +} void doStrReplace(std::string& str, const std::string& oldStr, const std::string& newStr){ size_t pos = 0; @@ -3227,6 +4181,15 @@ Node TermDbSygus::getAnchor( Node n ) { } } +unsigned TermDbSygus::getAnchorDepth( Node n ) { + if( n.getKind()==APPLY_SELECTOR_TOTAL ){ + return 1+getAnchorDepth( n[0] ); + }else{ + return 0; + } +} + + void TermDbSygus::registerEvalTerm( Node n ) { if( options::sygusDirectEval() ){ if( n.getKind()==APPLY_UF && !n.getType().isBoolean() ){ @@ -3238,6 +4201,15 @@ void TermDbSygus::registerEvalTerm( Node n ) { Node f = n.getOperator(); Trace("sygus-eager") << "...the evaluation function is : " << f << std::endl; if( n[0].getKind()!=APPLY_CONSTRUCTOR ){ + // check if it directly occurs in an input/ouput example + int pbe_id = getPbeExampleId( n ); + if( pbe_id!=-1 ){ + Node n_res = getPbeExampleOut( n[0], pbe_id ); + if( !n_res.isNull() ){ + Trace("sygus-eager") << "......do not evaluate " << n << " since it is an input/output example : " << n_res << std::endl; + return; + } + } d_evals[n[0]].push_back( n ); TypeNode tn = n[0].getType(); Assert( tn.isDatatype() ); @@ -3245,15 +4217,14 @@ void TermDbSygus::registerEvalTerm( Node n ) { Node var_list = Node::fromExpr( dt.getSygusVarList() ); Assert( dt.isSygus() ); d_eval_args[n[0]].push_back( std::vector< Node >() ); + bool isConst = true; for( unsigned j=1; j<n.getNumChildren(); j++ ){ - //if( var_list[j-1].getType().isBoolean() ){ - // //TODO: remove this case when boolean term conversion is eliminated - // Node c = NodeManager::currentNM()->mkConst(BitVector(1u, 1u)); - // d_eval_args[n[0]].back().push_back( n[j].eqNode( c ) ); - //}else{ - d_eval_args[n[0]].back().push_back( n[j] ); - //} + d_eval_args[n[0]].back().push_back( n[j] ); + if( !n[j].isConst() ){ + isConst = false; + } } + d_eval_args_const[n[0]].push_back( isConst ); Node a = getAnchor( n[0] ); d_subterms[a][n[0]] = true; } @@ -3277,7 +4248,11 @@ void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& terms Node vn = n.substitute( at, vt ); vn = Rewriter::rewrite( vn ); unsigned start = d_node_mv_args_proc[n][vn]; - Node antec = n.eqNode( vn ); + // get explanation in terms of testers + std::vector< Node > antec_exp; + getExplanationForConstantEquality( n, vn, antec_exp ); + Node antec = antec_exp.size()==1 ? antec_exp[0] : NodeManager::currentNM()->mkNode( kind::AND, antec_exp ); + //Node antec = n.eqNode( vn ); TypeNode tn = n.getType(); Assert( tn.isDatatype() ); const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); @@ -3291,20 +4266,78 @@ void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& terms for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ vars.push_back( var_list[j] ); } + //evaluation children + std::vector< Node > eval_children; + eval_children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) ); + eval_children.push_back( n ); //for each evaluation for( unsigned i=start; i<it->second.size(); i++ ){ - Assert( vars.size()==it->second[i].size() ); - Node sBTerm = bTerm.substitute( vars.begin(), vars.end(), it->second[i].begin(), it->second[i].end() ); - sBTerm = Rewriter::rewrite( sBTerm ); - //Node lem = NodeManager::currentNM()->mkNode(EQUAL, d_evals[n][i], sBTerm ); - //lem = NodeManager::currentNM()->mkNode( OR, antec.negate(), lem ); + Node res; + Node expn; + // unfold? + bool do_unfold = false; + if( options::sygusUnfoldBool() ){ + if( bTerm.getKind()==ITE || bTerm.getType().isBoolean() ){ + do_unfold = true; + } + } + if( do_unfold ){ + // TODO : this is replicated for different values, possibly do better caching + std::map< Node, Node > vtm; + std::vector< Node > exp; + vtm[n] = vn; + eval_children.insert( eval_children.end(), it->second[i].begin(), it->second[i].end() ); + Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children ); + eval_children.resize( 2 ); + res = unfold( eval_fun, vtm, exp ); + expn = exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp ); + }else{ + + EvalSygusInvarianceTest esit; + eval_children.insert( eval_children.end(), it->second[i].begin(), it->second[i].end() ); + esit.d_conj = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children ); + esit.d_var = n; + eval_children[1] = vn; + Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children ); + esit.d_result = evaluateWithUnfolding( eval_fun ); + res = esit.d_result; + eval_children.resize( 2 ); + eval_children[1] = n; + + //evaluate with minimal explanation + std::vector< Node > mexp; + getExplanationFor( n, vn, mexp, esit ); + Assert( !mexp.empty() ); + expn = mexp.size()==1 ? mexp[0] : NodeManager::currentNM()->mkNode( kind::AND, mexp ); + + //if all constant, we can use evaluation to minimize the explanation + //Assert( i<d_eval_args_const[n].size() ); + //if( d_eval_args_const[n][i] ){ + /* + std::map< Node, Node > vtm; + std::map< Node, Node > visited; + std::map< Node, std::vector< Node > > exp; + vtm[n] = vn; + res = crefEvaluate( eval_fun, vtm, visited, exp ); + Assert( !exp[eval_fun].empty() ); + expn = exp[eval_fun].size()==1 ? exp[eval_fun][0] : NodeManager::currentNM()->mkNode( kind::AND, exp[eval_fun] ); + */ + /* + //otherwise, just do a substitution + }else{ + Assert( vars.size()==it->second[i].size() ); + res = bTerm.substitute( vars.begin(), vars.end(), it->second[i].begin(), it->second[i].end() ); + res = Rewriter::rewrite( res ); + expn = antec; + } + */ + } + Assert( !res.isNull() ); terms.push_back( d_evals[n][i] ); - vals.push_back( sBTerm ); - exps.push_back( antec ); - Trace("sygus-eager") << "Conclude : " << d_evals[n][i] << " == " << sBTerm << std::endl; - Trace("sygus-eager") << " from " << antec << std::endl; - //Trace("sygus-eager") << "Lemma : " << lem << std::endl; - //lems.push_back( lem ); + vals.push_back( res ); + exps.push_back( expn ); + Trace("sygus-eager") << "Conclude : " << d_evals[n][i] << " == " << res << ", cref eval = " << d_eval_args_const[n][i] << std::endl; + Trace("sygus-eager") << " from " << expn << std::endl; } d_node_mv_args_proc[n][vn] = it->second.size(); } @@ -3312,9 +4345,139 @@ void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& terms } } +void TermDbSygus::getExplanationForConstantEquality( Node n, Node vn, std::vector< Node >& exp ) { + std::map< unsigned, bool > cexc; + getExplanationForConstantEquality( n, vn, exp, cexc ); +} + +void TermDbSygus::getExplanationForConstantEquality( Node n, Node vn, std::vector< Node >& exp, std::map< unsigned, bool >& cexc ) { + Assert( vn.getKind()==kind::APPLY_CONSTRUCTOR ); + Assert( n.getType()==vn.getType() ); + TypeNode tn = n.getType(); + Assert( tn.isDatatype() ); + const Datatype& dt = ((DatatypeType)tn.toType()).getDatatype(); + int i = Datatype::indexOf( vn.getOperator().toExpr() ); + Node tst = datatypes::DatatypesRewriter::mkTester( n, i, dt ); + exp.push_back( tst ); + for( unsigned j=0; j<vn.getNumChildren(); j++ ){ + if( cexc.find( j )==cexc.end() ){ + Node sel = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[i].getSelectorInternal( tn.toType(), j ) ), n ); + getExplanationForConstantEquality( sel, vn[j], exp ); + } + } +} + +Node TermDbSygus::getExplanationForConstantEquality( Node n, Node vn ) { + std::map< unsigned, bool > cexc; + return getExplanationForConstantEquality( n, vn, cexc ); +} + +Node TermDbSygus::getExplanationForConstantEquality( Node n, Node vn, std::map< unsigned, bool >& cexc ) { + std::vector< Node > exp; + getExplanationForConstantEquality( n, vn, exp, cexc ); + Assert( !exp.empty() ); + return exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp ); +} + +// we have ( n = vn => eval( n ) = bvr ) ^ vn != vnr , returns exp such that exp => ( eval( n ) = bvr ^ vn != vnr ) +void TermDbSygus::getExplanationFor( TermRecBuild& trb, Node n, Node vn, std::vector< Node >& exp, std::map< TypeNode, int >& var_count, + SygusInvarianceTest& et, Node vnr, Node& vnr_exp, int& sz ) { + Assert( vnr.isNull() || vn!=vnr ); + Assert( vn.getKind()==APPLY_CONSTRUCTOR ); + Assert( vnr.isNull() || vnr.getKind()==APPLY_CONSTRUCTOR ); + Assert( n.getType()==vn.getType() ); + TypeNode ntn = n.getType(); + std::map< unsigned, bool > cexc; + // for each child, check whether replacing by a fresh variable and rewriting again + for( unsigned i=0; i<vn.getNumChildren(); i++ ){ + TypeNode xtn = vn[i].getType(); + Node x = getFreeVarInc( xtn, var_count ); + trb.replaceChild( i, x ); + Node nvn = trb.build(); + Assert( nvn.getKind()==kind::APPLY_CONSTRUCTOR ); + if( et.is_invariant( this, nvn, x ) ){ + cexc[i] = true; + // we are tracking term size if positive + if( sz>=0 ){ + int s = getSygusTermSize( vn[i] ); + sz = sz - s; + } + }else{ + trb.replaceChild( i, vn[i] ); + } + } + const Datatype& dt = ((DatatypeType)ntn.toType()).getDatatype(); + int cindex = Datatype::indexOf( vn.getOperator().toExpr() ); + Assert( cindex>=0 && cindex<(int)dt.getNumConstructors() ); + Node tst = datatypes::DatatypesRewriter::mkTester( n, cindex, dt ); + exp.push_back( tst ); + // if the operator of vn is different than vnr, then disunification obligation is met + if( !vnr.isNull() ){ + if( vnr.getOperator()!=vn.getOperator() ){ + vnr = Node::null(); + vnr_exp = d_true; + } + } + for( unsigned i=0; i<vn.getNumChildren(); i++ ){ + Node sel = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[cindex].getSelectorInternal( ntn.toType(), i ) ), n ); + Node vnr_c = vnr.isNull() ? vnr : ( vn[i]==vnr[i] ? Node::null() : vnr[i] ); + if( cexc.find( i )==cexc.end() ){ + trb.push( i ); + Node vnr_exp_c; + getExplanationFor( trb, sel, vn[i], exp, var_count, et, vnr_c, vnr_exp_c, sz ); + trb.pop(); + if( !vnr_c.isNull() ){ + Assert( !vnr_exp_c.isNull() ); + if( vnr_exp_c.isConst() || vnr_exp.isNull() ){ + // recursively satisfied the disunification obligation + if( vnr_exp_c.isConst() ){ + // was successful, don't consider further + vnr = Node::null(); + } + vnr_exp = vnr_exp_c; + } + } + }else{ + // if excluded, we may need to add the explanation for this + if( vnr_exp.isNull() && !vnr_c.isNull() ){ + vnr_exp = getExplanationForConstantEquality( sel, vnr[i] ); + } + } + } +} + +void TermDbSygus::getExplanationFor( Node n, Node vn, std::vector< Node >& exp, SygusInvarianceTest& et, Node vnr, unsigned& sz ) { + // naive : + //return getExplanationForConstantEquality( n, vn, exp ); + + // set up the recursion object + std::map< TypeNode, int > var_count; + TermRecBuild trb; + trb.init( vn ); + Node vnr_exp; + int sz_use = sz; + getExplanationFor( trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz_use ); + Assert( sz_use>=0 ); + sz = sz_use; + Assert( vnr.isNull() || !vnr_exp.isNull() ); + if( !vnr_exp.isNull() && !vnr_exp.isConst() ){ + exp.push_back( vnr_exp.negate() ); + } +} + +void TermDbSygus::getExplanationFor( Node n, Node vn, std::vector< Node >& exp, SygusInvarianceTest& et ) { + int sz = -1; + std::map< TypeNode, int > var_count; + TermRecBuild trb; + trb.init( vn ); + Node vnr; + Node vnr_exp; + getExplanationFor( trb, n, vn, exp, var_count, et, vnr, vnr_exp, sz ); +} Node TermDbSygus::unfold( Node en, std::map< Node, Node >& vtm, std::vector< Node >& exp, bool track_exp ) { if( en.getKind()==kind::APPLY_UF ){ + Trace("sygus-db-debug") << "Unfold : " << en << std::endl; Node ev = en[0]; if( track_exp ){ std::map< Node, Node >::iterator itv = vtm.find( en[0] ); @@ -3340,13 +4503,20 @@ Node TermDbSygus::unfold( Node en, std::map< Node, Node >& vtm, std::vector< Nod exp.push_back( ee ); } } + Assert( !dt.isParametric() ); std::map< int, Node > pre; for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){ std::vector< Node > cc; //get the evaluation argument for the selector + Type rt = dt[i][j].getRangeType(); const Datatype & ad = ((DatatypeType)dt[i][j].getRangeType()).getDatatype(); cc.push_back( Node::fromExpr( ad.getSygusEvaluationFunc() ) ); - Node s = en[0].getKind()==kind::APPLY_CONSTRUCTOR ? en[0][j] : NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, Node::fromExpr( dt[i][j].getSelector() ), en[0] ); + Node s; + if( en[0].getKind()==kind::APPLY_CONSTRUCTOR ){ + s = en[0][j]; + }else{ + s = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, dt[i].getSelectorInternal( en[0].getType().toType(), j ), en[0] ); + } cc.push_back( s ); if( track_exp ){ //update vtm map @@ -3359,16 +4529,10 @@ Node TermDbSygus::unfold( Node en, std::map< Node, Node >& vtm, std::vector< Nod Node ret = mkGeneric( dt, i, var_count, pre ); // if it is a variable, apply the substitution if( ret.getKind()==kind::BOUND_VARIABLE ){ - //replace by argument - Node var_list = Node::fromExpr( dt.getSygusVarList() ); - //TODO : set argument # on sygus variables - for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ - if( var_list[j]==ret ){ - ret = args[j]; - break; - } - } - Assert( ret.isConst() ); + Assert( ret.hasAttribute(SygusVarNumAttribute()) ); + int i = ret.getAttribute(SygusVarNumAttribute()); + Assert( Node::fromExpr( dt.getSygusVarList() )[i]==ret ); + ret = args[i]; }else if( ret.getKind()==APPLY ){ //must expand definitions to account for defined functions in sygus grammars ret = Node::fromExpr( smt::currentSmtEngine()->expandDefinitions( ret.toExpr() ) ); @@ -3380,6 +4544,714 @@ Node TermDbSygus::unfold( Node en, std::map< Node, Node >& vtm, std::vector< Nod return en; } + +Node TermDbSygus::getEagerUnfold( Node n, std::map< Node, Node >& visited ) { + std::map< Node, Node >::iterator itv = visited.find( n ); + if( itv==visited.end() ){ + Trace("cegqi-eager-debug") << "getEagerUnfold " << n << std::endl; + Node ret; + if( n.getKind()==APPLY_UF ){ + TypeNode tn = n[0].getType(); + Trace("cegqi-eager-debug") << "check " << n[0].getType() << std::endl; + if( tn.isDatatype() ){ + const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype(); + if( dt.isSygus() ){ + Trace("cegqi-eager") << "Unfold eager : " << n << std::endl; + Node bTerm = sygusToBuiltin( n[0], tn ); + Trace("cegqi-eager") << "Built-in term : " << bTerm << std::endl; + std::vector< Node > vars; + std::vector< Node > subs; + Node var_list = Node::fromExpr( dt.getSygusVarList() ); + Assert( var_list.getNumChildren()+1==n.getNumChildren() ); + for( unsigned j=0; j<var_list.getNumChildren(); j++ ){ + vars.push_back( var_list[j] ); + } + for( unsigned j=1; j<n.getNumChildren(); j++ ){ + Node nc = getEagerUnfold( n[j], visited ); + subs.push_back( nc ); + Assert( subs[j-1].getType()==var_list[j-1].getType() ); + } + Assert( vars.size()==subs.size() ); + bTerm = bTerm.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() ); + Trace("cegqi-eager") << "Built-in term after subs : " << bTerm << std::endl; + Trace("cegqi-eager-debug") << "Types : " << bTerm.getType() << " " << n.getType() << std::endl; + Assert( n.getType()==bTerm.getType() ); + ret = bTerm; + } + } + } + if( ret.isNull() ){ + if( n.getKind()!=FORALL ){ + bool childChanged = false; + std::vector< Node > children; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node nc = getEagerUnfold( n[i], visited ); + childChanged = childChanged || n[i]!=nc; + children.push_back( nc ); + } + if( childChanged ){ + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.insert( children.begin(), n.getOperator() ); + } + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + } + if( ret.isNull() ){ + ret = n; + } + } + visited[n] = ret; + return ret; + }else{ + return itv->second; + } +} + + +Node TermDbSygus::evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args ) { + if( !args.empty() ){ + std::map< TypeNode, std::vector< Node > >::iterator it = d_var_list.find( tn ); + Assert( it!=d_var_list.end() ); + Assert( it->second.size()==args.size() ); + return Rewriter::rewrite( bn.substitute( it->second.begin(), it->second.end(), args.begin(), args.end() ) ); + }else{ + return Rewriter::rewrite( bn ); + } +} + +Node TermDbSygus::evaluateBuiltin( TypeNode tn, Node bn, Node ar, unsigned i ) { + std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_pbe_exs.find( ar ); + if( it!=d_pbe_exs.end() ){ + Assert( i<it->second.size() ); + return evaluateBuiltin( tn, bn, it->second[i] ); + }else{ + return Rewriter::rewrite( bn ); + } +} + +Node TermDbSygus::evaluateWithUnfolding( Node n, std::map< Node, Node >& visited ) { + std::map< Node, Node >::iterator it = visited.find( n ); + if( it==visited.end() ){ + Node ret = n; + while( ret.getKind()==APPLY_UF && ret[0].getKind()==APPLY_CONSTRUCTOR ){ + ret = unfold( ret ); + } + if( ret.getNumChildren()>0 ){ + std::vector< Node > children; + if( ret.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( ret.getOperator() ); + } + bool childChanged = false; + for( unsigned i=0; i<ret.getNumChildren(); i++ ){ + Node nc = evaluateWithUnfolding( ret[i], visited ); + childChanged = childChanged || nc!=ret[i]; + children.push_back( nc ); + } + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( ret.getKind(), children ); + } + // TODO : extended rewrite? + ret = extendedRewrite( ret ); + } + visited[n] = ret; + return ret; + }else{ + return it->second; + } +} + +Node TermDbSygus::evaluateWithUnfolding( Node n ) { + std::map< Node, Node > visited; + return evaluateWithUnfolding( n, visited ); +} + +bool TermDbSygus::computeGenericRedundant( TypeNode tn, Node g ) { + //everything added to this cache should be mutually exclusive cases + std::map< Node, bool >::iterator it = d_gen_redundant[tn].find( g ); + if( it==d_gen_redundant[tn].end() ){ + Trace("sygus-gnf") << "Register generic for " << tn << " : " << g << std::endl; + Node gr = getNormalized( tn, g, false ); + Trace("sygus-gnf-debug") << "Generic " << g << " rewrites to " << gr << std::endl; + std::map< Node, Node >::iterator itg = d_gen_terms[tn].find( gr ); + bool red = true; + if( itg==d_gen_terms[tn].end() ){ + red = false; + d_gen_terms[tn][gr] = g; + Trace("sygus-gnf-debug") << "...not redundant." << std::endl; + Trace("sygus-nf-reg") << "*** Sygus (generic) normal form : normal form of " << g << " is " << gr << std::endl; + }else{ + Trace("sygus-gnf-debug") << "...redundant." << std::endl; + Trace("sygus-nf") << "* Sygus normal form : simplify since " << g << " and " << itg->second << " both rewrite to " << gr << std::endl; + } + d_gen_redundant[tn][g] = red; + return red; + }else{ + return it->second; + } +} + +bool TermDbSygus::isGenericRedundant( TypeNode tn, unsigned i ) { + Assert( i<d_sygus_red_status[tn].size() ); + if( options::sygusMinGrammarAgg() ){ + return d_sygus_red_status[tn][i]!=0; + }else{ + return d_sygus_red_status[tn][i]==1; + } +} + +Node TermDbSygus::PbeTrie::addPbeExample( TypeNode etn, Node e, Node b, quantifiers::TermDbSygus * tds, unsigned index, unsigned ntotal ) { + Assert( tds->getNumPbeExamples( e )==ntotal ); + if( index==ntotal ){ + //lazy child holds the leaf data + if( d_lazy_child.isNull() ){ + d_lazy_child = b; + } + return d_lazy_child; + }else{ + std::vector< Node > ex; + if( d_children.empty() ){ + if( d_lazy_child.isNull() ){ + d_lazy_child = b; + return d_lazy_child; + }else{ + //evaluate the lazy child + tds->getPbeExample( e, index, ex ); + addPbeExampleEval( etn, e, d_lazy_child, ex, tds, index, ntotal ); + Assert( !d_children.empty() ); + d_lazy_child = Node::null(); + } + }else{ + tds->getPbeExample( e, index, ex ); + } + return addPbeExampleEval( etn, e, b, ex, tds, index, ntotal ); + } +} + +Node TermDbSygus::PbeTrie::addPbeExampleEval( TypeNode etn, Node e, Node b, std::vector< Node >& ex, quantifiers::TermDbSygus * tds, unsigned index, unsigned ntotal ) { + Node eb = tds->evaluateBuiltin( etn, b, ex ); + return d_children[eb].addPbeExample( etn, e, b, tds, index+1, ntotal ); +} + +Node TermDbSygus::addPbeSearchVal( TypeNode tn, Node e, Node bvr ){ + Assert( !e.isNull() ); + if( hasPbeExamples( e ) ){ + unsigned nex = getNumPbeExamples( e ); + Node ret = d_pbe_trie[e][tn].addPbeExample( tn, e, bvr, this, 0, nex ); + Assert( ret.getType()==bvr.getType() ); + return ret; + } + return Node::null(); +} + +Node TermDbSygus::extendedRewritePullIte( Node n ) { + // generalize this? + Assert( n.getNumChildren()==2 ); + Assert( n.getType().isBoolean() ); + Assert( n.getMetaKind() != kind::metakind::PARAMETERIZED ); + std::vector< Node > children; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + children.push_back( n[i] ); + } + for( unsigned i=0; i<2; i++ ){ + if( n[i].getKind()==kind::ITE ){ + for( unsigned j=0; j<2; j++ ){ + children[i] = n[i][j+1]; + Node eqr = extendedRewrite( NodeManager::currentNM()->mkNode( n.getKind(), children ) ); + children[i] = n[i]; + if( eqr.isConst() ){ + std::vector< Node > new_children; + Kind new_k; + if( eqr==d_true ){ + new_k = kind::OR; + new_children.push_back( j==0 ? n[i][0] : n[i][0].negate() ); + }else{ + Assert( eqr==d_false ); + new_k = kind::AND; + new_children.push_back( j==0 ? n[i][0].negate() : n[i][0] ); + } + children[i] = n[i][2-j]; + Node rem_eq = NodeManager::currentNM()->mkNode( n.getKind(), children ); + children[i] = n[i]; + new_children.push_back( rem_eq ); + Node nc = NodeManager::currentNM()->mkNode( new_k, new_children ); + Trace("sygus-ext-rewrite") << "sygus-extr : " << n << " rewrites to " << nc << " by simple ITE pulling." << std::endl; + //recurse + return extendedRewrite( nc ); + } + } + } + } + return Node::null(); +} + +Node TermDbSygus::extendedRewrite( Node n ) { + std::map< Node, Node >::iterator it = d_ext_rewrite_cache.find( n ); + if( it == d_ext_rewrite_cache.end() ){ + Node ret = n; + if( n.getNumChildren()>0 ){ + std::vector< Node > children; + if( n.getMetaKind() == kind::metakind::PARAMETERIZED ){ + children.push_back( n.getOperator() ); + } + bool childChanged = false; + for( unsigned i=0; i<n.getNumChildren(); i++ ){ + Node nc = extendedRewrite( n[i] ); + childChanged = nc!=n[i] || childChanged; + children.push_back( nc ); + } + Node ret; + if( childChanged ){ + ret = NodeManager::currentNM()->mkNode( n.getKind(), children ); + } + } + ret = Rewriter::rewrite( n ); + Trace("sygus-ext-rewrite-debug") << "Do extended rewrite on : " << ret << " (from " << n << ")" << std::endl; + + Node new_ret; + if( ret.getKind()==kind::EQUAL ){ + // string equalities with disequal prefix or suffix + if( ret[0].getType().isString() ){ + std::vector< Node > c[2]; + for( unsigned i=0; i<2; i++ ){ + strings::TheoryStringsRewriter::getConcat( ret[i], c[i] ); + } + if( c[0].empty()==c[1].empty() ){ + if( !c[0].empty() ){ + for( unsigned i=0; i<2; i++ ){ + unsigned index1 = i==0 ? 0 : c[0].size()-1; + unsigned index2 = i==0 ? 0 : c[1].size()-1; + if( c[0][index1].isConst() && c[1][index2].isConst() ){ + CVC4::String s = c[0][index1].getConst<String>(); + CVC4::String t = c[1][index2].getConst<String>(); + unsigned len_short = s.size() <= t.size() ? s.size() : t.size(); + bool isSameFix = i==1 ? s.rstrncmp(t, len_short): s.strncmp(t, len_short); + if( !isSameFix ){ + Trace("sygus-ext-rewrite") << "sygus-extr : " << ret << " rewrites to false due to disequal string prefix/suffix." << std::endl; + new_ret = d_false; + break; + } + } + } + } + }else{ + new_ret = d_false; + } + } + if( new_ret.isNull() ){ + // simple ITE pulling + new_ret = extendedRewritePullIte( ret ); + } + // TODO : ( ~contains( x, y ) --> false ) => ( ~x=y --> false ) + }else if( ret.getKind()==kind::ITE ){ + Assert( ret[1]!=ret[2] ); + if( ret[0].getKind()==NOT ){ + ret = NodeManager::currentNM()->mkNode( kind::ITE, ret[0][0], ret[2], ret[1] ); + } + if( ret[0].getKind()==kind::EQUAL ){ + // simple invariant ITE + for( unsigned i=0; i<2; i++ ){ + if( ret[1]==ret[0][i] && ret[2]==ret[0][1-i] ){ + Trace("sygus-ext-rewrite") << "sygus-extr : " << ret << " rewrites to " << ret[2] << " due to simple invariant ITE." << std::endl; + new_ret = ret[2]; + break; + } + } + // notice this is strictly more general that the above + if( new_ret.isNull() ){ + // simple substitution + for( unsigned i=0; i<2; i++ ){ + if( ret[0][i].isVar() && ( ( ret[0][1-i].isVar() && ret[0][i]<ret[0][1-i] ) || ret[0][1-i].isConst() ) ){ + TNode r1 = ret[0][i]; + TNode r2 = ret[0][1-i]; + Node retn = ret[1].substitute( r1, r2 ); + if( retn!=ret[1] ){ + new_ret = NodeManager::currentNM()->mkNode( kind::ITE, ret[0], retn, ret[2] ); + Trace("sygus-ext-rewrite") << "sygus-extr : " << ret << " rewrites to " << new_ret << " due to simple ITE substitution." << std::endl; + } + } + } + } + } + }else if( ret.getKind()==DIVISION || ret.getKind()==INTS_DIVISION || ret.getKind()==INTS_MODULUS ){ + // rewrite as though total + std::vector< Node > children; + bool all_const = true; + for( unsigned i=0; i<ret.getNumChildren(); i++ ){ + if( ret[i].isConst() ){ + children.push_back( ret[i] ); + }else{ + all_const = false; + break; + } + } + if( all_const ){ + Kind new_k = ( ret.getKind()==DIVISION ? DIVISION_TOTAL : ( ret.getKind()==INTS_DIVISION ? INTS_DIVISION_TOTAL : INTS_MODULUS_TOTAL ) ); + new_ret = NodeManager::currentNM()->mkNode( new_k, children ); + Trace("sygus-ext-rewrite") << "sygus-extr : " << ret << " rewrites to " << new_ret << " due to total interpretation." << std::endl; + } + } + // more expensive rewrites + if( new_ret.isNull() ){ + Trace("sygus-ext-rewrite-debug2") << "Do expensive rewrites on " << ret << std::endl; + bool polarity = ret.getKind()!=NOT; + Node ret_atom = ret.getKind()==NOT ? ret[0] : ret; + if( ( ret_atom.getKind()==EQUAL && ret_atom[0].getType().isReal() ) || ret_atom.getKind()==GEQ ){ + Trace("sygus-ext-rewrite-debug2") << "Compute monomial sum " << ret_atom << std::endl; + //compute monomial sum + std::map< Node, Node > msum; + if( QuantArith::getMonomialSumLit( ret_atom, msum ) ){ + for( std::map< Node, Node >::iterator itm = msum.begin(); itm != msum.end(); ++itm ){ + Node v = itm->first; + Trace("sygus-ext-rewrite-debug2") << itm->first << " * " << itm->second << std::endl; + if( v.getKind()==ITE ){ + Node veq; + int res = QuantArith::isolate( v, msum, veq, ret_atom.getKind() ); + if( res!=0 ){ + Trace("sygus-ext-rewrite-debug") << " have ITE relation, solved form : " << veq << std::endl; + // try pulling ITE + new_ret = extendedRewritePullIte( veq ); + if( !new_ret.isNull() ){ + if( !polarity ){ + new_ret = new_ret.negate(); + } + break; + } + }else{ + Trace("sygus-ext-rewrite-debug") << " failed to isolate " << v << " in " << ret << std::endl; + } + } + } + }else{ + Trace("sygus-ext-rewrite-debug") << " failed to get monomial sum of " << ret << std::endl; + } + }else if( ret_atom.getKind()==ITE ){ + // TODO : conditional rewriting + }else if( ret.getKind()==kind::AND || ret.getKind()==kind::OR ){ + // TODO condition merging + } + } + + if( !new_ret.isNull() ){ + ret = Rewriter::rewrite( new_ret ); + } + d_ext_rewrite_cache[n] = ret; + return ret; + }else{ + return it->second; + } +} + + + + + + +TypeNode TermDbSygus::mkUnresolvedType(const std::string& name, std::set<Type>& unres) { + TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER); + unres.insert( unresolved.toType() ); + return unresolved; +} + +void TermDbSygus::mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& 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 TermDbSygus::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<dt.getNumConstructors(); i++ ){ + for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){ + TypeNode crange = TypeNode::fromType( ((SelectorType)dt[i][j].getType()).getRangeType() ); + sels[crange].push_back( dt[i][j] ); + collectSygusGrammarTypesFor( crange, types, sels ); + } + } + } + } + } +} + +void TermDbSygus::mkSygusDefaultGrammar( TypeNode range, Node bvl, const std::string& fun, std::map< TypeNode, std::vector< Node > >& extra_cons, + std::vector< CVC4::Datatype >& datatypes, std::set<Type>& unres ) { + // collect the variables + std::vector<Node> sygus_vars; + if( !bvl.isNull() ){ + for( unsigned i=0; i<bvl.getNumChildren(); i++ ){ + sygus_vars.push_back( bvl[i] ); + } + } + //if( !range.isBoolean() && !range.isInteger() && !range.isBitVector() && !range.isDatatype() ){ + // parseError("No default grammar for type."); + //} + std::vector< std::vector< Expr > > 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<sygus_vars.size(); i++ ){ + collectSygusGrammarTypesFor( sygus_vars[i].getType(), types, sels ); + } + //types connected to range + collectSygusGrammarTypesFor( range, types, sels ); + + //name of boolean sort + std::stringstream ssb; + ssb << fun << "_Bool"; + std::string dbname = ssb.str(); + Type unres_bt = mkUnresolvedType(ssb.str(), unres).toType(); + + std::vector< Type > unres_types; + std::map< TypeNode, Type > type_to_unres; + for( unsigned i=0; i<types.size(); i++ ){ + std::stringstream ss; + ss << fun << "_" << types[i]; + std::string dname = ss.str(); + datatypes.push_back(Datatype(dname)); + ops.push_back(std::vector< Expr >()); + //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<types.size(); i++ ){ + Trace("sygus-grammar-def") << "Make grammar for " << types[i] << " " << unres_types[i] << std::endl; + std::vector<std::string> cnames; + std::vector<std::vector<CVC4::Type> > cargs; + Type unres_t = unres_types[i]; + //add variables + for( unsigned j=0; j<sygus_vars.size(); j++ ){ + if( sygus_vars[j].getType()==types[i] ){ + std::stringstream ss; + ss << sygus_vars[j]; + Trace("sygus-grammar-def") << "...add for variable " << ss.str() << std::endl; + ops[i].push_back( sygus_vars[j].toExpr() ); + cnames.push_back( ss.str() ); + cargs.push_back( std::vector< CVC4::Type >() ); + } + } + //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; j<itec->second.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<consts.size(); j++ ){ + std::stringstream ss; + ss << consts[j]; + Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl; + ops[i].push_back( consts[j].toExpr() ); + cnames.push_back( ss.str() ); + cargs.push_back( std::vector< CVC4::Type >() ); + } + //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<dt.getNumConstructors(); k++ ){ + Trace("sygus-grammar-def") << "...for " << dt[k].getName() << std::endl; + ops[i].push_back( dt[k].getConstructor() ); + cnames.push_back( dt[k].getName() ); + cargs.push_back( std::vector< CVC4::Type >() ); + for( unsigned j=0; j<dt[k].getNumArgs(); j++ ){ + TypeNode crange = TypeNode::fromType( ((SelectorType)dt[k][j].getType()).getRangeType() ); + //Assert( type_to_unres.find(crange)!=type_to_unres.end() ); + cargs.back().push_back( type_to_unres[crange] ); + } + } + }else{ + std::stringstream sserr; + sserr << "No implementation for default Sygus grammar of type " << types[i] << std::endl; + //AlwaysAssert( false, sserr.str() ); + // FIXME + AlwaysAssert( false ); + } + //add for all selectors to this type + if( !sels[types[i]].empty() ){ + Trace("sygus-grammar-def") << "...add for selectors" << std::endl; + for( unsigned j=0; j<sels[types[i]].size(); j++ ){ + Trace("sygus-grammar-def") << "...for " << sels[types[i]][j].getName() << std::endl; + TypeNode arg_type = TypeNode::fromType( ((SelectorType)sels[types[i]][j].getType()).getDomain() ); + ops[i].push_back( sels[types[i]][j].getSelector() ); + cnames.push_back( sels[types[i]][j].getName() ); + cargs.push_back( std::vector< CVC4::Type >() ); + //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; j<ops[i].size(); j++ ){ + datatypes[i].addSygusConstructor( ops[i][j], cnames[j], cargs[j] ); + } + //sorts.push_back( types[i] ); + //set start index if applicable + if( types[i]==range ){ + startIndex = i; + } + } + + //make Boolean type + TypeNode btype = NodeManager::currentNM()->booleanType(); + datatypes.push_back(Datatype(dbname)); + ops.push_back(std::vector<Expr>()); + std::vector<std::string> cnames; + std::vector<std::vector< Type > > cargs; + Trace("sygus-grammar-def") << "Make grammar for " << btype << " " << datatypes.back() << std::endl; + //add variables + for( unsigned i=0; i<sygus_vars.size(); i++ ){ + if( sygus_vars[i].getType().isBoolean() ){ + std::stringstream ss; + ss << sygus_vars[i]; + Trace("sygus-grammar-def") << "...add for variable " << ss.str() << std::endl; + ops.back().push_back( sygus_vars[i].toExpr() ); + cnames.push_back( ss.str() ); + cargs.push_back( std::vector< CVC4::Type >() ); + } + } + //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<consts.size(); j++ ){ + std::stringstream ss; + ss << consts[j]; + Trace("sygus-grammar-def") << "...add for constant " << ss.str() << std::endl; + ops.back().push_back( consts[j].toExpr() ); + cnames.push_back( ss.str() ); + cargs.push_back( std::vector< CVC4::Type >() ); + } + } + //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; i<types.size(); i++ ){ + Trace("sygus-grammar-def") << "...add predicates for " << types[i] << std::endl; + //add equality per type + CVC4::Kind k = kind::EQUAL; + Trace("sygus-grammar-def") << "...add for " << k << std::endl; + ops.back().push_back(NodeManager::currentNM()->operatorOf(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<dt.getNumConstructors(); k++ ){ + Trace("sygus-grammar-def") << "...for " << dt[k].getTesterName() << std::endl; + ops.back().push_back(dt[k].getTester()); + cnames.push_back(dt[k].getTesterName()); + cargs.push_back( std::vector< CVC4::Type >() ); + 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; j<ops.back().size(); j++ ){ + datatypes.back().addSygusConstructor( ops.back()[j], cnames[j], cargs[j] ); + } + //sorts.push_back( btype ); + Trace("sygus-grammar-def") << "...finished make default grammar for " << fun << " " << range << std::endl; + + if( startIndex>0 ){ + CVC4::Datatype tmp_dt = datatypes[0]; + datatypes[0] = datatypes[startIndex]; + datatypes[startIndex] = tmp_dt; + } +} + + +TypeNode TermDbSygus::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<Type> 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<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres); + Assert( types.size()==datatypes.size() ); + return TypeNode::fromType( types[0] ); +} + }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ + diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h index 912961e19..b1d4f7f2b 100644 --- a/src/theory/quantifiers/term_database.h +++ b/src/theory/quantifiers/term_database.h @@ -93,6 +93,14 @@ typedef expr::Attribute< LtePartialInstAttributeId, bool > LtePartialInstAttribu struct SygusProxyAttributeId {}; typedef expr::Attribute<SygusProxyAttributeId, Node> SygusProxyAttribute; +// attribute for associating a synthesis function with a first order variable +struct SygusSynthFunAttributeId {}; +typedef expr::Attribute<SygusSynthFunAttributeId, Node> SygusSynthFunAttribute; + +// attribute for associating a variable list with a synth fun +struct SygusSynthFunVarListAttributeId {}; +typedef expr::Attribute<SygusSynthFunVarListAttributeId, Node> SygusSynthFunVarListAttribute; + //attribute for fun-def abstraction type struct AbsTypeFunDefAttributeId {}; typedef expr::Attribute<AbsTypeFunDefAttributeId, bool> AbsTypeFunDefAttribute; @@ -109,6 +117,11 @@ typedef expr::Attribute< QuantElimPartialAttributeId, bool > QuantElimPartialAtt struct QuantIdNumAttributeId {}; typedef expr::Attribute< QuantIdNumAttributeId, uint64_t > QuantIdNumAttribute; +/** sygus var num */ +struct SygusVarNumAttributeId {}; +typedef expr::Attribute<SygusVarNumAttributeId, uint64_t> SygusVarNumAttribute; + + class QuantifiersEngine; @@ -495,6 +508,8 @@ public: static bool isAssoc( Kind k ); /** is comm */ static bool isComm( Kind k ); + /** ( x k ... ) k x = ( x k ... ) */ + static bool isNonAdditive( Kind k ); /** is bool connective */ static bool isBoolConnective( Kind k ); /** is bool connective term */ @@ -559,20 +574,50 @@ public: static void computeQuantAttributes( Node q, QAttributes& qa ); };/* class TermDb */ +class SygusInvarianceTest { +protected: + // check whether nvn[ x ] should be excluded + virtual bool invariant( TermDbSygus * tds, Node nvn, Node x ) = 0; +public: + bool is_invariant( TermDbSygus * tds, Node nvn, Node x ){ + if( invariant( tds, nvn, x ) ){ + d_update_nvn = nvn; + return true; + }else{ + return false; + } + } + // result of the node after invariant replacements + Node d_update_nvn; +}; + +class EvalSygusInvarianceTest : public SygusInvarianceTest { +public: + Node d_conj; + TNode d_var; + std::map< Node, Node > d_visited; + Node d_result; +protected: + bool invariant( quantifiers::TermDbSygus * tds, Node nvn, Node x ); +}; + class TermDbSygus { private: /** reference to the quantifiers engine */ QuantifiersEngine* d_quantEngine; - std::map< TypeNode, std::vector< Node > > d_fv; + std::map< TypeNode, std::vector< Node > > d_fv[2]; std::map< Node, TypeNode > d_fv_stype; std::map< Node, int > d_fv_num; + bool hasFreeVar( Node n, std::map< Node, bool >& visited ); +public: Node d_true; Node d_false; public: - TNode getVar( TypeNode tn, int i ); - TNode getVarInc( TypeNode tn, std::map< TypeNode, int >& var_count ); - bool isVar( Node n ) { return d_fv_stype.find( n )!=d_fv_stype.end(); } + TNode getFreeVar( TypeNode tn, int i, bool useSygusType = false ); + TNode getFreeVarInc( TypeNode tn, std::map< TypeNode, int >& var_count, bool useSygusType = false ); + bool isFreeVar( Node n ) { return d_fv_stype.find( n )!=d_fv_stype.end(); } int getVarNum( Node n ) { return d_fv_num[n]; } + bool hasFreeVar( Node n ); private: std::map< TypeNode, std::map< int, Node > > d_generic_base; std::map< TypeNode, std::vector< Node > > d_generic_templ; @@ -581,8 +626,15 @@ private: public: bool getMatch( Node n, TypeNode st, int& index_found, std::vector< Node >& args, int index_exc = -1, int index_start = 0 ); private: + void computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth ); + bool involvesDivByZero( Node n, std::map< Node, bool >& visited ); +private: + // stores root + std::map< Node, Node > d_measured_term; + std::map< Node, Node > d_measured_term_active_guard; //information for sygus types std::map< TypeNode, TypeNode > d_register; //stores sygus -> builtin type + std::map< TypeNode, std::vector< Node > > d_var_list; std::map< TypeNode, std::map< int, Kind > > d_arg_kind; std::map< TypeNode, std::map< Kind, int > > d_kinds; std::map< TypeNode, std::map< int, Node > > d_arg_const; @@ -592,6 +644,7 @@ private: std::map< TypeNode, std::vector< int > > d_id_funcs; std::map< TypeNode, std::vector< Node > > d_const_list; //sorted list of constants for type std::map< TypeNode, unsigned > d_const_list_pos; + std::map< TypeNode, std::map< Node, Node > > d_semantic_skolem; //information for builtin types std::map< TypeNode, std::map< int, Node > > d_type_value; std::map< TypeNode, Node > d_type_max_value; @@ -601,36 +654,63 @@ private: std::map< TypeNode, std::map< Node, Node > > d_normalized; std::map< TypeNode, std::map< Node, Node > > d_sygus_to_builtin; std::map< TypeNode, std::map< Node, Node > > d_builtin_const_to_sygus; + // grammar information + // root -> type -> _ + std::map< TypeNode, std::map< TypeNode, unsigned > > d_min_type_depth; + //std::map< TypeNode, std::map< Node, std::map< std::map< int, bool > > > d_consider_const; + // type -> cons -> _ + std::map< TypeNode, unsigned > d_min_term_size; + std::map< TypeNode, std::map< unsigned, unsigned > > d_min_cons_term_size; public: TermDbSygus( context::Context* c, QuantifiersEngine* qe ); ~TermDbSygus(){} bool reset( Theory::Effort e ); std::string identify() const { return "TermDbSygus"; } - +public: + /** register the sygus type */ + void registerSygusType( TypeNode tn ); + /** register a term that we will do enumerative search on */ + void registerMeasuredTerm( Node e, Node root, bool mkActiveGuard = false ); + /** is measured term */ + Node isMeasuredTerm( Node e ); + /** get active guard */ + Node getActiveGuardForMeasureTerm( Node e ); + /** get measured terms */ + void getMeasuredTerms( std::vector< Node >& mts ); +public: //general sygus utilities bool isRegistered( TypeNode tn ); + // get the minimum depth of type in its parent grammar + unsigned getMinTypeDepth( TypeNode root_tn, TypeNode tn ); + // get the minimum size for a constructor term + unsigned getMinTermSize( TypeNode tn ); + unsigned getMinConsTermSize( TypeNode tn, unsigned cindex ); +public: TypeNode sygusToBuiltinType( TypeNode tn ); - int getKindArg( TypeNode tn, Kind k ); - int getConstArg( TypeNode tn, Node n ); - int getOpArg( TypeNode tn, Node n ); + int getKindConsNum( TypeNode tn, Kind k ); + int getConstConsNum( TypeNode tn, Node n ); + int getOpConsNum( TypeNode tn, Node n ); bool hasKind( TypeNode tn, Kind k ); bool hasConst( TypeNode tn, Node n ); bool hasOp( TypeNode tn, Node n ); - Node getArgConst( TypeNode tn, int i ); - Node getArgOp( TypeNode tn, int i ); - Kind getArgKind( TypeNode tn, int i ); + Node getConsNumConst( TypeNode tn, int i ); + Node getConsNumOp( TypeNode tn, int i ); + Kind getConsNumKind( TypeNode tn, int i ); bool isKindArg( TypeNode tn, int i ); bool isConstArg( TypeNode tn, int i ); unsigned getNumIdFuncs( TypeNode tn ); unsigned getIdFuncIndex( TypeNode tn, unsigned i ); - void registerSygusType( TypeNode tn ); /** get arg type */ TypeNode getArgType( const DatatypeConstructor& c, int i ); + /** get first occurrence */ + int getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn ); + /** is type match */ + bool isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 ); /** isAntisymmetric */ bool isAntisymmetric( Kind k, Kind& dk ); /** is idempotent arg */ bool isIdempotentArg( Node n, Kind ik, int arg ); /** is singular arg */ - bool isSingularArg( Node n, Kind ik, int arg ); + Node isSingularArg( Node n, Kind ik, int arg ); /** get offset arg */ bool hasOffsetArg( Kind ik, int arg, int& offset, Kind& ok ); /** get value */ @@ -643,10 +723,14 @@ public: Node getGenericBase( TypeNode tn, const Datatype& dt, int c ); Node mkGeneric( const Datatype& dt, int c, std::map< TypeNode, int >& var_count, std::map< int, Node >& pre ); Node sygusToBuiltin( Node n, TypeNode tn ); + Node sygusToBuiltin( Node n ) { return sygusToBuiltin( n, n.getType() ); } + Node sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args ); Node builtinToSygusConst( Node c, TypeNode tn, int rcons_depth = 0 ); Node getSygusNormalized( Node n, std::map< TypeNode, int >& var_count, std::map< Node, Node >& subs ); Node getNormalized( TypeNode t, Node prog, bool do_pre_norm = false, bool do_post_norm = true ); - int getSygusTermSize( Node n ); + unsigned getSygusTermSize( Node n ); + // returns size + unsigned getSygusConstructors( Node n, std::vector< Node >& cons ); /** given a term, construct an equivalent smaller one that respects syntax */ Node minimizeBuiltinTerm( Node n ); /** given a term, expand it into more basic components */ @@ -655,19 +739,36 @@ public: Kind getComparisonKind( TypeNode tn ); Kind getPlusKind( TypeNode tn, bool is_neg = false ); bool doCompare( Node a, Node b, Kind k ); + // get semantic skolem for n (a sygus term whose builtin version is n) + Node getSemanticSkolem( TypeNode tn, Node n, bool doMk = true ); + /** involves div-by-zero */ + bool involvesDivByZero( Node n ); + /** get operator kind */ static Kind getOperatorKind( Node op ); /** print sygus term */ static void printSygusTerm( std::ostream& out, Node n, std::vector< Node >& lvs ); - + /** get anchor */ static Node getAnchor( Node n ); + static unsigned getAnchorDepth( Node n ); + +public: // for symmetry breaking + bool considerArgKind( TypeNode tn, TypeNode tnp, Kind k, Kind pk, int arg ); + bool considerConst( TypeNode tn, TypeNode tnp, Node c, Kind pk, int arg ); + bool considerConst( const Datatype& pdt, TypeNode tnp, Node c, Kind pk, int arg ); + int solveForArgument( TypeNode tnp, unsigned cindex, unsigned arg ); + //for eager instantiation private: std::map< Node, std::map< Node, bool > > d_subterms; std::map< Node, std::vector< Node > > d_evals; std::map< Node, std::vector< std::vector< Node > > > d_eval_args; + std::map< Node, std::vector< bool > > d_eval_args_const; std::map< Node, std::map< Node, unsigned > > d_node_mv_args_proc; + + void getExplanationFor( TermRecBuild& trb, Node n, Node vn, std::vector< Node >& exp, std::map< TypeNode, int >& var_count, + SygusInvarianceTest& et, Node vnr, Node& vnr_exp, int& sz ); public: void registerEvalTerm( Node n ); void registerModelValue( Node n, Node v, std::vector< Node >& exps, std::vector< Node >& terms, std::vector< Node >& vals ); @@ -677,6 +778,87 @@ public: std::vector< Node > exp; return unfold( en, vtm, exp, false ); } + Node getEagerUnfold( Node n, std::map< Node, Node >& visited ); + // returns straightforward exp => n = vn + void getExplanationForConstantEquality( Node n, Node vn, std::vector< Node >& exp ); + void getExplanationForConstantEquality( Node n, Node vn, std::vector< Node >& exp, std::map< unsigned, bool >& cexc ); + Node getExplanationForConstantEquality( Node n, Node vn ); + Node getExplanationForConstantEquality( Node n, Node vn, std::map< unsigned, bool >& cexc ); + // we have n = vn => eval( n ) = bvr, returns exp => eval( n ) = bvr + // ensures the explanation still allows for vnr + void getExplanationFor( Node n, Node vn, std::vector< Node >& exp, SygusInvarianceTest& et, Node vnr, unsigned& sz ); + void getExplanationFor( Node n, Node vn, std::vector< Node >& exp, SygusInvarianceTest& et ); + // builtin evaluation, returns rewrite( bn [ args / vars(tn) ] ) + Node evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args ); + Node evaluateBuiltin( TypeNode tn, Node bn, Node ar, unsigned i ); + // evaluate with unfolding + Node evaluateWithUnfolding( Node n, std::map< Node, Node >& visited ); + Node evaluateWithUnfolding( Node n ); +//for calculating redundant operators +private: + //whether each constructor is redundant + // 0 : not redundant, 1 : redundant, 2 : partially redundant + std::map< TypeNode, std::vector< int > > d_sygus_red_status; + // type to (rewritten) to original + std::map< TypeNode, std::map< Node, Node > > d_gen_terms; + std::map< TypeNode, std::map< Node, bool > > d_gen_redundant; + //compute generic redundant + bool computeGenericRedundant( TypeNode tn, Node g ); +public: + bool isGenericRedundant( TypeNode tn, unsigned i ); + +//sygus pbe +private: + std::map< Node, std::vector< std::vector< Node > > > d_pbe_exs; + std::map< Node, std::vector< Node > > d_pbe_exos; + std::map< Node, unsigned > d_pbe_term_id; +private: + class PbeTrie { + private: + Node addPbeExampleEval( TypeNode etn, Node e, Node b, std::vector< Node >& ex, quantifiers::TermDbSygus * tds, unsigned index, unsigned ntotal ); + public: + PbeTrie(){} + ~PbeTrie(){} + Node d_lazy_child; + std::map< Node, PbeTrie > d_children; + void clear() { d_children.clear(); } + Node addPbeExample( TypeNode etn, Node e, Node b, TermDbSygus * tds, unsigned index, unsigned ntotal ); + }; + std::map< Node, std::map< TypeNode, PbeTrie > > d_pbe_trie; +public: + /** register examples for an enumerative search term. + This should be a comprehensive set of examples. */ + void registerPbeExamples( Node e, std::vector< std::vector< Node > >& exs, + std::vector< Node >& exos, std::vector< Node >& exts ); + /** get examples */ + bool hasPbeExamples( Node e ); + unsigned getNumPbeExamples( Node e ); + /** return value is the required value for the example */ + void getPbeExample( Node e, unsigned i, std::vector< Node >& ex ); + Node getPbeExampleOut( Node e, unsigned i ); + int getPbeExampleId( Node n ); + /** add the search val, returns an equivalent value (possibly the same) */ + Node addPbeSearchVal( TypeNode tn, Node e, Node bvr ); + +// extended rewriting +private: + std::map< Node, Node > d_ext_rewrite_cache; + Node extendedRewritePullIte( Node n ); +public: + Node extendedRewrite( Node n ); + +// for default grammar construction +private: + TypeNode mkUnresolvedType(const std::string& name, std::set<Type>& unres); + void mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops ); + void collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels ); + void mkSygusDefaultGrammar( TypeNode range, Node bvl, const std::string& fun, std::map< TypeNode, std::vector< Node > >& extra_cons, std::vector< CVC4::Datatype >& datatypes, std::set<Type>& unres ); +public: + TypeNode mkSygusDefaultType( TypeNode range, Node bvl, const std::string& fun, std::map< TypeNode, std::vector< Node > >& extra_cons ); + TypeNode mkSygusDefaultType( TypeNode range, Node bvl, const std::string& fun ){ + std::map< TypeNode, std::vector< Node > > extra_cons; + return mkSygusDefaultType( range, bvl, fun, extra_cons ); + } }; }/* CVC4::theory::quantifiers namespace */ diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index 64160dc71..00a358e5f 100644 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -43,6 +43,8 @@ TheoryQuantifiers::TheoryQuantifiers(Context* c, context::UserContext* u, Output out.handleUserAttribute( "conjecture", this ); out.handleUserAttribute( "fun-def", this ); out.handleUserAttribute( "sygus", this ); + out.handleUserAttribute( "sygus-synth-fun", this ); + out.handleUserAttribute( "sygus-synth-fun-var-list", this ); out.handleUserAttribute( "synthesis", this ); out.handleUserAttribute( "quant-inst-max-level", this ); out.handleUserAttribute( "rr-priority", this ); |