summaryrefslogtreecommitdiff
path: root/src/theory/quantifiers/sygus
diff options
context:
space:
mode:
Diffstat (limited to 'src/theory/quantifiers/sygus')
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_conjecture.cpp894
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_conjecture.h283
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_instantiation.cpp388
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_instantiation.h90
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_single_inv.cpp1004
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_single_inv.h248
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_single_inv_sol.cpp1512
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_single_inv_sol.h191
-rw-r--r--src/theory/quantifiers/sygus/sygus_explain.cpp301
-rw-r--r--src/theory/quantifiers/sygus/sygus_explain.h222
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_cons.cpp693
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_cons.h131
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_norm.cpp492
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_norm.h455
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_red.cpp136
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_red.h119
-rw-r--r--src/theory/quantifiers/sygus/sygus_invariance.cpp229
-rw-r--r--src/theory/quantifiers/sygus/sygus_invariance.h276
-rw-r--r--src/theory/quantifiers/sygus/sygus_pbe.cpp2460
-rw-r--r--src/theory/quantifiers/sygus/sygus_pbe.h802
-rw-r--r--src/theory/quantifiers/sygus/sygus_process_conj.cpp798
-rw-r--r--src/theory/quantifiers/sygus/sygus_process_conj.h365
-rw-r--r--src/theory/quantifiers/sygus/term_database_sygus.cpp1487
-rw-r--r--src/theory/quantifiers/sygus/term_database_sygus.h286
24 files changed, 13862 insertions, 0 deletions
diff --git a/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp b/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp
new file mode 100644
index 000000000..7bcaa0cba
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp
@@ -0,0 +1,894 @@
+/********************* */
+/*! \file ce_guided_conjecture.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief implementation of class that encapsulates counterexample-guided instantiation
+ ** techniques for a single SyGuS synthesis conjecture
+ **/
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+
+#include "expr/datatype.h"
+#include "options/base_options.h"
+#include "options/quantifiers_options.h"
+#include "printer/printer.h"
+#include "prop/prop_engine.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/quantifiers/sygus/ce_guided_instantiation.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/instantiate.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/skolemize.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/theory_engine.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+// recursion is not an issue since OR nodes are flattened by the (quantifiers) rewriter
+// this function is for sanity since solution correctness in SyGuS depends on fully miniscoping based on this function
+void collectDisjuncts( Node n, std::vector< Node >& d ) {
+ if( n.getKind()==OR ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ collectDisjuncts( n[i], d );
+ }
+ }else{
+ d.push_back( n );
+ }
+}
+
+CegConjecture::CegConjecture(QuantifiersEngine* qe)
+ : d_qe(qe),
+ d_ceg_si(new CegConjectureSingleInv(qe, this)),
+ d_ceg_pbe(new CegConjecturePbe(qe, this)),
+ d_ceg_proc(new CegConjectureProcess(qe)),
+ d_ceg_gc(new CegGrammarConstructor(qe, this)),
+ d_refine_count(0),
+ d_syntax_guided(false) {}
+
+CegConjecture::~CegConjecture() {}
+
+void CegConjecture::assign( Node q ) {
+ Assert( d_embed_quant.isNull() );
+ Assert( q.getKind()==FORALL );
+ Trace("cegqi") << "CegConjecture : assign : " << q << std::endl;
+ d_quant = q;
+
+ // pre-simplify the quantified formula based on the process utility
+ d_simp_quant = d_ceg_proc->preSimplify(d_quant);
+
+ std::map< Node, Node > templates;
+ std::map< Node, Node > templates_arg;
+ //register with single invocation if applicable
+ if (d_qe->getQuantAttributes()->isSygus(q))
+ {
+ d_ceg_si->initialize(d_simp_quant);
+ d_simp_quant = d_ceg_si->getSimplifiedConjecture();
+ // carry the templates
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node v = q[0][i];
+ Node templ = d_ceg_si->getTemplate(v);
+ if( !templ.isNull() ){
+ templates[v] = templ;
+ templates_arg[v] = d_ceg_si->getTemplateArg(v);
+ }
+ }
+ }
+
+ // post-simplify the quantified formula based on the process utility
+ d_simp_quant = d_ceg_proc->postSimplify(d_simp_quant);
+
+ // finished simplifying the quantified formula at this point
+
+ // convert to deep embedding and finalize single invocation here
+ d_embed_quant = d_ceg_gc->process(d_simp_quant, templates, templates_arg);
+ Trace("cegqi") << "CegConjecture : converted to embedding : " << d_embed_quant << std::endl;
+
+ // we now finalize the single invocation module, based on the syntax restrictions
+ if (d_qe->getQuantAttributes()->isSygus(q))
+ {
+ d_ceg_si->finishInit( d_ceg_gc->isSyntaxRestricted(), d_ceg_gc->hasSyntaxITE() );
+ }
+
+ Assert( d_candidates.empty() );
+ std::vector< Node > vars;
+ for( unsigned i=0; i<d_embed_quant[0].getNumChildren(); i++ ){
+ vars.push_back( d_embed_quant[0][i] );
+ Node e = NodeManager::currentNM()->mkSkolem( "e", d_embed_quant[0][i].getType() );
+ d_candidates.push_back( e );
+ }
+ Trace("cegqi") << "Base quantified formula is : " << d_embed_quant << std::endl;
+ //construct base instantiation
+ d_base_inst = Rewriter::rewrite(d_qe->getInstantiate()->getInstantiation(
+ d_embed_quant, vars, d_candidates));
+ Trace("cegqi") << "Base instantiation is : " << d_base_inst << std::endl;
+ d_base_body = d_base_inst;
+ if (d_base_body.getKind() == NOT && d_base_body[0].getKind() == FORALL)
+ {
+ for (const Node& v : d_base_body[0][0])
+ {
+ d_base_vars.push_back(v);
+ }
+ d_base_body = d_base_body[0][1];
+ }
+
+ // register this term with sygus database and other utilities that impact
+ // the enumerative sygus search
+ std::vector< Node > guarded_lemmas;
+ if( !isSingleInvocation() ){
+ d_ceg_proc->initialize(d_base_inst, d_candidates);
+ if( options::sygusPbe() ){
+ d_ceg_pbe->initialize(d_base_inst, d_candidates, guarded_lemmas);
+ } else {
+ for (unsigned i = 0; i < d_candidates.size(); i++) {
+ Node e = d_candidates[i];
+ d_qe->getTermDatabaseSygus()->registerEnumerator(e, e, this);
+ }
+ }
+ }
+
+ if (d_qe->getQuantAttributes()->isSygus(q))
+ {
+ collectDisjuncts( d_base_inst, d_base_disj );
+ Trace("cegqi") << "Conjecture has " << d_base_disj.size() << " disjuncts." << std::endl;
+ //store the inner variables for each disjunct
+ for( unsigned j=0; j<d_base_disj.size(); j++ ){
+ Trace("cegqi") << " " << j << " : " << d_base_disj[j] << std::endl;
+ d_inner_vars_disj.push_back( std::vector< Node >() );
+ //if the disjunct is an existential, store it
+ if( d_base_disj[j].getKind()==NOT && d_base_disj[j][0].getKind()==FORALL ){
+ for( unsigned k=0; k<d_base_disj[j][0][0].getNumChildren(); k++ ){
+ d_inner_vars.push_back( d_base_disj[j][0][0][k] );
+ d_inner_vars_disj[j].push_back( d_base_disj[j][0][0][k] );
+ }
+ }
+ }
+ d_syntax_guided = true;
+ }
+ else if (d_qe->getQuantAttributes()->isSynthesis(q))
+ {
+ d_syntax_guided = false;
+ }else{
+ Assert( false );
+ }
+
+ // 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->getGuard().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() );
+ 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 );
+ }
+
+ // assign the cegis sampler if applicable
+ if (options::cegisSample() != CEGIS_SAMPLE_NONE)
+ {
+ Trace("cegis-sample") << "Initialize sampler for " << d_base_body << "..."
+ << std::endl;
+ TypeNode bt = d_base_body.getType();
+ d_cegis_sampler.initialize(bt, d_base_vars, options::sygusSamples());
+ }
+
+ Trace("cegqi") << "...finished, single invocation = " << isSingleInvocation() << std::endl;
+}
+
+Node CegConjecture::getGuard() {
+ return !d_syntax_guided ? d_nsg_guard : d_ceg_si->getGuard();
+}
+
+bool CegConjecture::isSingleInvocation() const {
+ return d_ceg_si->isSingleInvocation();
+}
+
+bool CegConjecture::needsCheck( std::vector< Node >& lem ) {
+ if( isSingleInvocation() && !d_ceg_si->needsCheck() ){
+ return false;
+ }else{
+ bool value;
+ Assert( !getGuard().isNull() );
+ // non or fully single invocation : look at guard only
+ if( d_qe->getValuation().hasSatValue( getGuard(), value ) ) {
+ if( !value ){
+ Trace("cegqi-engine-debug") << "Conjecture is infeasible." << std::endl;
+ return false;
+ }
+ }else{
+ Assert( false );
+ }
+ return true;
+ }
+}
+
+
+void CegConjecture::doSingleInvCheck(std::vector< Node >& lems) {
+ if( d_ceg_si!=NULL ){
+ d_ceg_si->check(lems);
+ }
+}
+
+void CegConjecture::doBasicCheck(std::vector< Node >& lems) {
+ std::vector< Node > model_terms;
+ std::vector< Node > clist;
+ getCandidateList( clist, true );
+ Assert( clist.size()==d_quant[0].getNumChildren() );
+ getModelValues( clist, model_terms );
+ if (d_qe->getInstantiate()->addInstantiation(d_quant, model_terms))
+ {
+ //record the instantiation
+ recordInstantiation( model_terms );
+ }else{
+ Assert( false );
+ }
+}
+
+bool CegConjecture::needsRefinement() {
+ return !d_ce_sk.empty();
+}
+
+void CegConjecture::getCandidateList( std::vector< Node >& clist, bool forceOrig ) {
+ if( d_ceg_pbe->isPbe() && !forceOrig ){
+ d_ceg_pbe->getCandidateList( d_candidates, clist );
+ }else{
+ clist.insert( clist.end(), d_candidates.begin(), d_candidates.end() );
+ }
+}
+
+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( d_ceg_pbe->isPbe() ){
+ 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() );
+ }
+ return true;
+}
+
+void CegConjecture::doCheck(std::vector< Node >& lems, std::vector< Node >& model_values) {
+ std::vector< Node > clist;
+ getCandidateList( clist );
+ std::vector< Node > c_model_values;
+ Trace("cegqi-check") << "CegConjuncture : check, build candidates..." << std::endl;
+ 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;
+ }
+ }
+ 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 ){
+ if (options::cegisSample() == CEGIS_SAMPLE_TRUST)
+ {
+ // we have that the current candidate passed a sample test
+ // since we trust sampling in this mode, we assert there is no
+ // counterexample to the conjecture here.
+ NodeManager* nm = NodeManager::currentNM();
+ Node lem = nm->mkNode(OR, d_quant.negate(), nm->mkConst(false));
+ lem = getStreamGuardedLemma(lem);
+ lems.push_back(lem);
+ recordInstantiation(c_model_values);
+ return;
+ }
+ 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_quant.negate() );
+ std::vector< Node > d;
+ 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->getSkolemize()->getSkolemizedBody(dr[0]).negate());
+ }
+ if( sk_refine ){
+ Assert( !isGround() );
+ d_ce_sk.back().push_back( dr[0] );
+ }
+ }else{
+ if( constructed_cand ){
+ ic.push_back( dr );
+ 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 );
+ }
+ lem = getStreamGuardedLemma(lem);
+ lems.push_back( lem );
+ recordInstantiation( c_model_values );
+ }
+}
+
+void CegConjecture::doRefine( std::vector< Node >& lems ){
+ Assert( lems.empty() );
+ Assert( d_ce_sk.size()==1 );
+
+ //first, make skolem substitution
+ Trace("cegqi-refine") << "doRefine : construct skolem substitution..." << std::endl;
+ std::vector< Node > sk_vars;
+ std::vector< Node > sk_subs;
+ //collect the substitution over all disjuncts
+ for( unsigned k=0; k<d_ce_sk[0].size(); k++ ){
+ Node ce_q = d_ce_sk[0][k];
+ if( !ce_q.isNull() ){
+ Assert( !d_inner_vars_disj[k].empty() );
+ std::vector<Node> skolems;
+ d_qe->getSkolemize()->getSkolemConstants(ce_q, skolems);
+ Assert(d_inner_vars_disj[k].size() == skolems.size());
+ std::vector< Node > model_values;
+ getModelValues(skolems, model_values);
+ sk_vars.insert( sk_vars.end(), d_inner_vars_disj[k].begin(), d_inner_vars_disj[k].end() );
+ sk_subs.insert( sk_subs.end(), model_values.begin(), model_values.end() );
+ }else{
+ if( !d_inner_vars_disj[k].empty() ){
+ //denegrate case : quantified disjunct was trivially true and does not need to be refined
+ //add trivial substitution (in case we need substitution for previous cex's)
+ for( unsigned i=0; i<d_inner_vars_disj[k].size(); i++ ){
+ sk_vars.push_back( d_inner_vars_disj[k][i] );
+ sk_subs.push_back( getModelValue( d_inner_vars_disj[k][i] ) ); // will return dummy value
+ }
+ }
+ }
+ }
+
+ //for conditional evaluation
+ std::vector< Node > lem_c;
+ Assert( d_ce_sk[0].size()==d_base_disj.size() );
+ std::vector< Node > inst_cond_c;
+ Trace("cegqi-refine") << "doRefine : Construct refinement lemma..." << std::endl;
+ for( unsigned k=0; k<d_ce_sk[0].size(); k++ ){
+ Node ce_q = d_ce_sk[0][k];
+ Trace("cegqi-refine-debug") << " For counterexample point, disjunct " << k << " : " << ce_q << " " << d_base_disj[k] << std::endl;
+ Node c_disj;
+ if( !ce_q.isNull() ){
+ Assert( d_base_disj[k].getKind()==kind::NOT && d_base_disj[k][0].getKind()==kind::FORALL );
+ c_disj = d_base_disj[k][0][1];
+ }else{
+ if( d_inner_vars_disj[k].empty() ){
+ c_disj = d_base_disj[k].negate();
+ }else{
+ //denegrate case : quantified disjunct was trivially true and does not need to be refined
+ Trace("cegqi-refine-debug") << "*** skip " << d_base_disj[k] << std::endl;
+ }
+ }
+ if( !c_disj.isNull() ){
+ //compute the body, inst_cond
+ //standard CEGIS refinement : plug in values, assert that d_candidates must satisfy entire specification
+ lem_c.push_back( c_disj );
+ }
+ }
+ Assert( sk_vars.size()==sk_subs.size() );
+
+ Node base_lem = lem_c.size()==1 ? lem_c[0] : NodeManager::currentNM()->mkNode( AND, lem_c );
+
+ Trace("cegqi-refine") << "doRefine : construct and finalize lemmas..." << std::endl;
+
+
+ base_lem = base_lem.substitute( sk_vars.begin(), sk_vars.end(), sk_subs.begin(), sk_subs.end() );
+ base_lem = Rewriter::rewrite( base_lem );
+ d_refinement_lemmas.push_back(base_lem);
+
+ Node lem =
+ NodeManager::currentNM()->mkNode(OR, getGuard().negate(), base_lem);
+ lems.push_back( lem );
+
+ d_ce_sk.clear();
+}
+
+void CegConjecture::preregisterConjecture( Node q ) {
+ d_ceg_si->preregisterConjecture( q );
+}
+
+void CegConjecture::getModelValues( std::vector< Node >& n, std::vector< Node >& v ) {
+ Trace("cegqi-engine") << " * Value is : ";
+ for( unsigned i=0; i<n.size(); i++ ){
+ Node nv = getModelValue( n[i] );
+ v.push_back( nv );
+ if( Trace.isOn("cegqi-engine") ){
+ TypeNode tn = nv.getType();
+ Trace("cegqi-engine") << n[i] << " -> ";
+ std::stringstream ss;
+ Printer::getPrinter(options::outputLanguage())->toStreamSygus(ss, nv);
+ Trace("cegqi-engine") << ss.str() << " ";
+ if (Trace.isOn("cegqi-engine-rr"))
+ {
+ Node bv = d_qe->getTermDatabaseSygus()->sygusToBuiltin(nv, tn);
+ bv = Rewriter::rewrite(bv);
+ Trace("cegqi-engine-rr") << " -> " << bv << std::endl;
+ }
+ }
+ Assert( !nv.isNull() );
+ }
+ Trace("cegqi-engine") << std::endl;
+}
+
+Node CegConjecture::getModelValue( Node n ) {
+ Trace("cegqi-mv") << "getModelValue for : " << n << std::endl;
+ return d_qe->getModel()->getValue( n );
+}
+
+void CegConjecture::debugPrint( const char * c ) {
+ Trace(c) << "Synthesis conjecture : " << d_embed_quant << std::endl;
+ Trace(c) << " * Candidate program/output symbol : ";
+ for( unsigned i=0; i<d_candidates.size(); i++ ){
+ Trace(c) << d_candidates[i] << " ";
+ }
+ Trace(c) << std::endl;
+ Trace(c) << " * Candidate ce skolems : ";
+ for( unsigned i=0; i<d_ce_sk.size(); i++ ){
+ Trace(c) << d_ce_sk[i] << " ";
+ }
+}
+
+Node CegConjecture::getCurrentStreamGuard() const {
+ if( d_stream_guards.empty() ){
+ return Node::null();
+ }else{
+ return d_stream_guards.back();
+ }
+}
+
+Node CegConjecture::getStreamGuardedLemma(Node n) const
+{
+ if (options::sygusStream())
+ {
+ // if we are in streaming mode, we guard with the current stream guard
+ Node csg = getCurrentStreamGuard();
+ Assert(!csg.isNull());
+ return NodeManager::currentNM()->mkNode(kind::OR, csg.negate(), n);
+ }
+ return n;
+}
+
+Node CegConjecture::getNextDecisionRequest( unsigned& priority ) {
+ // first, must try the guard
+ // which denotes "this conjecture is feasible"
+ Node feasible_guard = getGuard();
+ bool value;
+ if( !d_qe->getValuation().hasSatValue( feasible_guard, value ) ) {
+ priority = 0;
+ return feasible_guard;
+ }else{
+ if( value ){
+ // the conjecture is feasible
+ if( options::sygusStream() ){
+ Assert( !isSingleInvocation() );
+ // if we are in sygus streaming mode, then get the "next guard"
+ // which denotes "we have not yet generated the next solution to the conjecture"
+ Node curr_stream_guard = getCurrentStreamGuard();
+ bool needs_new_stream_guard = false;
+ if( curr_stream_guard.isNull() ){
+ needs_new_stream_guard = true;
+ }else{
+ // check the polarity of the guard
+ if( !d_qe->getValuation().hasSatValue( curr_stream_guard, value ) ) {
+ priority = 0;
+ return curr_stream_guard;
+ }else{
+ if( !value ){
+ Trace("cegqi-debug") << "getNextDecision : we have a new solution since stream guard was propagated false: " << curr_stream_guard << std::endl;
+ // we have generated a solution, print it
+ // get the current output stream
+ // this output stream should coincide with wherever --dump-synth is output on
+ Options& nodeManagerOptions = NodeManager::currentNM()->getOptions();
+ printSynthSolution( *nodeManagerOptions.getOut(), false );
+ // need to make the next stream guard
+ needs_new_stream_guard = true;
+
+ // We will not refine the current candidate solution since it is a solution
+ // thus, we clear information regarding the current refinement
+ d_ce_sk.clear();
+ // However, we need to exclude the current solution using an explicit refinement
+ // so that we proceed to the next solution.
+ std::vector< Node > clist;
+ getCandidateList( clist );
+ Trace("cegqi-debug") << "getNextDecision : solution was : " << std::endl;
+ std::vector< Node > exp;
+ for( unsigned i=0; i<clist.size(); i++ ){
+ Node cprog = clist[i];
+ Node sol = cprog;
+ if( !d_cinfo[cprog].d_inst.empty() ){
+ sol = d_cinfo[cprog].d_inst.back();
+ // add to explanation of exclusion
+ d_qe->getTermDatabaseSygus()
+ ->getExplain()
+ ->getExplanationForConstantEquality(cprog, sol, exp);
+ }
+ Trace("cegqi-debug") << " " << cprog << " -> " << sol << std::endl;
+ }
+ Assert( !exp.empty() );
+ Node exc_lem = exp.size()==1 ? exp[0] : NodeManager::currentNM()->mkNode( kind::AND, exp );
+ exc_lem = exc_lem.negate();
+ Trace("cegqi-lemma") << "Cegqi::Lemma : stream exclude current solution : " << exc_lem << std::endl;
+ d_qe->getOutputChannel().lemma( exc_lem );
+ }
+ }
+ }
+ if( needs_new_stream_guard ){
+ // generate a new stream guard
+ curr_stream_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G_Stream", NodeManager::currentNM()->booleanType() ) );
+ curr_stream_guard = d_qe->getValuation().ensureLiteral( curr_stream_guard );
+ AlwaysAssert( !curr_stream_guard.isNull() );
+ d_qe->getOutputChannel().requirePhase( curr_stream_guard, true );
+ d_stream_guards.push_back( curr_stream_guard );
+ Trace("cegqi-debug") << "getNextDecision : allocate new stream guard : " << curr_stream_guard << std::endl;
+ // return it as a decision
+ priority = 0;
+ return curr_stream_guard;
+ }
+ }
+ }else{
+ Trace("cegqi-debug") << "getNextDecision : conjecture is infeasible." << std::endl;
+ }
+ }
+ return Node::null();
+}
+
+void CegConjecture::printSynthSolution( std::ostream& out, bool singleInvocation ) {
+ Trace("cegqi-debug") << "Printing synth solution..." << std::endl;
+ Assert( d_quant[0].getNumChildren()==d_embed_quant[0].getNumChildren() );
+ std::vector<Node> sols;
+ std::vector<int> statuses;
+ getSynthSolutionsInternal(sols, statuses, singleInvocation);
+ for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
+ {
+ Node sol = sols[i];
+ if (!sol.isNull())
+ {
+ Node prog = d_embed_quant[0][i];
+ int status = statuses[i];
+ TypeNode tn = prog.getType();
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ std::stringstream ss;
+ ss << prog;
+ std::string f(ss.str());
+ f.erase(f.begin());
+ out << "(define-fun " << f << " ";
+ if( dt.getSygusVarList().isNull() ){
+ out << "() ";
+ }else{
+ out << dt.getSygusVarList() << " ";
+ }
+ out << dt.getSygusType() << " ";
+ if( status==0 ){
+ out << sol;
+ }else{
+ Printer::getPrinter(options::outputLanguage())->toStreamSygus(out, sol);
+ }
+ out << ")" << std::endl;
+ CegInstantiation* cei = d_qe->getCegInstantiation();
+ ++(cei->d_statistics.d_solutions);
+
+ if (status != 0 && options::sygusRewSynth())
+ {
+ TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
+ std::map<Node, SygusSamplerExt>::iterator its = d_sampler.find(prog);
+ if (its == d_sampler.end())
+ {
+ d_sampler[prog].initializeSygusExt(
+ d_qe, prog, options::sygusSamples());
+ its = d_sampler.find(prog);
+ }
+ Node solb = sygusDb->sygusToBuiltin(sol, prog.getType());
+ Node eq_sol = its->second.registerTerm(solb);
+ // eq_sol is a candidate solution that is equivalent to sol
+ if (eq_sol != solb)
+ {
+ ++(cei->d_statistics.d_candidate_rewrites);
+ if (!eq_sol.isNull())
+ {
+ // Terms solb and eq_sol are equivalent under sample points but do
+ // not rewrite to the same term. Hence, this indicates a candidate
+ // rewrite.
+ out << "(candidate-rewrite " << solb << " " << eq_sol << ")"
+ << std::endl;
+ ++(cei->d_statistics.d_candidate_rewrites_print);
+ // debugging information
+ if (Trace.isOn("sygus-rr-debug"))
+ {
+ ExtendedRewriter* er = sygusDb->getExtRewriter();
+ Node solbr = er->extendedRewrite(solb);
+ Node eq_solr = er->extendedRewrite(eq_sol);
+ Trace("sygus-rr-debug")
+ << "; candidate #1 ext-rewrites to: " << solbr << std::endl;
+ Trace("sygus-rr-debug")
+ << "; candidate #2 ext-rewrites to: " << eq_solr << std::endl;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void CegConjecture::getSynthSolutions(std::map<Node, Node>& sol_map,
+ bool singleInvocation)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
+ std::vector<Node> sols;
+ std::vector<int> statuses;
+ getSynthSolutionsInternal(sols, statuses, singleInvocation);
+ for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
+ {
+ Node sol = sols[i];
+ int status = statuses[i];
+ // get the builtin solution
+ Node bsol = sol;
+ if (status != 0)
+ {
+ // convert sygus to builtin here
+ bsol = sygusDb->sygusToBuiltin(sol, sol.getType());
+ }
+ // convert to lambda
+ TypeNode tn = d_embed_quant[0][i].getType();
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Node bvl = Node::fromExpr(dt.getSygusVarList());
+ if (!bvl.isNull())
+ {
+ bsol = nm->mkNode(LAMBDA, bvl, bsol);
+ }
+ // store in map
+ Node fvar = d_quant[0][i];
+ Assert(fvar.getType() == bsol.getType());
+ sol_map[fvar] = bsol;
+ }
+}
+
+void CegConjecture::getSynthSolutionsInternal(std::vector<Node>& sols,
+ std::vector<int>& statuses,
+ bool singleInvocation)
+{
+ for (unsigned i = 0, size = d_embed_quant[0].getNumChildren(); i < size; i++)
+ {
+ Node prog = d_embed_quant[0][i];
+ Trace("cegqi-debug") << " get solution for " << prog << std::endl;
+ TypeNode tn = prog.getType();
+ Assert(tn.isDatatype());
+ // get the solution
+ Node sol;
+ int status = -1;
+ if (singleInvocation)
+ {
+ Assert(d_ceg_si != NULL);
+ sol = d_ceg_si->getSolution(i, tn, status, true);
+ if (!sol.isNull())
+ {
+ sol = sol.getKind() == LAMBDA ? sol[1] : sol;
+ }
+ }
+ else
+ {
+ Node cprog = getCandidate(i);
+ if (!d_cinfo[cprog].d_inst.empty())
+ {
+ // the solution is just the last instantiated term
+ sol = d_cinfo[cprog].d_inst.back();
+ status = 1;
+
+ // check if there was a template
+ Node sf = d_quant[0][i];
+ Node templ = d_ceg_si->getTemplate(sf);
+ if (!templ.isNull())
+ {
+ Trace("cegqi-inv-debug")
+ << sf << " used template : " << templ << std::endl;
+ // if it was not embedded into the grammar
+ if (!options::sygusTemplEmbedGrammar())
+ {
+ TNode templa = d_ceg_si->getTemplateArg(sf);
+ // make the builtin version of the full solution
+ TermDbSygus* sygusDb = d_qe->getTermDatabaseSygus();
+ sol = sygusDb->sygusToBuiltin(sol, sol.getType());
+ Trace("cegqi-inv") << "Builtin version of solution is : " << sol
+ << ", type : " << sol.getType() << std::endl;
+ TNode tsol = sol;
+ sol = templ.substitute(templa, tsol);
+ Trace("cegqi-inv-debug") << "With template : " << sol << std::endl;
+ sol = Rewriter::rewrite(sol);
+ Trace("cegqi-inv-debug") << "Simplified : " << sol << std::endl;
+ // now, reconstruct to the syntax
+ sol = d_ceg_si->reconstructToSyntax(sol, tn, status, true);
+ sol = sol.getKind() == LAMBDA ? sol[1] : sol;
+ Trace("cegqi-inv-debug")
+ << "Reconstructed to syntax : " << sol << std::endl;
+ }
+ else
+ {
+ Trace("cegqi-inv-debug")
+ << "...was embedding into grammar." << std::endl;
+ }
+ }
+ else
+ {
+ Trace("cegqi-inv-debug")
+ << sf << " did not use template" << std::endl;
+ }
+ }
+ else
+ {
+ Trace("cegqi-warn") << "WARNING : No recorded instantiations for "
+ "syntax-guided solution!"
+ << std::endl;
+ }
+ }
+ sols.push_back(sol);
+ statuses.push_back(status);
+ }
+}
+
+Node CegConjecture::getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
+{
+ std::vector<Node> sb_lemmas;
+
+ // based on simple preprocessing
+ Node ppred =
+ d_ceg_proc->getSymmetryBreakingPredicate(x, e, tn, tindex, depth);
+ if (!ppred.isNull())
+ {
+ sb_lemmas.push_back(ppred);
+ }
+
+ // other static conjecture-dependent symmetry breaking goes here
+
+ if (!sb_lemmas.empty())
+ {
+ return sb_lemmas.size() == 1
+ ? sb_lemmas[0]
+ : NodeManager::currentNM()->mkNode(kind::AND, sb_lemmas);
+ }
+ else
+ {
+ return Node::null();
+ }
+}
+
+bool CegConjecture::sampleAddRefinementLemma(std::vector<Node>& vals,
+ std::vector<Node>& lems)
+{
+ if (Trace.isOn("cegis-sample"))
+ {
+ Trace("cegis-sample") << "Check sampling for candidate solution"
+ << std::endl;
+ for (unsigned i = 0, size = vals.size(); i < size; i++)
+ {
+ Trace("cegis-sample")
+ << " " << d_candidates[i] << " -> " << vals[i] << std::endl;
+ }
+ }
+ Assert(vals.size() == d_candidates.size());
+ Node sbody = d_base_body.substitute(
+ d_candidates.begin(), d_candidates.end(), vals.begin(), vals.end());
+ Trace("cegis-sample-debug") << "Sample " << sbody << std::endl;
+ // do eager unfolding
+ std::map<Node, Node> visited_n;
+ sbody = d_qe->getTermDatabaseSygus()->getEagerUnfold(sbody, visited_n);
+ Trace("cegis-sample") << "Sample (after unfolding): " << sbody << std::endl;
+
+ NodeManager* nm = NodeManager::currentNM();
+ for (unsigned i = 0, size = d_cegis_sampler.getNumSamplePoints(); i < size;
+ i++)
+ {
+ if (d_cegis_sample_refine.find(i) == d_cegis_sample_refine.end())
+ {
+ Node ev = d_cegis_sampler.evaluate(sbody, i);
+ Trace("cegis-sample-debug")
+ << "...evaluate point #" << i << " to " << ev << std::endl;
+ Assert(ev.isConst());
+ Assert(ev.getType().isBoolean());
+ if (!ev.getConst<bool>())
+ {
+ Trace("cegis-sample-debug") << "...false for point #" << i << std::endl;
+ // mark this as a CEGIS point (no longer sampled)
+ d_cegis_sample_refine.insert(i);
+ std::vector<Node> vars;
+ std::vector<Node> pt;
+ d_cegis_sampler.getSamplePoint(i, vars, pt);
+ Assert(d_base_vars.size() == pt.size());
+ Node rlem = d_base_body.substitute(
+ d_base_vars.begin(), d_base_vars.end(), pt.begin(), pt.end());
+ rlem = Rewriter::rewrite(rlem);
+ if (std::find(
+ d_refinement_lemmas.begin(), d_refinement_lemmas.end(), rlem)
+ == d_refinement_lemmas.end())
+ {
+ if (Trace.isOn("cegis-sample"))
+ {
+ Trace("cegis-sample") << " false for point #" << i << " : ";
+ for (const Node& cn : pt)
+ {
+ Trace("cegis-sample") << cn << " ";
+ }
+ Trace("cegis-sample") << std::endl;
+ }
+ Trace("cegqi-engine") << " *** Refine by sampling" << std::endl;
+ d_refinement_lemmas.push_back(rlem);
+ // if trust, we are not interested in sending out refinement lemmas
+ if (options::cegisSample() != CEGIS_SAMPLE_TRUST)
+ {
+ Node lem = nm->mkNode(OR, getGuard().negate(), rlem);
+ lems.push_back(lem);
+ }
+ return true;
+ }
+ else
+ {
+ Trace("cegis-sample-debug") << "...duplicate." << std::endl;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
diff --git a/src/theory/quantifiers/sygus/ce_guided_conjecture.h b/src/theory/quantifiers/sygus/ce_guided_conjecture.h
new file mode 100644
index 000000000..1ef8fef10
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_conjecture.h
@@ -0,0 +1,283 @@
+/********************* */
+/*! \file ce_guided_conjecture.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief class that encapsulates counterexample-guided instantiation
+ ** techniques for a single SyGuS synthesis conjecture
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_CONJECTURE_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_CONJECTURE_H
+
+#include <memory>
+
+#include "theory/quantifiers/sygus/sygus_pbe.h"
+#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
+#include "theory/quantifiers/sygus/sygus_grammar_cons.h"
+#include "theory/quantifiers/sygus/sygus_process_conj.h"
+#include "theory/quantifiers/sygus_sampler.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** a synthesis conjecture
+ * This class implements approaches for a synthesis conecjture, given by data
+ * member d_quant.
+ * This includes both approaches for synthesis in Reynolds et al CAV 2015. It
+ * determines which approach and optimizations are applicable to the
+ * conjecture, and has interfaces for implementing them.
+ */
+class CegConjecture {
+public:
+ CegConjecture( QuantifiersEngine * qe );
+ ~CegConjecture();
+ /** get original version of conjecture */
+ Node getConjecture() { return d_quant; }
+ /** get deep embedding version of conjecture */
+ Node getEmbeddedConjecture() { return d_embed_quant; }
+ /** get next decision request */
+ Node getNextDecisionRequest( unsigned& priority );
+
+ //-------------------------------for counterexample-guided check/refine
+ /** increment the number of times we have successfully done candidate
+ * refinement */
+ void incrementRefineCount() { d_refine_count++; }
+ /** whether the conjecture is waiting for a call to doCheck below */
+ bool needsCheck( std::vector< Node >& lem );
+ /** whether the conjecture is waiting for a call to doRefine below */
+ bool needsRefinement();
+ /** get the list of candidates */
+ void getCandidateList( std::vector< Node >& clist, bool forceOrig = false );
+ /** do single invocation check
+ * This updates Gamma for an iteration of step 2 of Figure 1 of Reynolds et al CAV 2015.
+ */
+ void doSingleInvCheck(std::vector< Node >& lems);
+ /** do syntax-guided enumerative check
+ * This is step 2(a) of Figure 3 of Reynolds et al CAV 2015.
+ */
+ void doCheck(std::vector< Node >& lems, std::vector< Node >& model_values);
+ /** do basic check
+ * This is called for non-SyGuS synthesis conjectures
+ */
+ void doBasicCheck(std::vector< Node >& lems);
+ /** do refinement
+ * This is step 2(b) of Figure 3 of Reynolds et al CAV 2015.
+ */
+ void doRefine(std::vector< Node >& lems);
+ //-------------------------------end for counterexample-guided check/refine
+ /**
+ * prints the synthesis solution to output stream out.
+ *
+ * singleInvocation : set to true if we should consult the single invocation
+ * module to get synthesis solutions.
+ */
+ void printSynthSolution( std::ostream& out, bool singleInvocation );
+ /** get synth solutions
+ *
+ * This returns a map from function-to-synthesize variables to their
+ * builtin solution, which has the same type. For example, for synthesis
+ * conjecture exists f. forall x. f( x )>x, this function may return the map
+ * containing the entry:
+ * f -> (lambda x. x+1)
+ *
+ * singleInvocation : set to true if we should consult the single invocation
+ * module to get synthesis solutions.
+ */
+ void getSynthSolutions(std::map<Node, Node>& sol_map, bool singleInvocation);
+ /** get guard, this is "G" in Figure 3 of Reynolds et al CAV 2015 */
+ Node getGuard();
+ /** is ground */
+ bool isGround() { return d_inner_vars.empty(); }
+ /** does this conjecture correspond to a syntax-guided synthesis input */
+ bool isSyntaxGuided() const { return d_syntax_guided; }
+ /** are we using single invocation techniques */
+ bool isSingleInvocation() const;
+ /** preregister conjecture
+ * This is used as a heuristic for solution reconstruction, so that we
+ * remember expressions in the conjecture before preprocessing, since they
+ * may be helpful during solution reconstruction (Figure 5 of Reynolds et al CAV 2015)
+ */
+ void preregisterConjecture( Node q );
+ /** assign conjecture q to this class */
+ void assign( Node q );
+ /** has a conjecture been assigned to this class */
+ bool isAssigned() { return !d_embed_quant.isNull(); }
+ /** get model values for terms n, store in vector v */
+ void getModelValues( std::vector< Node >& n, std::vector< Node >& v );
+ /** get model value for term n */
+ Node getModelValue( Node n );
+
+ //-----------------------------------refinement lemmas
+ /** get number of refinement lemmas we have added so far */
+ unsigned getNumRefinementLemmas() { return d_refinement_lemmas.size(); }
+ /** get refinement lemma
+ *
+ * If d_embed_quant is forall d. exists y. P( d, y ), then a refinement
+ * lemma is one of the form ~P( d_candidates, c ) for some c.
+ */
+ Node getRefinementLemma( unsigned i ) { return d_refinement_lemmas[i]; }
+ /** sample add refinement lemma
+ *
+ * This function will check if there is a sample point in d_sampler that
+ * refutes the candidate solution (d_quant_vars->vals). If so, it adds a
+ * refinement lemma to the lists d_refinement_lemmas that corresponds to that
+ * sample point, and adds a lemma to lems if cegisSample mode is not trust.
+ */
+ bool sampleAddRefinementLemma(std::vector<Node>& vals,
+ std::vector<Node>& lems);
+ //-----------------------------------end refinement lemmas
+
+ /** get program by examples utility */
+ CegConjecturePbe* getPbe() { return d_ceg_pbe.get(); }
+ /** get utility for static preprocessing and analysis of conjectures */
+ CegConjectureProcess* getProcess() { return d_ceg_proc.get(); }
+ /** get the symmetry breaking predicate for type */
+ Node getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
+ /** print out debug information about this conjecture */
+ void debugPrint( const char * c );
+private:
+ /** reference to quantifier engine */
+ QuantifiersEngine * d_qe;
+ /** single invocation utility */
+ std::unique_ptr<CegConjectureSingleInv> d_ceg_si;
+ /** program by examples utility */
+ std::unique_ptr<CegConjecturePbe> d_ceg_pbe;
+ /** utility for static preprocessing and analysis of conjectures */
+ std::unique_ptr<CegConjectureProcess> d_ceg_proc;
+ /** grammar utility */
+ std::unique_ptr<CegGrammarConstructor> d_ceg_gc;
+ /** list of constants for quantified formula
+ * The outer Skolems for the negation of d_embed_quant.
+ */
+ std::vector< Node > d_candidates;
+ /** base instantiation
+ * If d_embed_quant is forall d. exists y. P( d, y ), then
+ * this is the formula exists y. P( d_candidates, y ).
+ */
+ Node d_base_inst;
+ /** If d_base_inst is exists y. P( d, y ), then this is y. */
+ std::vector<Node> d_base_vars;
+ /**
+ * If d_base_inst is exists y. P( d, y ), then this is the formula
+ * P( d_candidates, y ).
+ */
+ Node d_base_body;
+ /** expand base inst to disjuncts */
+ std::vector< Node > d_base_disj;
+ /** list of variables on inner quantification */
+ std::vector< Node > d_inner_vars;
+ 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;
+
+ //-----------------------------------refinement lemmas
+ /** refinement lemmas */
+ std::vector< Node > d_refinement_lemmas;
+ //-----------------------------------end refinement lemmas
+
+ /** the asserted (negated) conjecture */
+ Node d_quant;
+ /** (negated) conjecture after simplification */
+ Node d_simp_quant;
+ /** (negated) conjecture after simplification, conversion to deep embedding */
+ Node d_embed_quant;
+ /** candidate information */
+ class CandidateInfo {
+ public:
+ CandidateInfo(){}
+ /** list of terms we have instantiated candidates with */
+ std::vector< Node > d_inst;
+ };
+ std::map< Node, CandidateInfo > d_cinfo;
+ /** number of times we have called doRefine */
+ unsigned d_refine_count;
+ /** construct candidates */
+ bool constructCandidates( std::vector< Node >& clist, std::vector< Node >& model_values,
+ std::vector< Node >& candidate_values, std::vector< Node >& lems );
+ /** get candidadate */
+ Node getCandidate( unsigned int i ) { return d_candidates[i]; }
+ /** record instantiation (this is used to construct solutions later) */
+ void recordInstantiation( std::vector< Node >& vs ) {
+ Assert( vs.size()==d_candidates.size() );
+ for( unsigned i=0; i<vs.size(); i++ ){
+ d_cinfo[d_candidates[i]].d_inst.push_back( vs[i] );
+ }
+ }
+ /** get synth solutions internal
+ *
+ * This function constructs the body of solutions for all
+ * functions-to-synthesize in this conjecture and stores them in sols, in
+ * order. For each solution added to sols, we add an integer indicating what
+ * kind of solution n is, where if sols[i] = n, then
+ * if status[i] = 0: n is the (builtin term) corresponding to the solution,
+ * if status[i] = 1: n is the sygus representation of the solution.
+ * We store builtin versions under some conditions (such as when the sygus
+ * grammar is being ignored).
+ *
+ * singleInvocation : set to true if we should consult the single invocation
+ * module to get synthesis solutions.
+ *
+ * For example, for conjecture exists fg. forall x. f(x)>g(x), this function
+ * may set ( sols, status ) to ( { x+1, d_x() }, { 1, 0 } ), where d_x() is
+ * the sygus datatype constructor corresponding to variable x.
+ */
+ void getSynthSolutionsInternal(std::vector<Node>& sols,
+ std::vector<int>& status,
+ bool singleInvocation);
+ //-------------------------------- sygus stream
+ /** the streaming guards for sygus streaming mode */
+ std::vector< Node > d_stream_guards;
+ /** get current stream guard */
+ Node getCurrentStreamGuard() const;
+ /** get stream guarded lemma
+ *
+ * If sygusStream is enabled, this returns ( G V n ) where G is the guard
+ * returned by getCurrentStreamGuard, otherwise this returns n.
+ */
+ Node getStreamGuardedLemma(Node n) const;
+ //-------------------------------- end sygus stream
+ //-------------------------------- non-syntax guided (deprecated)
+ /** Whether we are syntax-guided (e.g. was the input in SyGuS format).
+ * This includes SyGuS inputs where no syntactic restrictions are provided.
+ */
+ bool d_syntax_guided;
+ /** the guard for non-syntax-guided synthesis */
+ Node d_nsg_guard;
+ //-------------------------------- end non-syntax guided (deprecated)
+ /** sygus sampler objects for each program variable
+ *
+ * This is used for the sygusRewSynth() option to synthesize new candidate
+ * rewrite rules.
+ */
+ std::map<Node, SygusSamplerExt> d_sampler;
+ /** sampler object for the option cegisSample()
+ *
+ * This samples points of the type of the inner variables of the synthesis
+ * conjecture (d_base_vars).
+ */
+ SygusSampler d_cegis_sampler;
+ /** cegis sample refine points
+ *
+ * Stores the list of indices of sample points in d_cegis_sampler we have
+ * added as refinement lemmas.
+ */
+ std::unordered_set<unsigned> d_cegis_sample_refine;
+};
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
diff --git a/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp b/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp
new file mode 100644
index 000000000..35098f5ea
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp
@@ -0,0 +1,388 @@
+/********************* */
+/*! \file ce_guided_instantiation.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King, Morgan Deters
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief counterexample guided instantiation class
+ ** This class is the entry point for both synthesis algorithms in Reynolds et al CAV 2015
+ **
+ **/
+#include "theory/quantifiers/sygus/ce_guided_instantiation.h"
+
+#include "options/quantifiers_options.h"
+#include "smt/smt_statistics_registry.h"
+#include "theory/theory_engine.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+//FIXME : remove this include (github issue #1156)
+#include "theory/bv/theory_bv_rewriter.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+CegInstantiation::CegInstantiation( QuantifiersEngine * qe, context::Context* c ) : QuantifiersModule( qe ){
+ d_conj = new CegConjecture( qe );
+ d_last_inst_si = false;
+}
+
+CegInstantiation::~CegInstantiation(){
+ delete d_conj;
+}
+
+bool CegInstantiation::needsCheck( Theory::Effort e ) {
+ return e>=Theory::EFFORT_LAST_CALL;
+}
+
+QuantifiersModule::QEffort CegInstantiation::needsModel(Theory::Effort e)
+{
+ return d_conj->isSingleInvocation() ? QEFFORT_STANDARD : QEFFORT_MODEL;
+}
+
+void CegInstantiation::check(Theory::Effort e, QEffort quant_e)
+{
+ unsigned echeck =
+ d_conj->isSingleInvocation() ? QEFFORT_STANDARD : QEFFORT_MODEL;
+ if( quant_e==echeck ){
+ Trace("cegqi-engine") << "---Counterexample Guided Instantiation Engine---" << std::endl;
+ Trace("cegqi-engine-debug") << std::endl;
+ bool active = false;
+ bool value;
+ if( d_quantEngine->getValuation().hasSatValue( d_conj->getConjecture(), value ) ) {
+ active = value;
+ }else{
+ Trace("cegqi-engine-debug") << "...no value for quantified formula." << std::endl;
+ }
+ Trace("cegqi-engine-debug") << "Current conjecture status : active : " << active << std::endl;
+ std::vector< Node > lem;
+ if( active && d_conj->needsCheck( lem ) ){
+ checkCegConjecture( d_conj );
+ }else{
+ Trace("cegqi-engine-debug") << "...does not need check." << std::endl;
+ for( unsigned i=0; i<lem.size(); i++ ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : check lemma : " << lem[i] << std::endl;
+ d_quantEngine->addLemma( lem[i] );
+ }
+ }
+ Trace("cegqi-engine") << "Finished Counterexample Guided Instantiation engine." << std::endl;
+ }
+}
+
+void CegInstantiation::registerQuantifier( Node q ) {
+ 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 );
+ }else{
+ Assert( d_conj->getEmbeddedConjecture()==q );
+ }
+ }else{
+ Trace("cegqi-debug") << "Register quantifier : " << q << std::endl;
+ }
+}
+
+Node CegInstantiation::getNextDecisionRequest( unsigned& priority ) {
+ if( d_conj->isAssigned() ){
+ Node dec_req = d_conj->getNextDecisionRequest( priority );
+ if( !dec_req.isNull() ){
+ Trace("cegqi-debug") << "CEGQI : Decide next on : " << dec_req << "..." << std::endl;
+ return dec_req;
+ }
+ }
+ return Node::null();
+}
+
+void CegInstantiation::checkCegConjecture( CegConjecture * conj ) {
+ Node q = conj->getEmbeddedConjecture();
+ Node aq = conj->getConjecture();
+ if( Trace.isOn("cegqi-engine-debug") ){
+ conj->debugPrint("cegqi-engine-debug");
+ Trace("cegqi-engine-debug") << std::endl;
+ }
+
+ if( !conj->needsRefinement() ){
+ Trace("cegqi-engine-debug") << "Do conjecture check..." << std::endl;
+ if( conj->isSyntaxGuided() ){
+ std::vector< Node > clems;
+ conj->doSingleInvCheck( clems );
+ if( !clems.empty() ){
+ d_last_inst_si = true;
+ for( unsigned j=0; j<clems.size(); j++ ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : single invocation instantiation : " << clems[j] << std::endl;
+ d_quantEngine->addLemma( clems[j] );
+ }
+ d_statistics.d_cegqi_si_lemmas += clems.size();
+ Trace("cegqi-engine") << " ...try single invocation." << std::endl;
+ return;
+ }
+ //ignore return value here
+ std::vector< Node > clist;
+ conj->getCandidateList( clist );
+ std::vector< Node > model_values;
+ conj->getModelValues( clist, model_values );
+ if( options::sygusDirectEval() ){
+ bool addedEvalLemmas = false;
+ if( options::sygusCRefEval() ){
+ 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() ){
+ for( unsigned j=0; j<cre_lems.size(); j++ ){
+ Node lem = cre_lems[j];
+ if( d_quantEngine->addLemma( lem ) ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : cref evaluation : " << lem << std::endl;
+ addedEvalLemmas = true;
+ }
+ }
+ if( addedEvalLemmas ){
+ //return;
+ }
+ }
+ }
+ Trace("cegqi-engine") << " *** Do direct evaluation..." << std::endl;
+ std::vector< Node > eager_terms;
+ std::vector< Node > eager_vals;
+ std::vector< Node > eager_exps;
+ for( unsigned j=0; j<clist.size(); j++ ){
+ Trace("cegqi-debug") << " register " << clist[j] << " -> " << model_values[j] << std::endl;
+ d_quantEngine->getTermDatabaseSygus()->registerModelValue( clist[j], model_values[j], eager_terms, eager_vals, eager_exps );
+ }
+ Trace("cegqi-debug") << "...produced " << eager_terms.size() << " eager evaluation lemmas." << std::endl;
+ if( !eager_terms.empty() ){
+ 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 (github issue #1156)
+ lem = bv::TheoryBVRewriter::eliminateBVSDiv( lem );
+ }
+ if( d_quantEngine->addLemma( lem ) ){
+ Trace("cegqi-lemma") << "Cegqi::Lemma : evaluation : " << lem << std::endl;
+ addedEvalLemmas = true;
+ }
+ }
+ }
+ if( addedEvalLemmas ){
+ return;
+ }
+ }
+
+ Trace("cegqi-engine") << " *** Check candidate phase..." << std::endl;
+ std::vector< Node > cclems;
+ conj->doCheck( cclems, model_values );
+ bool addedLemma = false;
+ for( unsigned i=0; i<cclems.size(); i++ ){
+ Node lem = cclems[i];
+ d_last_inst_si = false;
+ Trace("cegqi-lemma") << "Cegqi::Lemma : counterexample : " << lem << std::endl;
+ if( d_quantEngine->addLemma( lem ) ){
+ ++(d_statistics.d_cegqi_lemmas_ce);
+ addedLemma = true;
+ }else{
+ //this may happen if we eagerly unfold, simplify to true
+ if( !options::sygusDirectEval() ){
+ Trace("cegqi-warn") << " ...FAILED to add candidate!" << std::endl;
+ }else{
+ Trace("cegqi-engine-debug") << " ...FAILED to add candidate!" << std::endl;
+ }
+ }
+ }
+ if( addedLemma ){
+ Trace("cegqi-engine") << " ...check for counterexample." << std::endl;
+ }else{
+ if( conj->needsRefinement() ){
+ //immediately go to refine candidate
+ checkCegConjecture( conj );
+ return;
+ }
+ }
+ }else{
+ Assert( aq==q );
+ Trace("cegqi-engine") << " *** Check candidate phase (non-SyGuS)." << std::endl;
+ std::vector< Node > lems;
+ conj->doBasicCheck(lems);
+ Assert(lems.empty());
+ }
+ }else{
+ Trace("cegqi-engine") << " *** Refine candidate phase..." << std::endl;
+ std::vector< Node > rlems;
+ conj->doRefine( rlems );
+ bool addedLemma = false;
+ for( unsigned i=0; i<rlems.size(); i++ ){
+ Node lem = rlems[i];
+ Trace("cegqi-lemma") << "Cegqi::Lemma : candidate refinement : " << lem << std::endl;
+ bool res = d_quantEngine->addLemma( lem );
+ if( res ){
+ ++(d_statistics.d_cegqi_lemmas_refine);
+ conj->incrementRefineCount();
+ addedLemma = true;
+ }else{
+ Trace("cegqi-warn") << " ...FAILED to add refinement!" << std::endl;
+ }
+ }
+ if( addedLemma ){
+ Trace("cegqi-engine") << " ...refine candidate." << std::endl;
+ }
+ }
+}
+
+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;
+ unsigned nlemmas = conj->getNumRefinementLemmas();
+ if (nlemmas > 0 || options::cegisSample() != CEGIS_SAMPLE_NONE)
+ {
+ Assert( vs.size()==ms.size() );
+
+ TermDbSygus* tds = d_quantEngine->getTermDatabaseSygus();
+ Node nfalse = d_quantEngine->getTermUtil()->d_false;
+ Node neg_guard = conj->getGuard().negate();
+ for (unsigned i = 0; i <= nlemmas; i++)
+ {
+ if (i == nlemmas)
+ {
+ bool addedSample = false;
+ // find a new one by sampling, if applicable
+ if (options::cegisSample() != CEGIS_SAMPLE_NONE)
+ {
+ addedSample = conj->sampleAddRefinementLemma(ms, lems);
+ }
+ if (!addedSample)
+ {
+ return;
+ }
+ }
+ Node lem;
+ std::map< Node, Node > visited;
+ std::map< Node, std::vector< Node > > exp;
+ lem = conj->getRefinementLemma(i);
+ if( !lem.isNull() ){
+ 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 );
+ }
+ EvalSygusInvarianceTest vsit;
+ 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 = vsit.doEvaluateWithUnfolding(tds, lemcs);
+ Trace("sygus-cref-eval2") << "...after unfolding is : " << lemcsu << std::endl;
+ if( lemcsu==d_quantEngine->getTermUtil()->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.setUpdatedTerm(msu[k]);
+ msu[k] = vs[k];
+ // substitute for everything except this
+ Node sconj =
+ lemc.substitute(vs.begin(), vs.end(), msu.begin(), msu.end());
+ vsit.init(sconj, vs[k], nfalse);
+ // get minimal explanation for this
+ Node ut = vsit.getUpdatedTerm();
+ Trace("sygus-cref-eval2-debug")
+ << " compute min explain of : " << vs[k] << " = " << ut
+ << std::endl;
+ d_quantEngine->getTermDatabaseSygus()
+ ->getExplain()
+ ->getExplanationFor(vs[k], ut, mexp, vsit);
+ msu[k] = ut;
+ }
+ 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;
+ }
+ }
+ 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 );
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void CegInstantiation::printSynthSolution( std::ostream& out ) {
+ if( d_conj->isAssigned() )
+ {
+ d_conj->printSynthSolution( out, d_last_inst_si );
+ }
+ else
+ {
+ Assert( false );
+ }
+}
+
+void CegInstantiation::getSynthSolutions(std::map<Node, Node>& sol_map)
+{
+ if (d_conj->isAssigned())
+ {
+ d_conj->getSynthSolutions(sol_map, d_last_inst_si);
+ }
+ else
+ {
+ Assert(false);
+ }
+}
+
+void CegInstantiation::preregisterAssertion( Node n ) {
+ //check if it sygus conjecture
+ if( QuantAttributes::checkSygusConjecture( n ) ){
+ //this is a sygus conjecture
+ Trace("cegqi") << "Preregister sygus conjecture : " << n << std::endl;
+ d_conj->preregisterConjecture( n );
+ }
+}
+
+CegInstantiation::Statistics::Statistics()
+ : d_cegqi_lemmas_ce("CegInstantiation::cegqi_lemmas_ce", 0),
+ d_cegqi_lemmas_refine("CegInstantiation::cegqi_lemmas_refine", 0),
+ d_cegqi_si_lemmas("CegInstantiation::cegqi_lemmas_si", 0),
+ d_solutions("CegConjecture::solutions", 0),
+ d_candidate_rewrites_print("CegConjecture::candidate_rewrites_print", 0),
+ d_candidate_rewrites("CegConjecture::candidate_rewrites", 0)
+
+{
+ smtStatisticsRegistry()->registerStat(&d_cegqi_lemmas_ce);
+ smtStatisticsRegistry()->registerStat(&d_cegqi_lemmas_refine);
+ smtStatisticsRegistry()->registerStat(&d_cegqi_si_lemmas);
+ smtStatisticsRegistry()->registerStat(&d_solutions);
+ smtStatisticsRegistry()->registerStat(&d_candidate_rewrites_print);
+ smtStatisticsRegistry()->registerStat(&d_candidate_rewrites);
+}
+
+CegInstantiation::Statistics::~Statistics(){
+ smtStatisticsRegistry()->unregisterStat(&d_cegqi_lemmas_ce);
+ smtStatisticsRegistry()->unregisterStat(&d_cegqi_lemmas_refine);
+ smtStatisticsRegistry()->unregisterStat(&d_cegqi_si_lemmas);
+ smtStatisticsRegistry()->unregisterStat(&d_solutions);
+ smtStatisticsRegistry()->unregisterStat(&d_candidate_rewrites_print);
+ smtStatisticsRegistry()->unregisterStat(&d_candidate_rewrites);
+}
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
diff --git a/src/theory/quantifiers/sygus/ce_guided_instantiation.h b/src/theory/quantifiers/sygus/ce_guided_instantiation.h
new file mode 100644
index 000000000..087836d5a
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_instantiation.h
@@ -0,0 +1,90 @@
+/********************* */
+/*! \file ce_guided_instantiation.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief counterexample guided instantiation class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_INSTANTIATION_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_INSTANTIATION_H
+
+#include "context/cdhashmap.h"
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegInstantiation : public QuantifiersModule
+{
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+private:
+ /** the quantified formula stating the synthesis conjecture */
+ CegConjecture * d_conj;
+ /** last instantiation by single invocation module? */
+ bool d_last_inst_si;
+private: //for direct evaluation
+ /** get refinement evaluation */
+ void getCRefEvaluationLemmas( CegConjecture * conj, std::vector< Node >& vs, std::vector< Node >& ms, std::vector< Node >& lems );
+private:
+ /** check conjecture */
+ void checkCegConjecture( CegConjecture * conj );
+public:
+ CegInstantiation( QuantifiersEngine * qe, context::Context* c );
+ ~CegInstantiation();
+public:
+ bool needsCheck( Theory::Effort e );
+ QEffort needsModel(Theory::Effort e);
+ /* Call during quantifier engine's check */
+ void check(Theory::Effort e, QEffort quant_e);
+ /* Called for new quantifiers */
+ void registerQuantifier( Node q );
+ /** get the next decision request */
+ Node getNextDecisionRequest( unsigned& priority );
+ /** Identify this module (for debugging, dynamic configuration, etc..) */
+ std::string identify() const { return "CegInstantiation"; }
+ /** print solution for synthesis conjectures */
+ void printSynthSolution( std::ostream& out );
+ /** get synth solutions
+ *
+ * This function adds entries to sol_map that map functions-to-synthesize
+ * with their solutions, for all active conjectures (currently just the one
+ * assigned to d_conj). This should be called immediately after the solver
+ * answers unsat for sygus input.
+ *
+ * For details on what is added to sol_map, see
+ * CegConjecture::getSynthSolutions.
+ */
+ void getSynthSolutions(std::map<Node, Node>& sol_map);
+ /** preregister assertion (before rewrite) */
+ void preregisterAssertion( Node n );
+public:
+ class Statistics {
+ public:
+ IntStat d_cegqi_lemmas_ce;
+ IntStat d_cegqi_lemmas_refine;
+ IntStat d_cegqi_si_lemmas;
+ IntStat d_solutions;
+ IntStat d_candidate_rewrites_print;
+ IntStat d_candidate_rewrites;
+ Statistics();
+ ~Statistics();
+ };/* class CegInstantiation::Statistics */
+ Statistics d_statistics;
+}; /* class CegInstantiation */
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
diff --git a/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp b/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp
new file mode 100644
index 000000000..d59f1f370
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_single_inv.cpp
@@ -0,0 +1,1004 @@
+/********************* */
+/*! \file ce_guided_single_inv.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing single invocation synthesis conjectures
+ **
+ **/
+#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
+
+#include "options/quantifiers_options.h"
+#include "theory/arith/arith_msum.h"
+#include "theory/quantifiers/term_enumeration.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+using namespace std;
+
+namespace CVC4 {
+
+bool CegqiOutputSingleInv::doAddInstantiation( std::vector< Node >& subs ) {
+ return d_out->doAddInstantiation( subs );
+}
+
+bool CegqiOutputSingleInv::isEligibleForInstantiation( Node n ) {
+ return d_out->isEligibleForInstantiation( n );
+}
+
+bool CegqiOutputSingleInv::addLemma( Node n ) {
+ return d_out->addLemma( n );
+}
+
+CegConjectureSingleInv::CegConjectureSingleInv(QuantifiersEngine* qe,
+ CegConjecture* p)
+ : d_qe(qe),
+ d_parent(p),
+ d_sip(new SingleInvocationPartition),
+ d_sol(new CegConjectureSingleInvSol(qe)),
+ d_cosi(new CegqiOutputSingleInv(this)),
+ d_cinst(NULL),
+ d_c_inst_match_trie(NULL),
+ d_has_ites(true),
+ d_single_invocation(false) {
+ // third and fourth arguments set to (false,false) until we have solution
+ // reconstruction for delta and infinity
+ d_cinst = new CegInstantiator(d_qe, d_cosi, false, false);
+
+ if (options::incrementalSolving()) {
+ d_c_inst_match_trie = new inst::CDInstMatchTrie(qe->getUserContext());
+ }
+}
+
+CegConjectureSingleInv::~CegConjectureSingleInv() {
+ if (d_c_inst_match_trie) {
+ delete d_c_inst_match_trie;
+ }
+ delete d_cinst;
+ delete d_cosi;
+ delete d_sol; // (new CegConjectureSingleInvSol(qe)),
+ delete d_sip; // d_sip(new SingleInvocationPartition),
+}
+
+void CegConjectureSingleInv::getInitialSingleInvLemma( std::vector< Node >& lems ) {
+ Assert( d_si_guard.isNull() );
+ //single invocation guard
+ d_si_guard = Rewriter::rewrite( NodeManager::currentNM()->mkSkolem( "G", NodeManager::currentNM()->booleanType() ) );
+ d_si_guard = d_qe->getValuation().ensureLiteral( d_si_guard );
+ AlwaysAssert( !d_si_guard.isNull() );
+ d_qe->getOutputChannel().requirePhase( d_si_guard, true );
+
+ if( !d_single_inv.isNull() ) {
+ //make for new var/sk
+ d_single_inv_var.clear();
+ d_single_inv_sk.clear();
+ Node inst;
+ if( d_single_inv.getKind()==FORALL ){
+ for( unsigned i=0; i<d_single_inv[0].getNumChildren(); i++ ){
+ std::stringstream ss;
+ ss << "k_" << d_single_inv[0][i];
+ Node k = NodeManager::currentNM()->mkSkolem( ss.str(), d_single_inv[0][i].getType(), "single invocation function skolem" );
+ d_single_inv_var.push_back( d_single_inv[0][i] );
+ d_single_inv_sk.push_back( k );
+ d_single_inv_sk_index[k] = i;
+ }
+ inst = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), d_single_inv_sk.begin(), d_single_inv_sk.end() );
+ }else{
+ inst = d_single_inv;
+ }
+ inst = TermUtil::simpleNegate( inst );
+ Trace("cegqi-si") << "Single invocation initial lemma : " << inst << std::endl;
+
+ //register with the instantiator
+ Node ginst = NodeManager::currentNM()->mkNode( OR, d_si_guard.negate(), inst );
+ lems.push_back( ginst );
+ //make and register the instantiator
+ if( d_cinst ){
+ delete d_cinst;
+ }
+ d_cinst = new CegInstantiator( d_qe, d_cosi, false, false );
+ d_cinst->registerCounterexampleLemma( lems, d_single_inv_sk );
+ }
+}
+
+void CegConjectureSingleInv::initialize( Node q ) {
+ // can only register one quantified formula with this
+ Assert( d_quant.isNull() );
+ d_quant = q;
+ d_simp_quant = q;
+ Trace("cegqi-si") << "CegConjectureSingleInv::initialize : " << q << std::endl;
+ // infer single invocation-ness
+ std::vector< Node > progs;
+ std::map< Node, std::vector< Node > > prog_vars;
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node sf = q[0][i];
+ progs.push_back( sf );
+ Node sfvl = sf.getAttribute(SygusSynthFunVarListAttribute());
+ for( unsigned j=0; j<sfvl.getNumChildren(); j++ ){
+ prog_vars[sf].push_back( sfvl[j] );
+ }
+ }
+ // compute single invocation partition
+ if( options::cegqiSingleInvMode()!=CEGQI_SI_MODE_NONE ){
+ Node qq;
+ if( q[1].getKind()==NOT && q[1][0].getKind()==FORALL ){
+ qq = q[1][0][1];
+ }else{
+ qq = TermUtil::simpleNegate( q[1] );
+ }
+ //process the single invocation-ness of the property
+ if( !d_sip->init( progs, qq ) ){
+ Trace("cegqi-si") << "...not single invocation (type mismatch)" << std::endl;
+ }else{
+ Trace("cegqi-si") << "- Partitioned to single invocation parts : " << std::endl;
+ d_sip->debugPrint( "cegqi-si" );
+
+ //map from program to bound variables
+ std::vector<Node> funcs;
+ d_sip->getFunctions(funcs);
+ for (unsigned j = 0, size = funcs.size(); j < size; j++)
+ {
+ Assert(std::find(progs.begin(), progs.end(), funcs[j]) != progs.end());
+ d_prog_to_sol_index[funcs[j]] = j;
+ }
+
+ //check if it is single invocation
+ if (!d_sip->isPurelySingleInvocation())
+ {
+ if( options::sygusInvTemplMode() != SYGUS_INV_TEMPL_MODE_NONE ){
+ //if we are doing invariant templates, then construct the template
+ Trace("cegqi-si") << "- Do transition inference..." << std::endl;
+ d_ti[q].process( qq );
+ Trace("cegqi-inv") << std::endl;
+ if( !d_ti[q].d_func.isNull() ){
+ // map the program back via non-single invocation map
+ Node prog = d_ti[q].d_func;
+ std::vector< Node > prog_templ_vars;
+ prog_templ_vars.insert( prog_templ_vars.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;
+ std::vector<Node> sivars;
+ d_sip->getSingleInvocationVariables(sivars);
+ Node invariant = d_sip->getFunctionInvocationFor(prog);
+ Assert(!invariant.isNull());
+ invariant = invariant.substitute(sivars.begin(),
+ sivars.end(),
+ prog_templ_vars.begin(),
+ prog_templ_vars.end());
+ Trace("cegqi-inv") << " invariant : " << invariant << std::endl;
+
+ // store simplified version of quantified formula
+ d_simp_quant = d_sip->getFullSpecification();
+ std::vector< Node > new_bv;
+ for (unsigned j = 0, size = sivars.size(); j < size; j++)
+ {
+ new_bv.push_back(
+ NodeManager::currentNM()->mkBoundVar(sivars[j].getType()));
+ }
+ d_simp_quant = d_simp_quant.substitute(
+ sivars.begin(), sivars.end(), new_bv.begin(), new_bv.end());
+ Assert( q[1].getKind()==NOT && q[1][0].getKind()==FORALL );
+ for( unsigned j=0; j<q[1][0][0].getNumChildren(); j++ ){
+ new_bv.push_back( q[1][0][0][j] );
+ }
+ d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, new_bv ), d_simp_quant ).negate();
+ d_simp_quant = Rewriter::rewrite( d_simp_quant );
+ d_simp_quant = NodeManager::currentNM()->mkNode( kind::FORALL, q[0], d_simp_quant, q[2] );
+ Trace("cegqi-si") << "Rewritten quantifier : " << d_simp_quant << std::endl;
+
+ //construct template argument
+ d_templ_arg[prog] = NodeManager::currentNM()->mkSkolem( "I", invariant.getType() );
+
+ //construct template
+ Node templ;
+ 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
+ templ = d_ti[q].constructFormulaTrace( dt );
+ Trace("cegqi-inv") << "By finite deterministic terminating trace, a solution invariant is : " << std::endl;
+ Trace("cegqi-inv") << " " << templ << std::endl;
+ // FIXME : this should be unnecessary
+ templ = NodeManager::currentNM()->mkNode( AND, templ, d_templ_arg[prog] );
+ }
+ }else{
+ Trace("cegqi-inv-auto-unfold") << "...failed initialize." << std::endl;
+ }
+ }
+ }
+ if( templ.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] );
+ templ = 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 ) );
+ templ = NodeManager::currentNM()->mkNode( AND, d_trans_post[prog], d_templ_arg[prog] );
+ }
+ }
+ Trace("cegqi-inv") << " template (pre-substitution) : " << templ << std::endl;
+ Assert( !templ.isNull() );
+ // subsitute the template arguments
+ templ = templ.substitute( prog_templ_vars.begin(), prog_templ_vars.end(), prog_vars[prog].begin(), prog_vars[prog].end() );
+ Trace("cegqi-inv") << " template : " << templ << std::endl;
+ d_templ[prog] = templ;
+ }
+ }
+ }else{
+ //we are fully single invocation
+ d_single_invocation = true;
+ }
+ }
+ }
+}
+
+void CegConjectureSingleInv::finishInit( bool syntaxRestricted, bool hasItes ) {
+ d_has_ites = hasItes;
+ // do not do single invocation if grammar is restricted and CEGQI_SI_MODE_ALL is not enabled
+ if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_USE && d_single_invocation && syntaxRestricted ){
+ d_single_invocation = false;
+ Trace("cegqi-si") << "...grammar is restricted, do not use single invocation techniques." << std::endl;
+ }
+
+ // we now have determined whether we will do single invocation techniques
+ if( d_single_invocation ){
+ d_single_inv = d_sip->getSingleInvocation();
+ d_single_inv = TermUtil::simpleNegate( d_single_inv );
+ std::vector<Node> func_vars;
+ d_sip->getFunctionVariables(func_vars);
+ if (!func_vars.empty())
+ {
+ Node pbvl = NodeManager::currentNM()->mkNode(BOUND_VAR_LIST, func_vars);
+ d_single_inv = NodeManager::currentNM()->mkNode( FORALL, pbvl, d_single_inv );
+ }
+ //now, introduce the skolems
+ std::vector<Node> sivars;
+ d_sip->getSingleInvocationVariables(sivars);
+ for (unsigned i = 0, size = sivars.size(); i < size; i++)
+ {
+ Node v = NodeManager::currentNM()->mkSkolem(
+ "a", sivars[i].getType(), "single invocation arg");
+ d_single_inv_arg_sk.push_back( v );
+ }
+ d_single_inv = d_single_inv.substitute(sivars.begin(),
+ sivars.end(),
+ d_single_inv_arg_sk.begin(),
+ d_single_inv_arg_sk.end());
+ Trace("cegqi-si") << "Single invocation formula is : " << d_single_inv << std::endl;
+ if( options::cbqiPreRegInst() && d_single_inv.getKind()==FORALL ){
+ //just invoke the presolve now
+ d_cinst->presolve( d_single_inv );
+ }
+ }else{
+ d_single_inv = Node::null();
+ Trace("cegqi-si") << "Formula is not single invocation." << std::endl;
+ if( options::cegqiSingleInvAbort() ){
+ Notice() << "Property is not single invocation." << std::endl;
+ exit( 1 );
+ }
+ }
+}
+
+bool CegConjectureSingleInv::doAddInstantiation( std::vector< Node >& subs ){
+ Assert( d_single_inv_sk.size()==subs.size() );
+ Trace("cegqi-si-inst-debug") << "CegConjectureSingleInv::doAddInstantiation, #vars = ";
+ Trace("cegqi-si-inst-debug") << d_single_inv_sk.size() << "..." << std::endl;
+ std::stringstream siss;
+ if( Trace.isOn("cegqi-si-inst-debug") || Trace.isOn("cegqi-engine") ){
+ siss << " * single invocation: " << std::endl;
+ for( unsigned j=0; j<d_single_inv_sk.size(); j++ ){
+ Node op = d_sip->getFunctionForFirstOrderVariable(d_single_inv[0][j]);
+ Assert(!op.isNull());
+ siss << " * " << op;
+ siss << " (" << d_single_inv_sk[j] << ")";
+ siss << " -> " << subs[j] << std::endl;
+ }
+ }
+ Trace("cegqi-si-inst-debug") << siss.str();
+
+ bool alreadyExists;
+ Node lem;
+ if( subs.empty() ){
+ Assert( d_single_inv.getKind()!=FORALL );
+ alreadyExists = false;
+ lem = d_single_inv;
+ }else{
+ Assert( d_single_inv.getKind()==FORALL );
+ if( options::incrementalSolving() ){
+ alreadyExists = !d_c_inst_match_trie->addInstMatch( d_qe, d_single_inv, subs, d_qe->getUserContext() );
+ }else{
+ alreadyExists = !d_inst_match_trie.addInstMatch( d_qe, d_single_inv, subs );
+ }
+ Trace("cegqi-si-inst-debug") << " * success = " << !alreadyExists << std::endl;
+ //Trace("cegqi-si-inst-debug") << siss.str();
+ //Trace("cegqi-si-inst-debug") << " * success = " << !alreadyExists << std::endl;
+ if( alreadyExists ){
+ return false;
+ }else{
+ Trace("cegqi-engine") << siss.str() << std::endl;
+ Assert( d_single_inv_var.size()==subs.size() );
+ lem = d_single_inv[1].substitute( d_single_inv_var.begin(), d_single_inv_var.end(), subs.begin(), subs.end() );
+ if( d_qe->getTermUtil()->containsVtsTerm( lem ) ){
+ Trace("cegqi-engine-debug") << "Rewrite based on vts symbols..." << std::endl;
+ lem = d_qe->getTermUtil()->rewriteVtsSymbols( lem );
+ }
+ }
+ }
+ Trace("cegqi-engine-debug") << "Rewrite..." << std::endl;
+ lem = Rewriter::rewrite( lem );
+ Trace("cegqi-si") << "Single invocation lemma : " << lem << std::endl;
+ if( std::find( d_lemmas_produced.begin(), d_lemmas_produced.end(), lem )==d_lemmas_produced.end() ){
+ d_curr_lemmas.push_back( lem );
+ d_lemmas_produced.push_back( lem );
+ d_inst.push_back( std::vector< Node >() );
+ d_inst.back().insert( d_inst.back().end(), subs.begin(), subs.end() );
+ }
+ return true;
+}
+
+bool CegConjectureSingleInv::isEligibleForInstantiation( Node n ) {
+ return n.getKind()!=SKOLEM || std::find( d_single_inv_arg_sk.begin(), d_single_inv_arg_sk.end(), n )!=d_single_inv_arg_sk.end();
+}
+
+bool CegConjectureSingleInv::addLemma( Node n ) {
+ d_curr_lemmas.push_back( n );
+ return true;
+}
+
+bool CegConjectureSingleInv::check( std::vector< Node >& lems ) {
+ if( !d_single_inv.isNull() ) {
+ Trace("cegqi-si-debug") << "CegConjectureSingleInv::check..." << std::endl;
+ 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();
+ Trace("cegqi-si-debug") << "...returned " << d_curr_lemmas.size() << " lemmas " << std::endl;
+ //add lemmas
+ lems.insert( lems.end(), d_curr_lemmas.begin(), d_curr_lemmas.end() );
+ return !lems.empty();
+ }else{
+ // not single invocation
+ return false;
+ }
+}
+
+Node CegConjectureSingleInv::constructSolution( std::vector< unsigned >& indices, unsigned i, unsigned index, std::map< Node, Node >& weak_imp ) {
+ Assert( index<d_inst.size() );
+ Assert( i<d_inst[index].size() );
+ unsigned uindex = indices[index];
+ if( index==indices.size()-1 ){
+ return d_inst[uindex][i];
+ }else{
+ Node cond = d_lemmas_produced[uindex];
+ //weaken based on unsat core
+ std::map< Node, Node >::iterator itw = weak_imp.find( cond );
+ if( itw!=weak_imp.end() ){
+ cond = itw->second;
+ }
+ cond = TermUtil::simpleNegate( cond );
+ Node ite1 = d_inst[uindex][i];
+ Node ite2 = constructSolution( indices, i, index+1, weak_imp );
+ return NodeManager::currentNM()->mkNode( ITE, cond, ite1, ite2 );
+ }
+}
+
+//TODO: use term size?
+struct sortSiInstanceIndices {
+ CegConjectureSingleInv* d_ccsi;
+ int d_i;
+ bool operator() (unsigned i, unsigned j) {
+ if( d_ccsi->d_inst[i][d_i].isConst() && !d_ccsi->d_inst[j][d_i].isConst() ){
+ return true;
+ }else{
+ return false;
+ }
+ }
+};
+
+
+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] );
+ // Node n2 = postProcessSolution( n[2] );
+ // 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() );
+ }
+ return NodeManager::currentNM()->mkNode( k, children );
+ }else{
+ return n;
+ }
+ //}
+}
+
+
+Node CegConjectureSingleInv::getSolution( unsigned sol_index, TypeNode stn, int& reconstructed, bool rconsSygus ){
+ Assert( d_sol!=NULL );
+ Assert( !d_lemmas_produced.empty() );
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ Node varList = Node::fromExpr( dt.getSygusVarList() );
+ Node prog = d_quant[0][sol_index];
+ std::vector< Node > vars;
+ Node s;
+ if( d_prog_to_sol_index.find( prog )==d_prog_to_sol_index.end() ){
+ Trace("csi-sol") << "Get solution for (unconstrained) " << prog << std::endl;
+ s = d_qe->getTermEnumeration()->getEnumerateTerm(
+ TypeNode::fromType(dt.getSygusType()), 0);
+ }else{
+ Trace("csi-sol") << "Get solution for " << prog << ", with skolems : ";
+ sol_index = d_prog_to_sol_index[prog];
+ d_sol->d_varList.clear();
+ Assert( d_single_inv_arg_sk.size()==varList.getNumChildren() );
+ for( unsigned i=0; i<d_single_inv_arg_sk.size(); i++ ){
+ Trace("csi-sol") << d_single_inv_arg_sk[i] << " ";
+ vars.push_back( d_single_inv_arg_sk[i] );
+ d_sol->d_varList.push_back( varList[i] );
+ }
+ Trace("csi-sol") << std::endl;
+
+ //construct the solution
+ Trace("csi-sol") << "Sort solution return values " << sol_index << std::endl;
+ bool useUnsatCore = false;
+ std::vector< Node > active_lemmas;
+ //minimize based on unsat core, if possible
+ std::map< Node, Node > weak_imp;
+ if( options::cegqiSolMinCore() ){
+ if( options::cegqiSolMinInst() ){
+ if( d_qe->getUnsatCoreLemmas( active_lemmas, weak_imp ) ){
+ useUnsatCore = true;
+ }
+ }else{
+ if( d_qe->getUnsatCoreLemmas( active_lemmas ) ){
+ useUnsatCore = true;
+ }
+ }
+ }
+ Assert( d_lemmas_produced.size()==d_inst.size() );
+ std::vector< unsigned > indices;
+ for( unsigned i=0; i<d_lemmas_produced.size(); i++ ){
+ bool incl = true;
+ if( useUnsatCore ){
+ incl = std::find( active_lemmas.begin(), active_lemmas.end(), d_lemmas_produced[i] )!=active_lemmas.end();
+ }
+ if( incl ){
+ Assert( sol_index<d_inst[i].size() );
+ indices.push_back( i );
+ }
+ }
+ Trace("csi-sol") << "...included " << indices.size() << " / " << d_lemmas_produced.size() << " instantiations." << std::endl;
+ Assert( !indices.empty() );
+ //sort indices based on heuristic : currently, do all constant returns first (leads to simpler conditions)
+ // TODO : to minimize solution size, put the largest term last
+ sortSiInstanceIndices ssii;
+ ssii.d_ccsi = this;
+ ssii.d_i = sol_index;
+ std::sort( indices.begin(), indices.end(), ssii );
+ Trace("csi-sol") << "Construct solution" << std::endl;
+ s = constructSolution( indices, sol_index, 0, weak_imp );
+ Assert( vars.size()==d_sol->d_varList.size() );
+ s = s.substitute( vars.begin(), vars.end(), d_sol->d_varList.begin(), d_sol->d_varList.end() );
+ }
+ d_orig_solution = s;
+
+ //simplify the solution
+ Trace("csi-sol") << "Solution (pre-simplification): " << d_orig_solution << std::endl;
+ s = d_sol->simplifySolution( s, stn );
+ Trace("csi-sol") << "Solution (post-simplification): " << s << std::endl;
+ return reconstructToSyntax( s, stn, reconstructed, rconsSygus );
+}
+
+Node CegConjectureSingleInv::reconstructToSyntax( Node s, TypeNode stn, int& reconstructed, bool rconsSygus ) {
+ d_solution = s;
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+
+ //reconstruct the solution into sygus if necessary
+ reconstructed = 0;
+ if( options::cegqiSingleInvReconstruct() && !dt.getSygusAllowAll() && !stn.isNull() && rconsSygus ){
+ d_sol->preregisterConjecture( d_orig_conjecture );
+ d_sygus_solution = d_sol->reconstructSolution( s, stn, reconstructed );
+ if( reconstructed==1 ){
+ Trace("csi-sol") << "Solution (post-reconstruction into Sygus): " << d_sygus_solution << std::endl;
+ }
+ }else{
+ Trace("csi-sol") << "Post-process solution..." << std::endl;
+ Node prev = d_solution;
+ d_solution = postProcessSolution( d_solution );
+ if( prev!=d_solution ){
+ Trace("csi-sol") << "Solution (after post process) : " << d_solution << std::endl;
+ }
+ }
+
+
+ if( Trace.isOn("csi-sol") ){
+ //debug solution
+ if( !d_sol->debugSolution( d_solution ) ){
+ Trace("csi-sol") << "WARNING : solution " << d_solution << " contains free constants." << std::endl;
+ //exit( 47 );
+ }else{
+ //exit( 49 );
+ }
+ }
+ if( Trace.isOn("cegqi-stats") ){
+ int tsize, itesize;
+ tsize = 0;itesize = 0;
+ d_sol->debugTermSize( d_orig_solution, tsize, itesize );
+ Trace("cegqi-stats") << tsize << " " << itesize << " ";
+ tsize = 0;itesize = 0;
+ d_sol->debugTermSize( d_solution, tsize, itesize );
+ Trace("cegqi-stats") << tsize << " " << itesize << " ";
+ if( !d_sygus_solution.isNull() ){
+ tsize = 0;itesize = 0;
+ d_sol->debugTermSize( d_sygus_solution, tsize, itesize );
+ Trace("cegqi-stats") << tsize << " - ";
+ }else{
+ Trace("cegqi-stats") << "null ";
+ }
+ Trace("cegqi-stats") << std::endl;
+ }
+ Node sol;
+ if( reconstructed==1 ){
+ sol = d_sygus_solution;
+ }else if( reconstructed==-1 ){
+ return Node::null();
+ }else{
+ sol = d_solution;
+ }
+ //make into lambda
+ if( !dt.getSygusVarList().isNull() ){
+ Node varList = Node::fromExpr( dt.getSygusVarList() );
+ return NodeManager::currentNM()->mkNode( LAMBDA, varList, sol );
+ }else{
+ return sol;
+ }
+}
+
+bool CegConjectureSingleInv::needsCheck() {
+ if( options::cegqiSingleInvMode()==CEGQI_SI_MODE_ALL_ABORT ){
+ if( !d_has_ites ){
+ return d_inst.empty();
+ }
+ }
+ return true;
+}
+
+void CegConjectureSingleInv::preregisterConjecture( Node q ) {
+ d_orig_conjecture = q;
+}
+
+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( !TermUtil::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 (ArithMSum::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 =
+ ArithMSum::isolate(itm->first, msum, veq_c, val, EQUAL);
+ if( ires!=0 && veq_c.isNull() && !TermUtil::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 = TermUtil::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/sygus/ce_guided_single_inv.h b/src/theory/quantifiers/sygus/ce_guided_single_inv.h
new file mode 100644
index 000000000..abdbef708
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_single_inv.h
@@ -0,0 +1,248 @@
+/********************* */
+/*! \file ce_guided_single_inv.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing single invocation synthesis conjectures
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_H
+
+#include "context/cdlist.h"
+#include "theory/quantifiers/sygus/ce_guided_single_inv_sol.h"
+#include "theory/quantifiers/inst_match_trie.h"
+#include "theory/quantifiers/cegqi/inst_strategy_cbqi.h"
+#include "theory/quantifiers/single_inv_partition.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegConjecture;
+class CegConjectureSingleInv;
+class CegEntailmentInfer;
+
+class CegqiOutputSingleInv : public CegqiOutput {
+public:
+ CegqiOutputSingleInv( CegConjectureSingleInv * out ) : d_out( out ){}
+ virtual ~CegqiOutputSingleInv() {}
+ CegConjectureSingleInv * d_out;
+ bool doAddInstantiation( std::vector< Node >& subs );
+ bool isEligibleForInstantiation( Node n );
+ bool addLemma( Node lem );
+};
+
+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 );
+};
+
+// this class infers whether a conjecture is single invocation (Reynolds et al CAV 2015), and sets up the
+// counterexample-guided quantifier instantiation utility (d_cinst), and methods for solution
+// reconstruction (d_sol).
+// It also has more advanced techniques for:
+// (1) partitioning a conjecture into single invocation / non-single invocation portions for invariant synthesis,
+// (2) inferring whether the conjecture corresponds to a deterministic transistion system (by utility d_ti).
+// For these techniques, we may generate a template (d_templ) which specifies a restricted
+// solution space. We may in turn embed this template as a SyGuS grammar.
+class CegConjectureSingleInv {
+ private:
+ friend class CegqiOutputSingleInv;
+ //presolve
+ void collectPresolveEqTerms( Node n,
+ std::map< Node, std::vector< Node > >& teq );
+ void getPresolveEqConjuncts( std::vector< Node >& vars,
+ std::vector< Node >& terms,
+ std::map< Node, std::vector< Node > >& teq,
+ Node n, std::vector< Node >& conj );
+ // constructing solution
+ Node constructSolution(std::vector<unsigned>& indices, unsigned i,
+ unsigned index, std::map<Node, Node>& weak_imp);
+ Node postProcessSolution(Node n);
+ private:
+ QuantifiersEngine* d_qe;
+ CegConjecture* d_parent;
+ // single invocation inference utility
+ SingleInvocationPartition* d_sip;
+ // transition inference module for each function to synthesize
+ std::map< Node, TransitionInference > d_ti;
+ // solution reconstruction
+ CegConjectureSingleInvSol* d_sol;
+ // the instantiator's output channel
+ CegqiOutputSingleInv* d_cosi;
+ // the instantiator
+ CegInstantiator* d_cinst;
+
+ // list of skolems for each argument of programs
+ std::vector<Node> d_single_inv_arg_sk;
+ // list of variables/skolems for each program
+ std::vector<Node> d_single_inv_var;
+ std::vector<Node> d_single_inv_sk;
+ std::map<Node, int> d_single_inv_sk_index;
+ // program to solution index
+ std::map<Node, unsigned> d_prog_to_sol_index;
+ // lemmas produced
+ inst::InstMatchTrie d_inst_match_trie;
+ inst::CDInstMatchTrie* d_c_inst_match_trie;
+ // original conjecture
+ Node d_orig_conjecture;
+ // solution
+ Node d_orig_solution;
+ Node d_solution;
+ Node d_sygus_solution;
+ // whether the grammar for our solution allows ITEs, this tells us when reconstruction is infeasible
+ bool d_has_ites;
+
+ public:
+ // lemmas produced
+ std::vector<Node> d_lemmas_produced;
+ std::vector<std::vector<Node> > d_inst;
+
+ private:
+ std::vector<Node> d_curr_lemmas;
+ // add instantiation
+ bool doAddInstantiation( std::vector< Node >& subs );
+ //is eligible for instantiation
+ bool isEligibleForInstantiation( Node n );
+ // add lemma
+ bool addLemma( Node lem );
+ // conjecture
+ Node d_quant;
+ Node d_simp_quant;
+ // are we single invocation?
+ bool d_single_invocation;
+ // single invocation portion of quantified formula
+ Node d_single_inv;
+ Node d_si_guard;
+ // transition relation version per program
+ std::map< Node, Node > d_trans_pre;
+ std::map< Node, Node > d_trans_post;
+ // the template for each function to synthesize
+ std::map< Node, Node > d_templ;
+ // the template argument for each function to synthesize (occurs in exactly one position of its template)
+ std::map< Node, Node > d_templ_arg;
+
+ public:
+ CegConjectureSingleInv( QuantifiersEngine * qe, CegConjecture * p );
+ ~CegConjectureSingleInv();
+
+ // get simplified conjecture
+ Node getSimplifiedConjecture() { return d_simp_quant; }
+ // get single invocation guard
+ Node getGuard() { return d_si_guard; }
+ public:
+ //get the single invocation lemma(s)
+ void getInitialSingleInvLemma( std::vector< Node >& lems );
+ // initialize this class for synthesis conjecture q
+ void initialize( Node q );
+ // finish initialize, sets up final decisions about whether to use single invocation techniques
+ // syntaxRestricted is whether the syntax for solutions for the initialized conjecture is restricted
+ // hasItes is whether the syntax for solutions for the initialized conjecture allows ITEs
+ void finishInit( bool syntaxRestricted, bool hasItes );
+ //check
+ bool check( std::vector< Node >& lems );
+ //get solution
+ Node getSolution( unsigned sol_index, TypeNode stn, int& reconstructed, bool rconsSygus = true );
+ //reconstruct to syntax
+ Node reconstructToSyntax( Node s, TypeNode stn, int& reconstructed,
+ bool rconsSygus = true );
+ // has ites
+ bool hasITEs() { return d_has_ites; }
+ // is single invocation
+ bool isSingleInvocation() const { return !d_single_inv.isNull(); }
+ //needs check
+ bool needsCheck();
+ /** preregister conjecture */
+ void preregisterConjecture( Node q );
+
+ Node getTransPre(Node prog) const {
+ std::map<Node, Node>::const_iterator location = d_trans_pre.find(prog);
+ return location->second;
+ }
+
+ Node getTransPost(Node prog) const {
+ std::map<Node, Node>::const_iterator location = d_trans_post.find(prog);
+ return location->second;
+ }
+ // get template for program prog. This returns a term of the form t[x] where x is the template argument (see below)
+ 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();
+ }
+ }
+ // get the template argument for program prog.
+ // This is a variable which indicates the position of the function/predicate to synthesize.
+ 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();
+ }
+ }
+};
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
+
+#endif
diff --git a/src/theory/quantifiers/sygus/ce_guided_single_inv_sol.cpp b/src/theory/quantifiers/sygus/ce_guided_single_inv_sol.cpp
new file mode 100644
index 000000000..f900297e5
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_single_inv_sol.cpp
@@ -0,0 +1,1512 @@
+/********************* */
+/*! \file ce_guided_single_inv_sol.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Paul Meng, Tim King
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for processing single invocation synthesis conjectures
+ **
+ **/
+#include "theory/quantifiers/sygus/ce_guided_single_inv_sol.h"
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/ce_guided_instantiation.h"
+#include "theory/quantifiers/sygus/ce_guided_single_inv.h"
+#include "theory/quantifiers/first_order_model.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_enumeration.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/quantifiers/ematching/trigger.h"
+#include "theory/theory_engine.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+bool doCompare(Node a, Node b, Kind k)
+{
+ Node com = NodeManager::currentNM()->mkNode(k, a, b);
+ com = Rewriter::rewrite(com);
+ Assert(com.getType().isBoolean());
+ return com.isConst() && com.getConst<bool>();
+}
+
+CegConjectureSingleInvSol::CegConjectureSingleInvSol(QuantifiersEngine* qe)
+ : d_qe(qe), d_id_count(0), d_root_id() {}
+
+bool CegConjectureSingleInvSol::debugSolution( Node sol ) {
+ if( sol.getKind()==SKOLEM ){
+ return false;
+ }else{
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ if( !debugSolution( sol[i] ) ){
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+
+void CegConjectureSingleInvSol::debugTermSize( Node sol, int& t_size, int& num_ite ) {
+ std::map< Node, int >::iterator it = d_dterm_size.find( sol );
+ if( it==d_dterm_size.end() ){
+ int prev = t_size;
+ int prev_ite = num_ite;
+ t_size++;
+ if( sol.getKind()==ITE ){
+ num_ite++;
+ }
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ debugTermSize( sol[i], t_size, num_ite );
+ }
+ d_dterm_size[sol] = t_size-prev;
+ d_dterm_ite_size[sol] = num_ite-prev_ite;
+ }else{
+ t_size += it->second;
+ num_ite += d_dterm_ite_size[sol];
+ }
+}
+
+
+Node CegConjectureSingleInvSol::pullITEs( Node s ) {
+ if( s.getKind()==ITE ){
+ bool success;
+ do {
+ success = false;
+ std::vector< Node > conj;
+ Node t;
+ Node rem;
+ if( pullITECondition( s, s, conj, t, rem, 0 ) ){
+ Assert( !conj.empty() );
+ Node cond = conj.size()==1 ? conj[0] : NodeManager::currentNM()->mkNode( AND, conj );
+ Trace("csi-sol-debug") << "For " << s << ", can pull " << cond << " -> " << t << " with remainder " << rem << std::endl;
+ t = pullITEs( t );
+ rem = pullITEs( rem );
+ Trace("csi-pull-ite") << "PI: Rewrite : " << s << std::endl;
+ Node prev = s;
+ s = NodeManager::currentNM()->mkNode( ITE, TermUtil::simpleNegate( cond ), t, rem );
+ Trace("csi-pull-ite") << "PI: Rewrite Now : " << s << std::endl;
+ Trace("csi-pull-ite") << "(= " << prev << " " << s << ")" << std::endl;
+ success = true;
+ }
+ }while( success );
+ }
+ return s;
+}
+
+// pull condition common to all ITE conditions in path of size > 1
+bool CegConjectureSingleInvSol::pullITECondition( Node root, Node n_ite, std::vector< Node >& conj, Node& t, Node& rem, int depth ) {
+ Assert( n_ite.getKind()==ITE );
+ std::vector< Node > curr_conj;
+ std::vector< Node > orig_conj;
+ bool isAnd;
+ if( n_ite[0].getKind()==AND || n_ite[0].getKind()==OR ){
+ isAnd = n_ite[0].getKind()==AND;
+ for( unsigned i=0; i<n_ite[0].getNumChildren(); i++ ){
+ Node cond = n_ite[0][i];
+ orig_conj.push_back( cond );
+ if( n_ite[0].getKind()==OR ){
+ cond = TermUtil::simpleNegate( cond );
+ }
+ curr_conj.push_back( cond );
+ }
+ }else{
+ Node neg = n_ite[0].negate();
+ if( std::find( conj.begin(), conj.end(), neg )!=conj.end() ){
+ //if negation of condition exists, use it
+ isAnd = false;
+ curr_conj.push_back( neg );
+ }else{
+ //otherwise, use condition
+ isAnd = true;
+ curr_conj.push_back( n_ite[0] );
+ }
+ orig_conj.push_back( n_ite[0] );
+ }
+ // take intersection with current conditions
+ std::vector< Node > new_conj;
+ std::vector< Node > prev_conj;
+ if( n_ite==root ){
+ new_conj.insert( new_conj.end(), curr_conj.begin(), curr_conj.end() );
+ Trace("csi-sol-debug") << "Pull ITE root " << n_ite << ", #conj = " << new_conj.size() << std::endl;
+ }else{
+ for( unsigned i=0; i<curr_conj.size(); i++ ){
+ if( std::find( conj.begin(), conj.end(), curr_conj[i] )!=conj.end() ){
+ new_conj.push_back( curr_conj[i] );
+ }
+ }
+ Trace("csi-sol-debug") << "Pull ITE " << n_ite << ", #conj = " << conj.size() << " intersect " << curr_conj.size() << " = " << new_conj.size() << std::endl;
+ }
+ //cannot go further
+ if( new_conj.empty() ){
+ return false;
+ }
+ //it is an intersection with current
+ conj.clear();
+ conj.insert( conj.end(), new_conj.begin(), new_conj.end() );
+ //recurse if possible
+ Node trec = n_ite[ isAnd ? 2 : 1 ];
+ Node tval = n_ite[ isAnd ? 1 : 2 ];
+ bool success = false;
+ if( trec.getKind()==ITE ){
+ prev_conj.insert( prev_conj.end(), conj.begin(), conj.end() );
+ success = pullITECondition( root, trec, conj, t, rem, depth+1 );
+ }
+ if( !success && depth>0 ){
+ t = trec;
+ rem = trec;
+ success = true;
+ if( trec.getKind()==ITE ){
+ //restore previous state
+ conj.clear();
+ conj.insert( conj.end(), prev_conj.begin(), prev_conj.end() );
+ }
+ }
+ if( success ){
+ //make remainder : strip out conditions in conj
+ Assert( !conj.empty() );
+ std::vector< Node > cond_c;
+ Assert( orig_conj.size()==curr_conj.size() );
+ for( unsigned i=0; i<curr_conj.size(); i++ ){
+ if( std::find( conj.begin(), conj.end(), curr_conj[i] )==conj.end() ){
+ cond_c.push_back( orig_conj[i] );
+ }
+ }
+ if( cond_c.empty() ){
+ rem = tval;
+ }else{
+ Node new_cond = cond_c.size()==1 ? cond_c[0] : NodeManager::currentNM()->mkNode( n_ite[0].getKind(), cond_c );
+ rem = NodeManager::currentNM()->mkNode( ITE, new_cond, isAnd ? tval : rem, isAnd ? rem : tval );
+ }
+ return true;
+ }else{
+ return false;
+ }
+}
+
+Node CegConjectureSingleInvSol::flattenITEs( Node n, bool rec ) {
+ Assert( !n.isNull() );
+ if( n.getKind()==ITE ){
+ Trace("csi-sol-debug") << "Flatten ITE." << std::endl;
+ Node ret;
+ Node n0 = rec ? flattenITEs( n[0] ) : n[0];
+ Node n1 = rec ? flattenITEs( n[1] ) : n[1];
+ Node n2 = rec ? flattenITEs( n[2] ) : n[2];
+ Assert( !n0.isNull() );
+ Assert( !n1.isNull() );
+ Assert( !n2.isNull() );
+ if( n0.getKind()==NOT ){
+ ret = NodeManager::currentNM()->mkNode( ITE, n0[0], n2, n1 );
+ }else if( n0.getKind()==AND || n0.getKind()==OR ){
+ std::vector< Node > children;
+ for( unsigned i=1; i<n0.getNumChildren(); i++ ){
+ children.push_back( n0[i] );
+ }
+ Node rem = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( n0.getKind(), children );
+ if( n0.getKind()==AND ){
+ ret = NodeManager::currentNM()->mkNode( ITE, rem, NodeManager::currentNM()->mkNode( ITE, n0[0], n1, n2 ), n2 );
+ }else{
+ ret = NodeManager::currentNM()->mkNode( ITE, rem, n1, NodeManager::currentNM()->mkNode( ITE, n0[0], n1, n2 ) );
+ }
+ }else{
+ if( n0.getKind()==ITE ){
+ n0 = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n0, n1 ),
+ NodeManager::currentNM()->mkNode( AND, n0.negate(), n2 ) );
+ }else if( n0.getKind()==EQUAL && n0[0].getType().isBoolean() ){
+ n0 = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, n0, n1 ),
+ NodeManager::currentNM()->mkNode( AND, n0.negate(), n1.negate() ) );
+ }else{
+ return NodeManager::currentNM()->mkNode( ITE, n0, n1, n2 );
+ }
+ ret = NodeManager::currentNM()->mkNode( ITE, n0, n1, n2 );
+ }
+ Assert( !ret.isNull() );
+ return flattenITEs( ret, false );
+ }else{
+ 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 = flattenITEs( n[i] );
+ children.push_back( nc );
+ childChanged = childChanged || nc!=n[i];
+ }
+ if( !childChanged ){
+ return n;
+ }else{
+ return NodeManager::currentNM()->mkNode( n.getKind(), children );
+ }
+ }else{
+ return n;
+ }
+ }
+}
+
+// assign is from literals to booleans
+// union_find is from args to values
+
+bool CegConjectureSingleInvSol::getAssign( bool pol, Node n, std::map< Node, bool >& assign, std::vector< Node >& new_assign, std::vector< Node >& vars,
+ std::vector< Node >& new_vars, std::vector< Node >& new_subs ) {
+ std::map< Node, bool >::iterator ita = assign.find( n );
+ if( ita!=assign.end() ){
+ Trace("csi-simp-debug") << "---already assigned, lookup " << pol << " " << ita->second << std::endl;
+ return pol==ita->second;
+ }else if( n.isConst() ){
+ return pol==(n==d_qe->getTermUtil()->d_true);
+ }else{
+ Trace("csi-simp-debug") << "---assign " << n << " " << pol << std::endl;
+ assign[n] = pol;
+ new_assign.push_back( n );
+ if( ( pol && n.getKind()==AND ) || ( !pol && n.getKind()==OR ) ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( !getAssign( pol, n[i], assign, new_assign, vars, new_vars, new_subs ) ){
+ return false;
+ }
+ }
+ }else if( n.getKind()==NOT ){
+ return getAssign( !pol, n[0], assign, new_assign, vars, new_vars, new_subs );
+ }else if( pol && n.getKind()==EQUAL ){
+ getAssignEquality( n, vars, new_vars, new_subs );
+ }
+ }
+ return true;
+}
+
+bool CegConjectureSingleInvSol::getAssignEquality( Node eq, std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs ) {
+ Assert( eq.getKind()==EQUAL );
+ //try to find valid argument
+ for( unsigned r=0; r<2; r++ ){
+ if( std::find( d_varList.begin(), d_varList.end(), eq[r] )!=d_varList.end() ){
+ Assert( std::find( vars.begin(), vars.end(), eq[r] )==vars.end() );
+ if( std::find( new_vars.begin(), new_vars.end(), eq[r] )==new_vars.end() ){
+ Node eqro = eq[r==0 ? 1 : 0 ];
+ if( !d_qe->getTermUtil()->containsTerm( eqro, eq[r] ) ){
+ Trace("csi-simp-debug") << "---equality " << eq[r] << " = " << eqro << std::endl;
+ new_vars.push_back( eq[r] );
+ new_subs.push_back( eqro );
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+Node CegConjectureSingleInvSol::simplifySolution( Node sol, TypeNode stn ){
+ int tsize, itesize;
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( sol, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << sol << "..." << std::endl;
+ }
+ Node sol0 = Rewriter::rewrite( sol );
+ Trace("csi-sol") << "now : " << sol0 << std::endl;
+
+ Node curr_sol = sol0;
+ Node prev_sol;
+ do{
+ prev_sol = curr_sol;
+ //first, pull ITE conditions
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( curr_sol, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " pull ITE..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << curr_sol << "..." << std::endl;
+ }
+ Node sol1 = pullITEs( curr_sol );
+ Trace("csi-sol") << "now : " << sol1 << std::endl;
+ //do standard rewriting
+ if( sol1!=curr_sol ){
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( sol1, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << sol1 << "..." << std::endl;
+ }
+ Node sol2 = Rewriter::rewrite( sol1 );
+ Trace("csi-sol") << "now : " << sol2 << std::endl;
+ curr_sol = sol2;
+ }
+ //now do branch analysis
+ if( Trace.isOn("csi-sol") ){
+ tsize = 0;itesize = 0;
+ debugTermSize( curr_sol, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " simplify solution..." << std::endl;
+ Trace("csi-sol-debug") << "sol : " << curr_sol << "..." << std::endl;
+ }
+ std::map< Node, bool > sassign;
+ std::vector< Node > svars;
+ std::vector< Node > ssubs;
+ Node sol3 = simplifySolutionNode( curr_sol, stn, sassign, svars, ssubs, 0 );
+ Trace("csi-sol") << "now : " << sol3 << std::endl;
+ if( sol3!=curr_sol ){
+ //do standard rewriting again
+ if( Trace.isOn("csi-sol" ) ){
+ tsize = 0;itesize = 0;
+ debugTermSize( sol3, tsize, itesize );
+ Trace("csi-sol") << tsize << " " << itesize << " rewrite..." << std::endl;
+ }
+ Node sol4 = Rewriter::rewrite( sol3 );
+ Trace("csi-sol") << "now : " << sol4 << std::endl;
+ curr_sol = sol4;
+ }
+ }while( curr_sol!=prev_sol );
+
+ return curr_sol;
+}
+
+Node CegConjectureSingleInvSol::simplifySolutionNode( Node sol, TypeNode stn, std::map< Node, bool >& assign,
+ std::vector< Node >& vars, std::vector< Node >& subs, int status ) {
+
+ Assert( vars.size()==subs.size() );
+ std::map< Node, bool >::iterator ita = assign.find( sol );
+ if( ita!=assign.end() ){
+ //it is currently assigned a boolean value
+ return NodeManager::currentNM()->mkConst( ita->second );
+ }else{
+ d_qe->getTermDatabaseSygus()->registerSygusType( stn );
+ std::map< int, TypeNode > stnc;
+ if( !stn.isNull() ){
+ 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() ){
+ for( unsigned i=0; i<dt[karg].getNumArgs(); i++ ){
+ stnc[i] = d_qe->getTermDatabaseSygus()->getArgType( dt[karg], i );
+ }
+ }
+ }
+ }
+
+ if( sol.getKind()==ITE ){
+ Trace("csi-simp") << "Simplify ITE " << std::endl;
+ std::vector< Node > children;
+ for( unsigned r=1; r<=2; r++ ){
+ std::vector< Node > new_assign;
+ std::vector< Node > new_vars;
+ std::vector< Node > new_subs;
+ if( getAssign( r==1, sol[0], assign, new_assign, vars, new_vars, new_subs ) ){
+ Trace("csi-simp") << "- branch " << r << " led to " << new_assign.size() << " assignments, " << new_vars.size() << " equalities." << std::endl;
+ unsigned prev_size = vars.size();
+ Node nc = sol[r];
+ if( !new_vars.empty() ){
+ nc = nc.substitute( new_vars.begin(), new_vars.end(), new_subs.begin(), new_subs.end() );
+ vars.insert( vars.end(), new_vars.begin(), new_vars.end() );
+ subs.insert( subs.end(), new_subs.begin(), new_subs.end() );
+ }
+ nc = simplifySolutionNode( nc, stnc[r], assign, vars, subs, 0 );
+ children.push_back( nc );
+ //clean up substitution
+ if( !new_vars.empty() ){
+ vars.resize( prev_size );
+ subs.resize( prev_size );
+ }
+ }else{
+ Trace("csi-simp") << "- branch " << r << " of " << sol[0] << " is infeasible." << std::endl;
+ }
+ //clean up assignment
+ for( unsigned i=0; i<new_assign.size(); i++ ){
+ assign.erase( new_assign[i] );
+ }
+ }
+ if( children.size()==1 || ( children.size()==2 && children[0]==children[1] ) ){
+ return children[0];
+ }else{
+ Assert( children.size()==2 );
+ Node ncond = simplifySolutionNode( sol[0], stnc[0], assign, vars, subs, 0 );
+ Node ret = NodeManager::currentNM()->mkNode( ITE, ncond, children[0], children[1] );
+
+ //expand/flatten if necessary
+ Node orig_ret = ret;
+ if( !stnc[0].isNull() ){
+ d_qe->getTermDatabaseSygus()->registerSygusType( stnc[0] );
+ Node prev_ret;
+ while( !d_qe->getTermDatabaseSygus()->hasKind( stnc[0], ret[0].getKind() ) && ret!=prev_ret ){
+ prev_ret = ret;
+ Node exp_c = d_qe->getTermDatabaseSygus()->expandBuiltinTerm( ret[0] );
+ if( !exp_c.isNull() ){
+ Trace("csi-simp-debug") << "Pre expand to " << ret[0] << " to " << exp_c << std::endl;
+ ret = NodeManager::currentNM()->mkNode( ITE, exp_c, ret[1], ret[2] );
+ }
+ if( !d_qe->getTermDatabaseSygus()->hasKind( stnc[0], ret[0].getKind() ) ){
+ Trace("csi-simp-debug") << "Flatten based on " << ret[0] << "." << std::endl;
+ ret = flattenITEs( ret, false );
+ }
+ }
+ }
+ return ret;
+ /*
+ if( orig_ret!=ret ){
+ Trace("csi-simp") << "Try expanded ITE" << std::endl;
+ return ret;//simplifySolutionNode( ret, stn, assign, vars, subs, status );
+ }else{
+ return ret;
+ }
+ */
+ }
+ }else if( sol.getKind()==OR || sol.getKind()==AND ){
+ Trace("csi-simp") << "Simplify " << sol.getKind() << std::endl;
+ //collect new equalities
+ std::map< Node, bool > atoms;
+ std::vector< Node > inc;
+ std::vector< Node > children;
+ std::vector< Node > new_vars;
+ std::vector< Node > new_subs;
+ Node bc = sol.getKind()==OR ? d_qe->getTermUtil()->d_true : d_qe->getTermUtil()->d_false;
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ bool do_exc = false;
+ Node c;
+ std::map< Node, bool >::iterator ita = assign.find( sol[i] );
+ if( ita==assign.end() ){
+ c = sol[i];
+ }else{
+ c = NodeManager::currentNM()->mkConst( ita->second );
+ }
+ Trace("csi-simp") << " - child " << i << " : " << c << std::endl;
+ if( c.isConst() ){
+ if( c==bc ){
+ Trace("csi-simp") << " ...singularity." << std::endl;
+ return bc;
+ }else{
+ do_exc = true;
+ }
+ }else{
+ Node atom = c.getKind()==NOT ? c[0] : c;
+ bool pol = c.getKind()!=NOT;
+ std::map< Node, bool >::iterator it = atoms.find( atom );
+ if( it==atoms.end() ){
+ atoms[atom] = pol;
+ if( status==0 && atom.getKind()==EQUAL ){
+ if( pol==( sol.getKind()==AND ) ){
+ Trace("csi-simp") << " ...equality." << std::endl;
+ if( getAssignEquality( atom, vars, new_vars, new_subs ) ){
+ children.push_back( sol[i] );
+ do_exc = true;
+ }
+ }
+ }
+ }else{
+ //repeated atom
+ if( it->second!=pol ){
+ return NodeManager::currentNM()->mkConst( sol.getKind()==OR );
+ }else{
+ do_exc = true;
+ }
+ }
+ }
+ if( !do_exc ){
+ inc.push_back( sol[i] );
+ }else{
+ Trace("csi-simp") << " ...exclude." << std::endl;
+ }
+ }
+ if( !new_vars.empty() ){
+ if( !inc.empty() ){
+ Node ret = inc.size()==1 ? inc[0] : NodeManager::currentNM()->mkNode( sol.getKind(), inc );
+ Trace("csi-simp") << "Base return is : " << ret << std::endl;
+ // apply substitution
+ ret = ret.substitute( new_vars.begin(), new_vars.end(), new_subs.begin(), new_subs.end() );
+ ret = Rewriter::rewrite( ret );
+ Trace("csi-simp") << "After substitution : " << ret << std::endl;
+ unsigned prev_size = vars.size();
+ vars.insert( vars.end(), new_vars.begin(), new_vars.end() );
+ subs.insert( subs.end(), new_subs.begin(), new_subs.end() );
+ ret = simplifySolutionNode( ret, TypeNode::null(), assign, vars, subs, 1 );
+ //clean up substitution
+ if( !vars.empty() ){
+ vars.resize( prev_size );
+ subs.resize( prev_size );
+ }
+ //Trace("csi-simp") << "After simplification : " << ret << std::endl;
+ if( ret.isConst() ){
+ if( ret==bc ){
+ return bc;
+ }
+ }else{
+ if( ret.getKind()==sol.getKind() ){
+ for( unsigned i=0; i<ret.getNumChildren(); i++ ){
+ children.push_back( ret[i] );
+ }
+ }else{
+ children.push_back( ret );
+ }
+ }
+ }
+ }else{
+ //recurse on children
+ for( unsigned i=0; i<inc.size(); i++ ){
+ Node retc = simplifySolutionNode( inc[i], TypeNode::null(), assign, vars, subs, 0 );
+ if( retc.isConst() ){
+ if( retc==bc ){
+ return bc;
+ }
+ }else{
+ children.push_back( retc );
+ }
+ }
+ }
+ // now, remove all equalities that are implied
+ std::vector< Node > final_children;
+ for( unsigned i=0; i<children.size(); i++ ){
+ bool red = false;
+ Node atom = children[i].getKind()==NOT ? children[i][0] : children[i];
+ bool pol = children[i].getKind()!=NOT;
+ if( status==0 && atom.getKind()==EQUAL ){
+ if( pol!=( sol.getKind()==AND ) ){
+ std::vector< Node > tmp_vars;
+ std::vector< Node > tmp_subs;
+ if( getAssignEquality( atom, vars, tmp_vars, tmp_subs ) ){
+ Trace("csi-simp-debug") << "Check if " << children[i] << " is redundant in " << sol << std::endl;
+ for( unsigned j=0; j<children.size(); j++ ){
+ if( j!=i && ( j>i || std::find( final_children.begin(), final_children.end(), children[j] )!=final_children.end() ) ){
+ Node sj = children[j].substitute( tmp_vars.begin(), tmp_vars.end(), tmp_subs.begin(), tmp_subs.end() );
+ sj = Rewriter::rewrite( sj );
+ if( sj==( sol.getKind()==AND ? d_qe->getTermUtil()->d_false : d_qe->getTermUtil()->d_true ) ){
+ Trace("csi-simp") << "--- " << children[i].negate() << " is implied by " << children[j].negate() << std::endl;
+ red = true;
+ break;
+ }
+ }
+ }
+ if( !red ){
+ Trace("csi-simp-debug") << "...is not." << std::endl;
+ }
+ }
+ }
+ }
+ if( !red ){
+ final_children.push_back( children[i] );
+ }
+ }
+ return final_children.size()==0 ? NodeManager::currentNM()->mkConst( sol.getKind()==AND ) :
+ ( final_children.size()==1 ? final_children[0] : NodeManager::currentNM()->mkNode( sol.getKind(), final_children ) );
+ }else{
+ //generic simplification
+ std::vector< Node > children;
+ if( sol.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( sol.getOperator() );
+ }
+ bool childChanged = false;
+ for( unsigned i=0; i<sol.getNumChildren(); i++ ){
+ Node nc = simplifySolutionNode( sol[i], stnc[i], assign, vars, subs, 0 );
+ childChanged = childChanged || nc!=sol[i];
+ children.push_back( nc );
+ }
+ if( childChanged ){
+ return NodeManager::currentNM()->mkNode( sol.getKind(), children );
+ }
+ }
+ return sol;
+ }
+}
+
+
+void CegConjectureSingleInvSol::preregisterConjecture( Node q ) {
+ Trace("csi-sol") << "Preregister conjecture : " << q << std::endl;
+ Node n = q;
+ if( n.getKind()==FORALL ){
+ n = n[1];
+ }
+ if( n.getKind()==EXISTS ){
+ if( n[0].getNumChildren()==d_varList.size() ){
+ std::vector< Node > evars;
+ for( unsigned i=0; i<n[0].getNumChildren(); i++ ){
+ evars.push_back( n[0][i] );
+ }
+ n = n[1].substitute( evars.begin(), evars.end(), d_varList.begin(), d_varList.end() );
+ }else{
+ Trace("csi-sol") << "Not the same number of variables, return." << std::endl;
+ return;
+ }
+ }
+ Trace("csi-sol") << "Preregister node for solution reconstruction : " << n << std::endl;
+ registerEquivalentTerms( n );
+}
+
+Node CegConjectureSingleInvSol::reconstructSolution( Node sol, TypeNode stn, int& reconstructed ) {
+ Trace("csi-rcons") << "Solution (pre-reconstruction) is : " << sol << std::endl;
+ int status;
+ d_root_id = collectReconstructNodes( sol, stn, status );
+ if( status==0 ){
+ Node ret = getReconstructedSolution( d_root_id );
+ Trace("csi-rcons") << "Sygus solution is : " << ret << std::endl;
+ Assert( !ret.isNull() );
+ reconstructed = 1;
+ return ret;
+ }else{
+ //Trace("csi-debug-sol") << "Induced solution template is : " << d_templ_solution << std::endl;
+ if( Trace.isOn("csi-rcons") ){
+ for( std::map< TypeNode, std::map< Node, int > >::iterator it = d_rcons_to_id.begin(); it != d_rcons_to_id.end(); ++it ){
+ TypeNode tn = it->first;
+ Assert( tn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Trace("csi-rcons") << "Terms to reconstruct of type " << dt.getName() << " : " << std::endl;
+ for( std::map< Node, int >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
+ if( d_reconstruct.find( it2->second )==d_reconstruct.end() ){
+ Trace("csi-rcons") << " " << it2->first << std::endl;
+ }
+ }
+ Assert( !it->second.empty() );
+ }
+ }
+ unsigned index = 0;
+ std::map< TypeNode, bool > active;
+ for( std::map< TypeNode, std::map< Node, int > >::iterator it = d_rcons_to_id.begin(); it != d_rcons_to_id.end(); ++it ){
+ active[it->first] = true;
+ }
+ //enumerate for all types
+ do {
+ std::vector< TypeNode > to_erase;
+ for( std::map< TypeNode, bool >::iterator it = active.begin(); it != active.end(); ++it ){
+ TypeNode stn = it->first;
+ Node ns = d_qe->getTermEnumeration()->getEnumerateTerm(stn, index);
+ if( ns.isNull() ){
+ to_erase.push_back( stn );
+ }else{
+ Node nb = d_qe->getTermDatabaseSygus()->sygusToBuiltin( ns, stn );
+ Node nr = Rewriter::rewrite( nb );//d_qe->getTermDatabaseSygus()->getNormalized( stn, nb, false, false );
+ Trace("csi-rcons-debug2") << " - try " << ns << " -> " << nr << " for " << stn << " " << nr.getKind() << std::endl;
+ std::map< Node, int >::iterator itt = d_rcons_to_id[stn].find( nr );
+ if( itt!= d_rcons_to_id[stn].end() ){
+ // if it is not already reconstructed
+ if( d_reconstruct.find( itt->second )==d_reconstruct.end() ){
+ Trace("csi-rcons") << "...reconstructed " << ns << " for term " << nr << std::endl;
+ bool do_check = true;//getPathToRoot( itt->second );
+ setReconstructed( itt->second, ns );
+ if( do_check ){
+ Trace("csi-rcons-debug") << "...path to root, try reconstruction." << std::endl;
+ d_tmp_fail.clear();
+ Node ret = getReconstructedSolution( d_root_id );
+ if( !ret.isNull() ){
+ Trace("csi-rcons") << "Sygus solution (after enumeration) is : " << ret << std::endl;
+ reconstructed = 1;
+ return ret;
+ }
+ }else{
+ Trace("csi-rcons-debug") << "...no path to root." << std::endl;
+ }
+ }
+ }
+ }
+ }
+ for( unsigned i=0; i<to_erase.size(); i++ ){
+ active.erase( to_erase[i] );
+ }
+ index++;
+ if( index%100==0 ){
+ Trace("csi-rcons-stats") << "Tried " << index << " for each type." << std::endl;
+ }
+ }while( !active.empty() );
+
+ // we ran out of elements, return null
+ reconstructed = -1;
+ Warning() << CommandFailure("Cannot get synth function: reconstruction to syntax failed.");
+ return Node::null(); // return sol;
+ }
+}
+
+int CegConjectureSingleInvSol::collectReconstructNodes( Node t, TypeNode stn, int& status ) {
+ std::map< Node, int >::iterator itri = d_rcons_to_status[stn].find( t );
+ if( itri!=d_rcons_to_status[stn].end() ){
+ status = itri->second;
+ //Trace("csi-rcons-debug") << "-> (cached) " << status << " for " << d_rcons_to_id[stn][t] << std::endl;
+ return d_rcons_to_id[stn][t];
+ }else{
+ status = 1;
+ // register the type
+ registerType(stn);
+ int id = allocate( t, stn );
+ d_rcons_to_status[stn][t] = -1;
+ TypeNode tn = t.getType();
+ Assert( stn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ Assert( dt.isSygus() );
+ Trace("csi-rcons-debug") << "Check reconstruct " << t << ", sygus type " << dt.getName() << ", kind " << t.getKind() << ", id : " << id << std::endl;
+ int carg = -1;
+ int karg = -1;
+ // first, do standard minimizations
+ 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()->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()->getKindConsNum( stn, min_t.getKind() );
+ if( karg!=-1 ){
+ //collect the children of min_t
+ std::vector< Node > tchildren;
+ if( min_t.getNumChildren()>dt[karg].getNumArgs() && quantifiers::TermUtil::isAssoc( min_t.getKind() ) && dt[karg].getNumArgs()==2 ){
+ tchildren.push_back( min_t[0] );
+ std::vector< Node > rem_children;
+ for( unsigned i=1; i<min_t.getNumChildren(); i++ ){
+ rem_children.push_back( min_t[i] );
+ }
+ Node t2 = NodeManager::currentNM()->mkNode( min_t.getKind(), rem_children );
+ tchildren.push_back( t2 );
+ Trace("csi-rcons-debug") << "...split n-ary to binary " << min_t[0] << " " << t2 << "." << std::endl;
+ }else{
+ for( unsigned i=0; i<min_t.getNumChildren(); i++ ){
+ tchildren.push_back( min_t[i] );
+ }
+ }
+ //recurse on the children
+ if( tchildren.size()==dt[karg].getNumArgs() ){
+ Trace("csi-rcons-debug") << "Type for " << id << " has kind " << min_t.getKind() << ", recurse." << std::endl;
+ status = 0;
+ Node cons = Node::fromExpr( dt[karg].getConstructor() );
+ if( !collectReconstructNodes( id, tchildren, dt[karg], d_reconstruct_op[id][cons], status ) ){
+ Trace("csi-rcons-debug") << "...failure for " << id << " " << dt[karg].getName() << std::endl;
+ d_reconstruct_op[id].erase( cons );
+ status = 1;
+ }
+ }else{
+ Trace("csi-rcons-debug") << "Type for " << id << " has kind " << min_t.getKind() << ", but argument # mismatch." << std::endl;
+ }
+ }
+ if( status!=0 ){
+ //try constant reconstruction
+ if( min_t.isConst() ){
+ Trace("csi-rcons-debug") << "...try constant reconstruction." << std::endl;
+ Node min_t_c = builtinToSygusConst(min_t, stn);
+ if( !min_t_c.isNull() ){
+ Trace("csi-rcons-debug") << " constant reconstruction success for " << id << ", result = " << min_t_c << std::endl;
+ d_reconstruct[id] = min_t_c;
+ status = 0;
+ }
+ }
+ if( status!=0 ){
+ //try identity functions
+ for (unsigned ii : d_id_funcs[stn])
+ {
+ Assert( dt[ii].getNumArgs()==1 );
+ //try to directly reconstruct from single argument
+ std::vector< Node > tchildren;
+ tchildren.push_back( min_t );
+ TypeNode stnc = TypeNode::fromType( ((SelectorType)dt[ii][0].getType()).getRangeType() );
+ Trace("csi-rcons-debug") << "...try identity function " << dt[ii].getSygusOp() << ", child type is " << stnc << std::endl;
+ status = 0;
+ Node cons = Node::fromExpr( dt[ii].getConstructor() );
+ if( !collectReconstructNodes( id, tchildren, dt[ii], d_reconstruct_op[id][cons], status ) ){
+ d_reconstruct_op[id].erase( cons );
+ status = 1;
+ }else{
+ Trace("csi-rcons-debug") << " identity function success for " << id << std::endl;
+ break;
+ }
+ }
+ if( status!=0 ){
+ //try other options, such as matching against other constructors
+ Trace("csi-rcons-debug") << "Try matching for " << id << "." << std::endl;
+ bool success;
+ int c_index = 0;
+ do{
+ success = false;
+ int index_found;
+ std::vector< Node > args;
+ if (getMatch(min_t, stn, index_found, args, karg, c_index))
+ {
+ success = true;
+ status = 0;
+ Node cons = Node::fromExpr( dt[index_found].getConstructor() );
+ Trace("csi-rcons-debug") << "Try alternative for " << id << ", matching " << dt[index_found].getName() << " with children : " << std::endl;
+ for( unsigned i=0; i<args.size(); i++ ){
+ Trace("csi-rcons-debug") << " " << args[i] << std::endl;
+ }
+ if( !collectReconstructNodes( id, args, dt[index_found], d_reconstruct_op[id][cons], status ) ){
+ d_reconstruct_op[id].erase( cons );
+ status = 1;
+ }else{
+ c_index = index_found+1;
+ }
+ }
+ }while( success && status!=0 );
+
+ if( status!=0 ){
+ // construct an equivalence class of terms that are equivalent to t
+ if( d_rep[id]==id ){
+ Trace("csi-rcons-debug") << "Try rewriting for " << id << "." << std::endl;
+ //get equivalence class of term
+ std::vector< Node > equiv;
+ if( tn.isBoolean() ){
+ Node curr = min_t;
+ Node new_t;
+ do{
+ new_t = Node::null();
+ if( curr.getKind()==EQUAL ){
+ if( curr[0].getType().isInteger() || curr[0].getType().isReal() ){
+ new_t = NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( LEQ, curr[0], curr[1] ),
+ NodeManager::currentNM()->mkNode( LEQ, curr[1], curr[0] ) );
+ }else if( curr[0].getType().isBoolean() ){
+ new_t = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, curr[0], curr[1] ),
+ NodeManager::currentNM()->mkNode( AND, curr[0].negate(), curr[1].negate() ) );
+ }else{
+ new_t = NodeManager::currentNM()->mkNode( NOT, NodeManager::currentNM()->mkNode( NOT, curr ) );
+ }
+ }else if( curr.getKind()==ITE ){
+ new_t = NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, curr[0], curr[1] ),
+ NodeManager::currentNM()->mkNode( AND, curr[0].negate(), curr[2] ) );
+ }else if( curr.getKind()==OR || curr.getKind()==AND ){
+ new_t = TermUtil::simpleNegate( curr ).negate();
+ }else if( curr.getKind()==NOT ){
+ new_t = TermUtil::simpleNegate( curr[0] );
+ }else{
+ new_t = NodeManager::currentNM()->mkNode( NOT, NodeManager::currentNM()->mkNode( NOT, curr ) );
+ }
+ if( !new_t.isNull() ){
+ if( new_t!=min_t && std::find( equiv.begin(), equiv.end(), new_t )==equiv.end() ){
+ curr = new_t;
+ equiv.push_back( new_t );
+ }else{
+ new_t = Node::null();
+ }
+ }
+ }while( !new_t.isNull() );
+ }
+ //get decompositions
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ Kind k = d_qe->getTermDatabaseSygus()->getConsNumKind( stn, i );
+ getEquivalentTerms( k, min_t, equiv );
+ }
+ //assign ids to terms
+ Trace("csi-rcons-debug") << "Term " << id << " is equivalent to " << equiv.size() << " terms : " << std::endl;
+ std::vector< int > equiv_ids;
+ for( unsigned i=0; i<equiv.size(); i++ ){
+ Trace("csi-rcons-debug") << " " << equiv[i] << std::endl;
+ if( d_rcons_to_id[stn].find( equiv[i] )==d_rcons_to_id[stn].end() ){
+ int eq_id = allocate( equiv[i], stn );
+ d_eqc.erase( eq_id );
+ d_rep[eq_id] = id;
+ d_eqc[id].push_back( eq_id );
+ equiv_ids.push_back( eq_id );
+ }else{
+ equiv_ids.push_back( -1 );
+ }
+ }
+ // now, try each of them
+ for( unsigned i=0; i<equiv.size(); i++ ){
+ if( equiv_ids[i]!=-1 ){
+ collectReconstructNodes( equiv[i], stn, status );
+ //if one succeeds
+ if( status==0 ){
+ Node rsol = getReconstructedSolution( equiv_ids[i] );
+ Assert( !rsol.isNull() );
+ //set all members of the equivalence class that this is the reconstructed solution
+ setReconstructed( id, rsol );
+ break;
+ }
+ }
+ }
+ }else{
+ Trace("csi-rcons-debug") << "Do not try rewriting for " << id << ", rep = " << d_rep[id] << std::endl;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( status!=0 ){
+ Trace("csi-rcons-debug") << "-> *** reconstruction required for id " << id << std::endl;
+ }else{
+ Trace("csi-rcons-debug") << "-> success for " << id << std::endl;
+ }
+ d_rcons_to_status[stn][t] = status;
+ return id;
+ }
+}
+
+bool CegConjectureSingleInvSol::collectReconstructNodes( int pid, std::vector< Node >& ts, const DatatypeConstructor& dtc, std::vector< int >& ids, int& status ) {
+ Assert( dtc.getNumArgs()==ts.size() );
+ for( unsigned i=0; i<ts.size(); i++ ){
+ TypeNode cstn = d_qe->getTermDatabaseSygus()->getArgType( dtc, i );
+ int cstatus;
+ int c_id = collectReconstructNodes( ts[i], cstn, cstatus );
+ if( cstatus==-1 ){
+ return false;
+ }else if( cstatus!=0 ){
+ status = 1;
+ }
+ ids.push_back( c_id );
+ }
+ for( unsigned i=0; i<ids.size(); i++ ){
+ d_parents[ids[i]].push_back( pid );
+ }
+ return true;
+}
+
+ /*
+ //flatten ITEs if necessary TODO : carry assignment or move this elsewhere
+ if( t.getKind()==ITE ){
+ TypeNode cstn = tds->getArgType( dt[karg], 0 );
+ tds->registerSygusType( cstn );
+ Node prev_t;
+ while( !tds->hasKind( cstn, t[0].getKind() ) && t!=prev_t ){
+ prev_t = t;
+ Node exp_c = tds->expandBuiltinTerm( t[0] );
+ if( !exp_c.isNull() ){
+ t = NodeManager::currentNM()->mkNode( ITE, exp_c, t[1], t[2] );
+ Trace("csi-rcons-debug") << "Pre expand to " << t << std::endl;
+ }
+ t = flattenITEs( t, false );
+ if( t!=prev_t ){
+ Trace("csi-rcons-debug") << "Flatten ITE to " << t << std::endl;
+ std::map< Node, bool > sassign;
+ std::vector< Node > svars;
+ std::vector< Node > ssubs;
+ t = simplifySolutionNode( t, sassign, svars, ssubs, 0 );
+ }
+ Assert( t.getKind()==ITE );
+ }
+ }
+ */
+
+
+Node CegConjectureSingleInvSol::CegConjectureSingleInvSol::getReconstructedSolution( int id, bool mod_eq ) {
+ std::map< int, Node >::iterator it = d_reconstruct.find( id );
+ if( it!=d_reconstruct.end() ){
+ return it->second;
+ }else{
+ if( std::find( d_tmp_fail.begin(), d_tmp_fail.end(), id )!=d_tmp_fail.end() ){
+ return Node::null();
+ }else{
+ // try each child option
+ std::map< int, std::map< Node, std::vector< int > > >::iterator ito = d_reconstruct_op.find( id );
+ if( ito!=d_reconstruct_op.end() ){
+ for( std::map< Node, std::vector< int > >::iterator itt = ito->second.begin(); itt != ito->second.end(); ++itt ){
+ std::vector< Node > children;
+ children.push_back( itt->first );
+ bool success = true;
+ for( unsigned i=0; i<itt->second.size(); i++ ){
+ Node nc = getReconstructedSolution( itt->second[i] );
+ if( nc.isNull() ){
+ success = false;
+ break;
+ }else{
+ children.push_back( nc );
+ }
+ }
+ if( success ){
+ Node ret = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
+ setReconstructed( id, ret );
+ return ret;
+ }
+ }
+ }
+ // try terms in the equivalence class of this
+ if( mod_eq ){
+ int rid = d_rep[id];
+ for( unsigned i=0; i<d_eqc[rid].size(); i++ ){
+ int tid = d_eqc[rid][i];
+ if( tid!=id ){
+ Node eret = getReconstructedSolution( tid, false );
+ if( !eret.isNull() ){
+ setReconstructed( id, eret );
+ return eret;
+ }
+ }
+ }
+ }
+ d_tmp_fail.push_back( id );
+ return Node::null();
+ }
+ }
+}
+
+int CegConjectureSingleInvSol::allocate( Node n, TypeNode stn ) {
+ std::map< Node, int >::iterator it = d_rcons_to_id[stn].find( n );
+ if( it==d_rcons_to_id[stn].end() ){
+ int ret = d_id_count;
+ if( Trace.isOn("csi-rcons-debug") ){
+ const Datatype& dt = ((DatatypeType)(stn).toType()).getDatatype();
+ Trace("csi-rcons-debug") << "id " << ret << " : " << n << " " << dt.getName() << std::endl;
+ }
+ d_id_node[d_id_count] = n;
+ d_id_type[d_id_count] = stn;
+ d_rep[d_id_count] = d_id_count;
+ d_eqc[d_id_count].push_back( d_id_count );
+ d_rcons_to_id[stn][n] = d_id_count;
+ d_id_count++;
+ return ret;
+ }else{
+ return it->second;
+ }
+}
+
+bool CegConjectureSingleInvSol::getPathToRoot( int id ) {
+ if( id==d_root_id ){
+ return true;
+ }else{
+ std::map< int, Node >::iterator it = d_reconstruct.find( id );
+ if( it!=d_reconstruct.end() ){
+ return false;
+ }else{
+ int rid = d_rep[id];
+ for( unsigned j=0; j<d_parents[rid].size(); j++ ){
+ if( getPathToRoot( d_parents[rid][j] ) ){
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
+
+void CegConjectureSingleInvSol::setReconstructed( int id, Node n ) {
+ //set all equivalent to this as reconstructed
+ int rid = d_rep[id];
+ for( unsigned i=0; i<d_eqc[rid].size(); i++ ){
+ d_reconstruct[d_eqc[rid][i]] = n;
+ }
+}
+
+void CegConjectureSingleInvSol::getEquivalentTerms( Kind k, Node n, std::vector< Node >& equiv ) {
+ if( k==AND || k==OR ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, n, n ) );
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, n, NodeManager::currentNM()->mkConst( k==AND ) ) );
+ }
+ //multiplication for integers
+ //TODO for bitvectors
+ Kind mk = ( k==PLUS || k==MINUS ) ? MULT : UNDEFINED_KIND;
+ if( mk!=UNDEFINED_KIND ){
+ if( n.getKind()==mk && n[0].isConst() && n[0].getType().isInteger() ){
+ bool success = true;
+ for( unsigned i=0; i<2; i++ ){
+ Node eq;
+ if( k==PLUS || k==MINUS ){
+ Node oth = NodeManager::currentNM()->mkConst( Rational(i==0 ? 1000 : -1000) );
+ eq = i==0 ? NodeManager::currentNM()->mkNode( LEQ, n[0], oth ) : NodeManager::currentNM()->mkNode( GEQ, n[0], oth );
+ }
+ if( !eq.isNull() ){
+ eq = Rewriter::rewrite( eq );
+ if( eq!=d_qe->getTermUtil()->d_true ){
+ success = false;
+ break;
+ }
+ }
+ }
+ if( success ){
+ Node var = n[1];
+ Node rem;
+ if( k==PLUS || k==MINUS ){
+ int rem_coeff = (int)n[0].getConst<Rational>().getNumerator().getSignedInt();
+ if( rem_coeff>0 && k==PLUS ){
+ rem_coeff--;
+ }else if( rem_coeff<0 && k==MINUS ){
+ rem_coeff++;
+ }else{
+ success = false;
+ }
+ if( success ){
+ rem = NodeManager::currentNM()->mkNode( MULT, NodeManager::currentNM()->mkConst( Rational(rem_coeff) ), var );
+ rem = Rewriter::rewrite( rem );
+ }
+ }
+ if( !rem.isNull() ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, rem, var ) );
+ }
+ }
+ }
+ }
+ //negative constants
+ if( k==MINUS ){
+ if( n.isConst() && n.getType().isInteger() && n.getConst<Rational>().getNumerator().strictlyNegative() ){
+ Node nn = NodeManager::currentNM()->mkNode( UMINUS, n );
+ nn = Rewriter::rewrite( nn );
+ equiv.push_back( NodeManager::currentNM()->mkNode( MINUS, NodeManager::currentNM()->mkConst( Rational(0) ), nn ) );
+ }
+ }
+ //inequalities
+ if( k==GEQ || k==LEQ || k==LT || k==GT || k==NOT ){
+ Node atom = n.getKind()==NOT ? n[0] : n;
+ bool pol = n.getKind()!=NOT;
+ Kind ak = atom.getKind();
+ if( ( ak==GEQ || ak==LEQ || ak==LT || ak==GT ) && ( pol || k!=NOT ) ){
+ Node t1 = atom[0];
+ Node t2 = atom[1];
+ if( !pol ){
+ ak = ak==GEQ ? LT : ( ak==LEQ ? GT : ( ak==LT ? GEQ : LEQ ) );
+ }
+ if( k==NOT ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( ak==GEQ ? LT : ( ak==LEQ ? GT : ( ak==LT ? GEQ : LEQ ) ), t1, t2 ).negate() );
+ }else if( k==ak ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, t1, t2 ) );
+ }else if( (k==GEQ || k==LEQ)==(ak==GEQ || ak==LEQ) ){
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, t2, t1 ) );
+ }else if( t1.getType().isInteger() && t2.getType().isInteger() ){
+ if( (k==GEQ || k==GT)!=(ak==GEQ || ak==GT) ){
+ Node ts = t1;
+ t1 = t2;
+ t2 = ts;
+ ak = ak==GEQ ? LEQ : ( ak==LEQ ? GEQ : ( ak==LT ? GT : LT ) );
+ }
+ t2 = NodeManager::currentNM()->mkNode( PLUS, t2, NodeManager::currentNM()->mkConst( Rational( (ak==GT || ak==LEQ) ? 1 : -1 ) ) );
+ t2 = Rewriter::rewrite( t2 );
+ equiv.push_back( NodeManager::currentNM()->mkNode( k, t1, t2 ) );
+ }
+ }
+ }
+
+ //based on eqt cache
+ std::map< Node, Node >::iterator itet = d_eqt_rep.find( n );
+ if( itet!=d_eqt_rep.end() ){
+ Node rn = itet->second;
+ for( unsigned i=0; i<d_eqt_eqc[rn].size(); i++ ){
+ if( d_eqt_eqc[rn][i]!=n && d_eqt_eqc[rn][i].getKind()==k ){
+ if( std::find( equiv.begin(), equiv.end(), d_eqt_eqc[rn][i] )==equiv.end() ){
+ equiv.push_back( d_eqt_eqc[rn][i] );
+ }
+ }
+ }
+ }
+}
+
+void CegConjectureSingleInvSol::registerEquivalentTerms( Node n ) {
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ registerEquivalentTerms( n[i] );
+ }
+ Node rn = Rewriter::rewrite( n );
+ if( rn!=n ){
+ Trace("csi-equiv") << " eq terms : " << n << " " << rn << std::endl;
+ d_eqt_rep[n] = rn;
+ d_eqt_rep[rn] = rn;
+ if( std::find( d_eqt_eqc[rn].begin(), d_eqt_eqc[rn].end(), rn )==d_eqt_eqc[rn].end() ){
+ d_eqt_eqc[rn].push_back( rn );
+ }
+ if( std::find( d_eqt_eqc[rn].begin(), d_eqt_eqc[rn].end(), n )==d_eqt_eqc[rn].end() ){
+ d_eqt_eqc[rn].push_back( n );
+ }
+ }
+}
+
+Node CegConjectureSingleInvSol::builtinToSygusConst(Node c,
+ TypeNode tn,
+ int rcons_depth)
+{
+ std::map<Node, Node>::iterator it = d_builtin_const_to_sygus[tn].find(c);
+ if (it != d_builtin_const_to_sygus[tn].end())
+ {
+ return it->second;
+ }
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ NodeManager* nm = NodeManager::currentNM();
+ Node sc;
+ d_builtin_const_to_sygus[tn][c] = sc;
+ Assert(c.isConst());
+ Assert(tn.isDatatype());
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Trace("csi-rcons-debug") << "Try to reconstruct " << c << " in "
+ << dt.getName() << std::endl;
+ Assert(dt.isSygus());
+ // if we are not interested in reconstructing constants, or the grammar allows
+ // them, return a proxy
+ if (!options::cegqiSingleInvReconstructConst() || dt.getSygusAllowConst())
+ {
+ Node k = nm->mkSkolem("sy", tn, "sygus proxy");
+ SygusPrintProxyAttribute spa;
+ k.setAttribute(spa, c);
+ sc = k;
+ }
+ else
+ {
+ int carg = tds->getOpConsNum(tn, c);
+ if (carg != -1)
+ {
+ sc = nm->mkNode(APPLY_CONSTRUCTOR,
+ Node::fromExpr(dt[carg].getConstructor()));
+ }
+ else
+ {
+ // identity functions
+ for (unsigned ii : d_id_funcs[tn])
+ {
+ Assert(dt[ii].getNumArgs() == 1);
+ // try to directly reconstruct from single argument
+ TypeNode tnc = tds->getArgType(dt[ii], 0);
+ Trace("csi-rcons-debug")
+ << "Based on id function " << dt[ii].getSygusOp()
+ << ", try reconstructing " << c << " instead in " << tnc
+ << std::endl;
+ Node n = builtinToSygusConst(c, tnc, rcons_depth);
+ if (!n.isNull())
+ {
+ sc = nm->mkNode(
+ APPLY_CONSTRUCTOR, Node::fromExpr(dt[ii].getConstructor()), n);
+ break;
+ }
+ }
+ if (sc.isNull())
+ {
+ if (rcons_depth < 1000)
+ {
+ // accelerated, recursive reconstruction of constants
+ Kind pk = tds->getPlusKind(TypeNode::fromType(dt.getSygusType()));
+ if (pk != UNDEFINED_KIND)
+ {
+ int arg = tds->getKindConsNum(tn, pk);
+ if (arg != -1)
+ {
+ Kind ck =
+ tds->getComparisonKind(TypeNode::fromType(dt.getSygusType()));
+ Kind pkm =
+ tds->getPlusKind(TypeNode::fromType(dt.getSygusType()), true);
+ // get types
+ Assert(dt[arg].getNumArgs() == 2);
+ TypeNode tn1 = tds->getArgType(dt[arg], 0);
+ TypeNode tn2 = tds->getArgType(dt[arg], 1);
+ // initialize d_const_list for tn1
+ registerType(tn1);
+ // iterate over all positive constants, largest to smallest
+ int start = d_const_list[tn1].size() - 1;
+ int end = d_const_list[tn1].size() - d_const_list_pos[tn1];
+ for (int i = start; i >= end; --i)
+ {
+ Node c1 = d_const_list[tn1][i];
+ // only consider if smaller than c, and
+ if (doCompare(c1, c, ck))
+ {
+ Node c2 = nm->mkNode(pkm, c, c1);
+ c2 = Rewriter::rewrite(c2);
+ if (c2.isConst())
+ {
+ // reconstruct constant on the other side
+ Node sc2 = builtinToSygusConst(c2, tn2, rcons_depth + 1);
+ if (!sc2.isNull())
+ {
+ Node sc1 = builtinToSygusConst(c1, tn1, rcons_depth);
+ Assert(!sc1.isNull());
+ sc = nm->mkNode(APPLY_CONSTRUCTOR,
+ Node::fromExpr(dt[arg].getConstructor()),
+ sc1,
+ sc2);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ d_builtin_const_to_sygus[tn][c] = sc;
+ return sc;
+}
+
+struct sortConstants
+{
+ Kind d_comp_kind;
+ bool operator()(Node i, Node j)
+ {
+ return i != j && doCompare(i, j, d_comp_kind);
+ }
+};
+
+void CegConjectureSingleInvSol::registerType(TypeNode tn)
+{
+ if (d_const_list_pos.find(tn) != d_const_list_pos.end())
+ {
+ return;
+ }
+ d_const_list_pos[tn] = 0;
+ Assert(tn.isDatatype());
+
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ // ensure it is registered
+ tds->registerSygusType(tn);
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ TypeNode btn = TypeNode::fromType(dt.getSygusType());
+ // for constant reconstruction
+ Kind ck = tds->getComparisonKind(btn);
+ Node z = d_qe->getTermUtil()->getTypeValue(btn, 0);
+
+ // iterate over constructors
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ Node n = Node::fromExpr(dt[i].getSygusOp());
+ if (n.getKind() != kind::BUILTIN && n.isConst())
+ {
+ d_const_list[tn].push_back(n);
+ if (ck != UNDEFINED_KIND && doCompare(z, n, ck))
+ {
+ d_const_list_pos[tn]++;
+ }
+ }
+ if (dt[i].isSygusIdFunc())
+ {
+ d_id_funcs[tn].push_back(i);
+ }
+ }
+ // sort the constant list
+ if (!d_const_list[tn].empty())
+ {
+ if (ck != UNDEFINED_KIND)
+ {
+ sortConstants sc;
+ sc.d_comp_kind = ck;
+ std::sort(d_const_list[tn].begin(), d_const_list[tn].end(), sc);
+ }
+ Trace("csi-rcons") << "Type has " << d_const_list[tn].size()
+ << " constants..." << std::endl
+ << " ";
+ for (unsigned i = 0; i < d_const_list[tn].size(); i++)
+ {
+ Trace("csi-rcons") << d_const_list[tn][i] << " ";
+ }
+ Trace("csi-rcons") << std::endl;
+ Trace("csi-rcons") << "Of these, " << d_const_list_pos[tn]
+ << " are marked as positive." << std::endl;
+ }
+}
+
+bool CegConjectureSingleInvSol::getMatch(Node p,
+ Node n,
+ std::map<int, Node>& s,
+ std::vector<int>& new_s)
+{
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ if (tds->isFreeVar(p))
+ {
+ unsigned vnum = tds->getVarNum(p);
+ Node prev = s[vnum];
+ s[vnum] = n;
+ if (prev.isNull())
+ {
+ new_s.push_back(vnum);
+ }
+ return prev.isNull() || prev == n;
+ }
+ if (n.getNumChildren() == 0)
+ {
+ return p == n;
+ }
+ if (n.getKind() == p.getKind() && n.getNumChildren() == p.getNumChildren())
+ {
+ // try both ways?
+ unsigned rmax =
+ TermUtil::isComm(n.getKind()) && n.getNumChildren() == 2 ? 2 : 1;
+ std::vector<int> new_tmp;
+ for (unsigned r = 0; r < rmax; r++)
+ {
+ bool success = true;
+ for (unsigned i = 0, size = n.getNumChildren(); i < size; i++)
+ {
+ int io = r == 0 ? i : (i == 0 ? 1 : 0);
+ if (!getMatch(p[i], n[io], s, new_tmp))
+ {
+ success = false;
+ for (unsigned j = 0; j < new_tmp.size(); j++)
+ {
+ s.erase(new_tmp[j]);
+ }
+ new_tmp.clear();
+ break;
+ }
+ }
+ if (success)
+ {
+ new_s.insert(new_s.end(), new_tmp.begin(), new_tmp.end());
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool CegConjectureSingleInvSol::getMatch(Node t,
+ TypeNode st,
+ int& index_found,
+ std::vector<Node>& args,
+ int index_exc,
+ int index_start)
+{
+ Assert(st.isDatatype());
+ const Datatype& dt = static_cast<DatatypeType>(st.toType()).getDatatype();
+ Assert(dt.isSygus());
+ std::map<Kind, std::vector<Node> > kgens;
+ std::vector<Node> gens;
+ for (unsigned i = index_start, ncons = dt.getNumConstructors(); i < ncons;
+ i++)
+ {
+ if ((int)i != index_exc)
+ {
+ Node g = getGenericBase(st, dt, i);
+ gens.push_back(g);
+ kgens[g.getKind()].push_back(g);
+ Trace("csi-sol-debug") << "Check generic base : " << g << " from "
+ << dt[i].getName() << std::endl;
+ if (g.getKind() == t.getKind())
+ {
+ Trace("csi-sol-debug") << "Possible match ? " << g << " " << t
+ << " for " << dt[i].getName() << std::endl;
+ std::map<int, Node> sigma;
+ std::vector<int> new_s;
+ if (getMatch(g, t, sigma, new_s))
+ {
+ // we found an exact match
+ bool msuccess = true;
+ for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
+ {
+ if (sigma[j].isNull())
+ {
+ msuccess = false;
+ break;
+ }
+ else
+ {
+ args.push_back(sigma[j]);
+ }
+ }
+ if (msuccess)
+ {
+ index_found = i;
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+Node CegConjectureSingleInvSol::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())
+ {
+ return it->second;
+ }
+ TermDbSygus* tds = d_qe->getTermDatabaseSygus();
+ Assert(tds->isRegistered(tn));
+ std::map<TypeNode, int> var_count;
+ std::map<int, Node> pre;
+ Node g = tds->mkGeneric(dt, c, var_count, pre);
+ Trace("csi-sol-debug") << "Generic is " << g << std::endl;
+ Node gr = Rewriter::rewrite(g);
+ Trace("csi-sol-debug") << "Generic rewritten is " << gr << std::endl;
+ d_generic_base[tn][c] = gr;
+ return gr;
+}
+}
+}
+}
diff --git a/src/theory/quantifiers/sygus/ce_guided_single_inv_sol.h b/src/theory/quantifiers/sygus/ce_guided_single_inv_sol.h
new file mode 100644
index 000000000..7043e1ecf
--- /dev/null
+++ b/src/theory/quantifiers/sygus/ce_guided_single_inv_sol.h
@@ -0,0 +1,191 @@
+/********************* */
+/*! \file ce_guided_single_inv_sol.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds, Paul Meng
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief utility for reconstructing solutions for single invocation synthesis conjectures
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_SOL_H
+#define __CVC4__THEORY__QUANTIFIERS__CE_GUIDED_SINGLE_INV_SOL_H
+
+#include "context/cdhashmap.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+
+class CegConjectureSingleInv;
+
+/** CegConjectureSingleInvSol
+ *
+ * This function implements Figure 5 of "Counterexample-Guided Quantifier
+ * Instantiation for Synthesis in SMT", Reynolds et al CAV 2015.
+ *
+ */
+class CegConjectureSingleInvSol
+{
+ friend class CegConjectureSingleInv;
+private:
+ QuantifiersEngine * d_qe;
+ std::vector< Node > d_varList;
+ std::map< Node, int > d_dterm_size;
+ std::map< Node, int > d_dterm_ite_size;
+//solution simplification
+private:
+ bool debugSolution( Node sol );
+ void debugTermSize( Node sol, int& t_size, int& num_ite );
+ Node pullITEs( Node n );
+ bool pullITECondition( Node root, Node n, std::vector< Node >& conj, Node& t, Node& rem, int depth );
+ Node flattenITEs( Node n, bool rec = true );
+ bool getAssign( bool pol, Node n, std::map< Node, bool >& assign, std::vector< Node >& new_assign,
+ std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs );
+ bool getAssignEquality( Node eq, std::vector< Node >& vars, std::vector< Node >& new_vars, std::vector< Node >& new_subs );
+ Node simplifySolutionNode( Node sol, TypeNode stn, std::map< Node, bool >& assign,
+ std::vector< Node >& vars, std::vector< Node >& subs, int status );
+
+ public:
+ CegConjectureSingleInvSol(QuantifiersEngine* qe);
+ /** simplify solution
+ *
+ * Returns the simplified version of node sol whose syntax is restricted by
+ * the grammar corresponding to sygus datatype stn.
+ */
+ Node simplifySolution( Node sol, TypeNode stn );
+ /** reconstruct solution
+ *
+ * Returns (if possible) a node that is equivalent to sol those syntax
+ * matches the grammar corresponding to sygus datatype stn.
+ * The value reconstructed is set to 1 if we successfully return a node,
+ * otherwise it is set to -1.
+ */
+ Node reconstructSolution(Node sol, TypeNode stn, int& reconstructed);
+ /** preregister conjecture
+ *
+ * q : the synthesis conjecture this class is for.
+ * This is used as a heuristic to find terms in the original conjecture which
+ * may be helpful for using during reconstruction.
+ */
+ void preregisterConjecture(Node q);
+
+ private:
+ int d_id_count;
+ int d_root_id;
+ std::map< int, Node > d_id_node;
+ std::map< int, TypeNode > d_id_type;
+ std::map< TypeNode, std::map< Node, int > > d_rcons_to_id;
+ std::map< TypeNode, std::map< Node, int > > d_rcons_to_status;
+
+ std::map< int, std::map< Node, std::vector< int > > > d_reconstruct_op;
+ std::map< int, Node > d_reconstruct;
+ std::map< int, std::vector< int > > d_parents;
+
+ std::map< int, std::vector< int > > d_eqc;
+ std::map< int, int > d_rep;
+
+ //equivalent terms
+ std::map< Node, Node > d_eqt_rep;
+ std::map< Node, std::vector< Node > > d_eqt_eqc;
+
+ //cache when reconstructing solutions
+ std::vector< int > d_tmp_fail;
+ // get reconstructed solution
+ Node getReconstructedSolution( int id, bool mod_eq = true );
+
+ // allocate node with type
+ int allocate( Node n, TypeNode stn );
+ // term t with sygus type st, returns inducted templated form of t
+ int collectReconstructNodes( Node t, TypeNode stn, int& status );
+ bool collectReconstructNodes( int pid, std::vector< Node >& ts, const DatatypeConstructor& dtc, std::vector< int >& ids, int& status );
+ bool getPathToRoot( int id );
+ void setReconstructed( int id, Node n );
+ //get equivalent terms to n with top symbol k
+ void getEquivalentTerms( Kind k, Node n, std::vector< Node >& equiv );
+ //register equivalent terms
+ void registerEquivalentTerms( Node n );
+ /** builtin to sygus const
+ *
+ * Returns a sygus term of type tn that encodes the builtin constant c.
+ * If the sygus datatype tn allows any constant, this may return a variable
+ * with the attribute SygusPrintProxyAttribute that associates it with c.
+ *
+ * rcons_depth limits the number of recursive calls when doing accelerated
+ * constant reconstruction (currently limited to 1000). Notice this is hacky:
+ * depending upon order of calls, constant rcons may succeed, e.g. 1001, 999
+ * vs. 999, 1001.
+ */
+ Node builtinToSygusConst(Node c, TypeNode tn, int rcons_depth = 0);
+ /** cache for the above function */
+ std::map<TypeNode, std::map<Node, Node> > d_builtin_const_to_sygus;
+ /** sorted list of constants, per type */
+ std::map<TypeNode, std::vector<Node> > d_const_list;
+ /** number of positive constants, per type */
+ std::map<TypeNode, unsigned> d_const_list_pos;
+ /** list of constructor indices whose operators are identity functions */
+ std::map<TypeNode, std::vector<int> > d_id_funcs;
+ /** initialize the above information for sygus type tn */
+ void registerType(TypeNode tn);
+ /** get generic base
+ *
+ * This returns the builtin term that is the analog of an application of the
+ * c^th constructor of dt to fresh variables.
+ */
+ Node getGenericBase(TypeNode tn, const Datatype& dt, int c);
+ /** cache for the above function */
+ std::map<TypeNode, std::map<int, Node> > d_generic_base;
+ /** get match
+ *
+ * This function attempts to find a substitution for which p = n. If
+ * successful, this function returns a substitution in the form of s/new_s,
+ * where:
+ * s : substitution, where the domain are indices of terms in the sygus
+ * term database, and
+ * new_s : the members that were added to s on this call.
+ * Otherwise, this function returns false and s and new_s are unmodified.
+ */
+ bool getMatch(Node p,
+ Node n,
+ std::map<int, Node>& s,
+ std::vector<int>& new_s);
+ /** get match
+ *
+ * This function attempts to find a builtin term that is analog to a value
+ * of the sygus datatype st that is equivalent to n. If this function returns
+ * true, then it has found such a term. Then we set:
+ * index_found : updated to the constructor index of the sygus term whose
+ * analog to equivalent to n.
+ * args : builtin terms corresponding to the match, in order.
+ * Otherwise, this function returns false and index_found and args are
+ * unmodified.
+ * For example, for grammar:
+ * A -> 0 | 1 | x | +( A, A )
+ * Given input ( 5 + (x+1) ) and A we would return true, where:
+ * index_found is set to 3 and args is set to { 5, x+1 }.
+ *
+ * index_exc : (if applicable) exclude a constructor index of st
+ * index_start : start index of constructors of st to try
+ */
+ bool getMatch(Node n,
+ TypeNode st,
+ int& index_found,
+ std::vector<Node>& args,
+ int index_exc = -1,
+ int index_start = 0);
+};
+
+
+}
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/sygus/sygus_explain.cpp b/src/theory/quantifiers/sygus/sygus_explain.cpp
new file mode 100644
index 000000000..aafaa07e1
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_explain.cpp
@@ -0,0 +1,301 @@
+/********************* */
+/*! \file sygus_explain.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of techniques for sygus explanations
+ **/
+
+#include "theory/quantifiers/sygus/sygus_explain.h"
+
+#include "theory/datatypes/datatypes_rewriter.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+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 r)
+{
+ Assert(!d_term.empty());
+ unsigned curr = d_term.size() - 1;
+ unsigned o = d_has_op[curr] ? 1 : 0;
+ d_children[curr][i + o] = r;
+}
+
+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);
+}
+
+void SygusExplain::getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::vector<Node>& exp)
+{
+ std::map<unsigned, bool> cexc;
+ getExplanationForConstantEquality(n, vn, exp, cexc);
+}
+
+void SygusExplain::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 SygusExplain::getExplanationForConstantEquality(Node n, Node vn)
+{
+ std::map<unsigned, bool> cexc;
+ return getExplanationForConstantEquality(n, vn, cexc);
+}
+
+Node SygusExplain::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 SygusExplain::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 that child by a fresh variable
+ // also satisfies the invariance test.
+ for (unsigned i = 0; i < vn.getNumChildren(); i++)
+ {
+ TypeNode xtn = vn[i].getType();
+ Node x = d_tdb->getFreeVarInc(xtn, var_count);
+ trb.replaceChild(i, x);
+ Node nvn = trb.build();
+ Assert(nvn.getKind() == kind::APPLY_CONSTRUCTOR);
+ if (et.is_invariant(d_tdb, nvn, x))
+ {
+ cexc[i] = true;
+ // we are tracking term size if positive
+ if (sz >= 0)
+ {
+ int s = d_tdb->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 = NodeManager::currentNM()->mkConst(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 SygusExplain::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 SygusExplain::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);
+}
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
diff --git a/src/theory/quantifiers/sygus/sygus_explain.h b/src/theory/quantifiers/sygus/sygus_explain.h
new file mode 100644
index 000000000..ad26f29e4
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_explain.h
@@ -0,0 +1,222 @@
+/********************* */
+/*! \file sygus_explain.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief sygus explanations
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H
+
+#include <vector>
+
+#include "expr/node.h"
+#include "theory/quantifiers/sygus/sygus_invariance.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** Recursive term builder
+ *
+ * This is a utility used to generate variants
+ * of a term n, where subterms of n can be replaced
+ * by others via calls to replaceChild(...).
+ *
+ * This class maintains a "context", which indicates
+ * a position in term n. Below, we call the subterm of
+ * the initial term n at this position the "active term".
+ *
+ */
+class TermRecBuild
+{
+ public:
+ TermRecBuild() {}
+ /** set the initial term to n
+ *
+ * The context initially empty, that is,
+ * the active term is initially n.
+ */
+ void init(Node n);
+
+ /** push the context
+ *
+ * This updates the context so that the
+ * active term is updated to curr[p], where
+ * curr is the previously active term.
+ */
+ void push(unsigned p);
+
+ /** pop the context */
+ void pop();
+ /** indicates that the i^th child of the active
+ * term should be replaced by r in calls to build().
+ */
+ void replaceChild(unsigned i, Node r);
+ /** get the i^th child of the active term */
+ Node getChild(unsigned i);
+ /** build the (modified) version of the term
+ * we intialized via the call to init().
+ */
+ Node build(unsigned p = 0);
+
+ private:
+ /** stack of active terms */
+ std::vector<Node> d_term;
+ /** stack of children of active terms
+ * Notice that these may be modified with calls to replaceChild(...).
+ */
+ std::vector<std::vector<Node> > d_children;
+ /** stack the kind of active terms */
+ std::vector<Kind> d_kind;
+ /** stack of whether the active terms had an operator */
+ std::vector<bool> d_has_op;
+ /** stack of positions that were pushed via calls to push(...) */
+ std::vector<unsigned> d_pos;
+ /** add term to the context stack */
+ void addTerm(Node n);
+};
+
+/*The SygusExplain utility
+ *
+ * This class is used to produce explanations for refinement lemmas
+ * in the counterexample-guided inductive synthesis (CEGIS) loop.
+ *
+ * When given an invariance test T traverses the AST of a given term,
+ * uses TermRecBuild to replace various subterms by fresh variables and
+ * recheck whether the invariant, as specified by T still holds.
+ * If it does, then we may exclude the explanation for that subterm.
+ *
+ * For example, say we have that the current value of
+ * (datatype) sygus term n is:
+ * (if (gt x 0) 0 0)
+ * where if, gt, x, 0 are datatype constructors.
+ * The explanation returned by getExplanationForConstantEquality
+ * below for n and the above term is:
+ * { ((_ is if) n), ((_ is geq) n.0),
+ * ((_ is x) n.0.0), ((_ is 0) n.0.1),
+ * ((_ is 0) n.1), ((_ is 0) n.2) }
+ *
+ * This class can also return more precise
+ * explanations based on a property that holds for
+ * variants of n. For instance,
+ * say we find that n's builtin analog rewrites to 0:
+ * ite( x>0, 0, 0 ) ----> 0
+ * and we would like to find the minimal explanation for
+ * why the builtin analog of n rewrites to 0.
+ * We use the invariance test EquivSygusInvarianceTest
+ * (see sygus_invariance.h) for doing this.
+ * Using the SygusExplain::getExplanationFor method below,
+ * this will invoke the invariant test to check, e.g.
+ * ite( x>0, 0, y1 ) ----> 0 ? fail
+ * ite( x>0, y2, 0 ) ----> 0 ? fail
+ * ite( y3, 0, 0 ) ----> 0 ? success
+ * where y1, y2, y3 are fresh variables.
+ * Hence the explanation for the condition x>0 is irrelevant.
+ * This gives us the explanation:
+ * { ((_ is if) n), ((_ is 0) n.1), ((_ is 0) n.2) }
+ * indicating that all terms of the form:
+ * (if _ 0 0) have a builtin equivalent that rewrites to 0.
+ *
+ * For details, see Reynolds et al SYNT 2017.
+ *
+ * Below, we let [[exp]]_n denote the term induced by
+ * the explanation exp for n.
+ * For example:
+ * exp = { ((_ is plus) n), ((_ is y) n.1) }
+ * is such that:
+ * [[exp]]_n = (plus w y)
+ * where w is a fresh variable.
+ */
+class SygusExplain
+{
+ public:
+ SygusExplain(TermDbSygus* tdb) : d_tdb(tdb) {}
+ ~SygusExplain() {}
+ /** get explanation for constant equality
+ *
+ * This function constructs an explanation, stored in exp, such that:
+ * - All formulas in exp are of the form ((_ is C) ns), where ns
+ * is a chain of selectors applied to n, and
+ * - exp => ( n = vn )
+ */
+ void getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::vector<Node>& exp);
+ /** returns the conjunction of exp computed in the above function */
+ Node getExplanationForConstantEquality(Node n, Node vn);
+
+ /** get explanation for constant equality
+ * This is identical to the above function except that we
+ * take an additional argument cexc, which says which
+ * children of vn should be excluded from the explanation.
+ *
+ * For example, if vn = plus( plus( x, x ), y ) and cexc is { 0 -> true },
+ * then the following is appended to exp :
+ * { ((_ is plus) n), ((_ is y) n.1) }
+ * where notice that the 0^th argument of vn is excluded.
+ */
+ void getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::vector<Node>& exp,
+ std::map<unsigned, bool>& cexc);
+ /** returns the conjunction of exp computed in the above function */
+ Node getExplanationForConstantEquality(Node n,
+ Node vn,
+ std::map<unsigned, bool>& cexc);
+
+ /** get explanation for
+ *
+ * This function constructs an explanation, stored in exp, such that:
+ * - All formulas in exp are of the form ((_ is C) ns), where ns
+ * is a chain of selectors applied to n, and
+ * - The test et holds for [[exp]]_n, and
+ * - (if applicable) exp => ( n != vnr ).
+ *
+ * This function updates sz to be the term size of [[exp]]_n.
+ */
+ 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);
+
+ private:
+ /** sygus term database associated with this utility */
+ TermDbSygus* d_tdb;
+ /** Helper function for getExplanationFor
+ * var_count is the number of free variables we have introduced,
+ * per type, for the purposes of generalizing subterms of n.
+ * vnr_exp stores the explanation, if one exists, for
+ * n != vnr. It is only non-null if vnr is non-null.
+ */
+ 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);
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_EXPLAIN_H */
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp
new file mode 100644
index 000000000..1ca774c5d
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp
@@ -0,0 +1,693 @@
+/********************* */
+/*! \file sygus_grammar_cons.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief implementation of class for constructing inductive datatypes that correspond to
+ ** grammars that encode syntactic restrictions for SyGuS.
+ **/
+#include "theory/quantifiers/sygus/sygus_grammar_cons.h"
+
+#include <stack>
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers/sygus/sygus_process_conj.h"
+#include "theory/quantifiers/sygus/sygus_grammar_norm.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+CegGrammarConstructor::CegGrammarConstructor(QuantifiersEngine* qe,
+ CegConjecture* p)
+ : d_qe(qe), d_parent(p), d_is_syntax_restricted(false), d_has_ite(true)
+{
+}
+
+void CegGrammarConstructor::collectTerms( Node n, std::map< TypeNode, std::vector< Node > >& consts ){
+ std::unordered_map<TNode, bool, TNodeHashFunction> visited;
+ std::unordered_map<TNode, bool, TNodeHashFunction>::iterator it;
+ std::stack<TNode> visit;
+ TNode cur;
+ visit.push(n);
+ do {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+ if (it == visited.end()) {
+ visited[cur] = true;
+ // is this a constant?
+ if( cur.isConst() ){
+ TypeNode tn = cur.getType();
+ Node c = cur;
+ if( tn.isReal() ){
+ c = NodeManager::currentNM()->mkConst( c.getConst<Rational>().abs() );
+ }
+ if( std::find( consts[tn].begin(), consts[tn].end(), c )==consts[tn].end() ){
+ Trace("cegqi-debug") << "...consider const : " << c << std::endl;
+ consts[tn].push_back( c );
+ }
+ }
+ // recurse
+ for (unsigned i = 0; i < cur.getNumChildren(); i++) {
+ visit.push(cur[i]);
+ }
+ }
+ } while (!visit.empty());
+}
+
+
+
+Node CegGrammarConstructor::process( Node q, std::map< Node, Node >& templates, std::map< Node, Node >& templates_arg ) {
+ // convert to deep embedding and finalize single invocation here
+ // now, construct the grammar
+ Trace("cegqi") << "CegConjecture : convert to deep embedding..." << std::endl;
+ std::map< TypeNode, std::vector< Node > > extra_cons;
+ if( options::sygusAddConstGrammar() ){
+ Trace("cegqi") << "CegConjecture : collect constants..." << std::endl;
+ collectTerms( q[1], extra_cons );
+ }
+
+ std::vector< Node > qchildren;
+ std::map< Node, Node > synth_fun_vars;
+ std::vector< Node > ebvl;
+ Node qbody_subs = q[1];
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node sf = q[0][i];
+ // v encodes the syntactic restrictions (via an inductive datatype) on sf
+ // from the input
+ Node v = sf.getAttribute(SygusSynthGrammarAttribute());
+ Assert(!v.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;
+ std::stringstream ss;
+ ss << sf;
+ if( v.getType().isDatatype() && ((DatatypeType)v.getType().toType()).getDatatype().isSygus() ){
+ tn = v.getType();
+ }else{
+ // check which arguments are irrelevant
+ std::unordered_set<unsigned> arg_irrelevant;
+ d_parent->getProcess()->getIrrelevantArgs(sf, arg_irrelevant);
+ std::unordered_set<Node, NodeHashFunction> term_irrelevant;
+ // convert to term
+ for (std::unordered_set<unsigned>::iterator ita = arg_irrelevant.begin();
+ ita != arg_irrelevant.end();
+ ++ita)
+ {
+ unsigned arg = *ita;
+ Assert(arg < sfvl.getNumChildren());
+ term_irrelevant.insert(sfvl[arg]);
+ }
+
+ // make the default grammar
+ tn = mkSygusDefaultType(
+ v.getType(), sfvl, ss.str(), extra_cons, term_irrelevant);
+ }
+ // normalize type
+ SygusGrammarNorm sygus_norm(d_qe);
+ tn = sygus_norm.normalizeSygusType(tn, sfvl);
+ // check if there is a template
+ std::map< Node, Node >::iterator itt = templates.find( sf );
+ if( itt!=templates.end() ){
+ Node templ = itt->second;
+ TNode templ_arg = templates_arg[sf];
+ Assert( !templ_arg.isNull() );
+ Trace("cegqi-debug") << "Template for " << sf << " is : " << templ << " with arg " << templ_arg << std::endl;
+ // if there is a template for this argument, make a sygus type on top of it
+ if( options::sygusTemplEmbedGrammar() ){
+ Trace("cegqi-debug") << " embed this template as a grammar..." << std::endl;
+ tn = mkSygusTemplateType( templ, templ_arg, tn, sfvl, ss.str() );
+ }else{
+ // otherwise, apply it as a preprocessing pass
+ Trace("cegqi-debug") << " apply this template as a substituion during preprocess..." << std::endl;
+ std::vector< Node > schildren;
+ std::vector< Node > largs;
+ for( unsigned j=0; j<sfvl.getNumChildren(); j++ ){
+ schildren.push_back( sfvl[j] );
+ largs.push_back( NodeManager::currentNM()->mkBoundVar( sfvl[j].getType() ) );
+ }
+ std::vector< Node > subsfn_children;
+ subsfn_children.push_back( sf );
+ subsfn_children.insert( subsfn_children.end(), schildren.begin(), schildren.end() );
+ Node subsfn = NodeManager::currentNM()->mkNode( kind::APPLY_UF, subsfn_children );
+ TNode subsf = subsfn;
+ Trace("cegqi-debug") << " substitute arg : " << templ_arg << " -> " << subsf << std::endl;
+ templ = templ.substitute( templ_arg, subsf );
+ // substitute lambda arguments
+ templ = templ.substitute( schildren.begin(), schildren.end(), largs.begin(), largs.end() );
+ Node subsn = NodeManager::currentNM()->mkNode( kind::LAMBDA, NodeManager::currentNM()->mkNode( BOUND_VAR_LIST, largs ), templ );
+ TNode var = sf;
+ TNode subs = subsn;
+ Trace("cegqi-debug") << " substitute : " << var << " -> " << subs << std::endl;
+ qbody_subs = qbody_subs.substitute( var, subs );
+ Trace("cegqi-debug") << " body is now : " << qbody_subs << std::endl;
+ }
+ }
+ d_qe->getTermDatabaseSygus()->registerSygusType( tn );
+ // check grammar restrictions
+ if( !d_qe->getTermDatabaseSygus()->sygusToBuiltinType( tn ).isBoolean() ){
+ if( !d_qe->getTermDatabaseSygus()->hasKind( tn, ITE ) ){
+ d_has_ite = false;
+ }
+ }
+ Assert( tn.isDatatype() );
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ Assert( dt.isSygus() );
+ if( !dt.getSygusAllowAll() ){
+ d_is_syntax_restricted = true;
+ }
+
+ // ev is the first-order variable corresponding to this synth fun
+ std::stringstream ssf;
+ ssf << "f" << sf;
+ Node ev = NodeManager::currentNM()->mkBoundVar( ssf.str(), tn );
+ ebvl.push_back( ev );
+ synth_fun_vars[sf] = ev;
+ Trace("cegqi") << "...embedding synth fun : " << sf << " -> " << ev << std::endl;
+ }
+ qchildren.push_back( NodeManager::currentNM()->mkNode( kind::BOUND_VAR_LIST, ebvl ) );
+ if( qbody_subs!=q[1] ){
+ Trace("cegqi") << "...rewriting : " << qbody_subs << std::endl;
+ qbody_subs = Rewriter::rewrite( qbody_subs );
+ Trace("cegqi") << "...got : " << qbody_subs << std::endl;
+ }
+ qchildren.push_back( convertToEmbedding( qbody_subs, synth_fun_vars ) );
+ if( q.getNumChildren()==3 ){
+ qchildren.push_back( q[2] );
+ }
+ return NodeManager::currentNM()->mkNode( kind::FORALL, qchildren );
+}
+
+Node CegGrammarConstructor::convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars ){
+ std::unordered_map<TNode, Node, TNodeHashFunction> visited;
+ std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
+ std::stack<TNode> visit;
+ TNode cur;
+ visit.push(n);
+ do {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+ if (it == visited.end()) {
+ visited[cur] = Node::null();
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++) {
+ visit.push(cur[i]);
+ }
+ } else if (it->second.isNull()) {
+ Node ret = cur;
+ Kind ret_k = cur.getKind();
+ Node op;
+ bool childChanged = false;
+ std::vector<Node> children;
+ // get the potential operator
+ if( cur.getNumChildren()>0 ){
+ if( cur.getKind()==kind::APPLY_UF ){
+ op = cur.getOperator();
+ }
+ }else{
+ op = cur;
+ }
+ // is the operator a synth function?
+ if( !op.isNull() ){
+ std::map< Node, Node >::iterator its = synth_fun_vars.find( op );
+ if( its!=synth_fun_vars.end() ){
+ Assert( its->second.getType().isDatatype() );
+ // will make into an application of an evaluation function
+ const Datatype& dt = ((DatatypeType)its->second.getType().toType()).getDatatype();
+ Assert( dt.isSygus() );
+ children.push_back( Node::fromExpr( dt.getSygusEvaluationFunc() ) );
+ children.push_back( its->second );
+ childChanged = true;
+ ret_k = kind::APPLY_UF;
+ }
+ }
+ if( !childChanged ){
+ // otherwise, we apply the previous operator
+ if( cur.getMetaKind() == kind::metakind::PARAMETERIZED ){
+ children.push_back( cur.getOperator() );
+ }
+ }
+ for (unsigned i = 0; i < cur.getNumChildren(); i++) {
+ it = visited.find(cur[i]);
+ Assert(it != visited.end());
+ Assert(!it->second.isNull());
+ childChanged = childChanged || cur[i] != it->second;
+ children.push_back(it->second);
+ }
+ if (childChanged) {
+ ret = NodeManager::currentNM()->mkNode(ret_k, children);
+ }
+ visited[cur] = ret;
+ }
+ } while (!visit.empty());
+ Assert(visited.find(n) != visited.end());
+ Assert(!visited.find(n)->second.isNull());
+ return visited[n];
+}
+
+
+TypeNode CegGrammarConstructor::mkUnresolvedType(const std::string& name, std::set<Type>& unres) {
+ TypeNode unresolved = NodeManager::currentNM()->mkSort(name, ExprManager::SORT_FLAG_PLACEHOLDER);
+ unres.insert( unresolved.toType() );
+ return unresolved;
+}
+
+void CegGrammarConstructor::mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops ) {
+ if (type.isReal())
+ {
+ ops.push_back(NodeManager::currentNM()->mkConst(Rational(0)));
+ ops.push_back(NodeManager::currentNM()->mkConst(Rational(1)));
+ }else if( type.isBitVector() ){
+ unsigned sz = ((BitVectorType)type.toType()).getSize();
+ BitVector bval0(sz, (unsigned int)0);
+ ops.push_back( NodeManager::currentNM()->mkConst(bval0) );
+ BitVector bval1(sz, (unsigned int)1);
+ ops.push_back( NodeManager::currentNM()->mkConst(bval1) );
+ }else if( type.isBoolean() ){
+ ops.push_back(NodeManager::currentNM()->mkConst(true));
+ ops.push_back(NodeManager::currentNM()->mkConst(false));
+ }
+ //TODO : others?
+}
+
+void CegGrammarConstructor::collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels ){
+ if( !range.isBoolean() ){
+ if( std::find( types.begin(), types.end(), range )==types.end() ){
+ Trace("sygus-grammar-def") << "...will make grammar for " << range << std::endl;
+ types.push_back( range );
+ if( range.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)range.toType()).getDatatype();
+ for( unsigned i=0; i<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 CegGrammarConstructor::mkSygusDefaultGrammar(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant,
+ std::vector<CVC4::Datatype>& datatypes,
+ std::set<Type>& unres)
+{
+ Trace("sygus-grammar-def") << "Construct default grammar for " << fun << " "
+ << range << std::endl;
+ // collect the variables
+ std::vector<Node> sygus_vars;
+ if( !bvl.isNull() ){
+ for( unsigned i=0; i<bvl.getNumChildren(); i++ ){
+ if (term_irrelevant.find(bvl[i]) == term_irrelevant.end())
+ {
+ sygus_vars.push_back(bvl[i]);
+ }
+ else
+ {
+ Trace("sygus-grammar-def") << "...synth var " << bvl[i]
+ << " has been marked irrelevant."
+ << std::endl;
+ }
+ }
+ }
+ //if( !range.isBoolean() && !range.isInteger() && !range.isBitVector() && !range.isDatatype() ){
+ // parseError("No default grammar for type.");
+ //}
+ std::vector< std::vector< Expr > > ops;
+ int startIndex = -1;
+ 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].isReal())
+ {
+ for (unsigned j = 0; j < 2; j++)
+ {
+ Kind k = j == 0 ? PLUS : 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);
+ }
+ if (!types[i].isInteger())
+ {
+ Trace("sygus-grammar-def") << "...Dedicate to Real\n";
+ /* Creating type for positive integers */
+ std::stringstream ss;
+ ss << fun << "_PosInt";
+ std::string pos_int_name = ss.str();
+ // make unresolved type
+ Type unres_pos_int_t = mkUnresolvedType(pos_int_name, unres).toType();
+ // make data type
+ datatypes.push_back(Datatype(pos_int_name));
+ /* add placeholders */
+ std::vector<Expr> ops_pos_int;
+ std::vector<std::string> cnames_pos_int;
+ std::vector<std::vector<Type>> cargs_pos_int;
+ /* Add operator 1 */
+ Trace("sygus-grammar-def") << "\t...add for 1 to Pos_Int\n";
+ ops_pos_int.push_back(
+ NodeManager::currentNM()->mkConst(Rational(1)).toExpr());
+ ss << "_1";
+ cnames_pos_int.push_back(ss.str());
+ cargs_pos_int.push_back(std::vector<Type>());
+ /* Add operator PLUS */
+ Kind k = PLUS;
+ Trace("sygus-grammar-def") << "\t...add for PLUS to Pos_Int\n";
+ ops_pos_int.push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames_pos_int.push_back(kindToString(k));
+ cargs_pos_int.push_back(std::vector<Type>());
+ cargs_pos_int.back().push_back(unres_pos_int_t);
+ cargs_pos_int.back().push_back(unres_pos_int_t);
+ datatypes.back().setSygus(types[i].toType(), bvl.toExpr(), true, true);
+ for (unsigned j = 0; j < ops_pos_int.size(); j++)
+ {
+ datatypes.back().addSygusConstructor(
+ ops_pos_int[j], cnames_pos_int[j], cargs_pos_int[j]);
+ }
+ Trace("sygus-grammar-def")
+ << "...built datatype " << datatypes.back() << " ";
+ /* Adding division at root */
+ k = DIVISION;
+ Trace("sygus-grammar-def") << "\t...add for " << k << std::endl;
+ ops[i].push_back(NodeManager::currentNM()->operatorOf(k).toExpr());
+ cnames.push_back(kindToString(k));
+ cargs.push_back(std::vector<Type>());
+ cargs.back().push_back(unres_t);
+ cargs.back().push_back(unres_pos_int_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[i] << 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] );
+ }
+ Trace("sygus-grammar-def")
+ << "...built datatype " << datatypes[i] << " ";
+ //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].isReal())
+ {
+ 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 CegGrammarConstructor::mkSygusDefaultType(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant)
+{
+ 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, term_irrelevant, 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] );
+}
+
+TypeNode CegGrammarConstructor::mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
+ const std::string& fun, unsigned& tcount ) {
+ if( templ==templ_arg ){
+ //Assert( templ_arg.getType()==sygusToBuiltinType( templ_arg_sygus_type ) );
+ return templ_arg_sygus_type;
+ }else{
+ tcount++;
+ std::set<Type> unres;
+ std::vector< CVC4::Datatype > datatypes;
+ std::stringstream ssd;
+ ssd << fun << "_templ_" << tcount;
+ std::string dbname = ssd.str();
+ datatypes.push_back(Datatype(dbname));
+ Node op;
+ std::vector< Type > argTypes;
+ if( templ.getNumChildren()==0 ){
+ // TODO : can short circuit to this case when !TermUtil::containsTerm( templ, templ_arg )
+ op = templ;
+ }else{
+ Assert( templ.hasOperator() );
+ op = templ.getOperator();
+ // make constructor taking arguments types from children
+ for( unsigned i=0; i<templ.getNumChildren(); i++ ){
+ //recursion depth bound by the depth of SyGuS template expressions (low)
+ TypeNode tnc = mkSygusTemplateTypeRec( templ[i], templ_arg, templ_arg_sygus_type, bvl, fun, tcount );
+ argTypes.push_back( tnc.toType() );
+ }
+ }
+ std::stringstream ssdc;
+ ssdc << fun << "_templ_cons_" << tcount;
+ std::string cname = ssdc.str();
+ // we have a single sygus constructor that encodes the template
+ datatypes.back().addSygusConstructor( op.toExpr(), cname, argTypes );
+ datatypes.back().setSygus( templ.getType().toType(), bvl.toExpr(), true, true );
+ std::vector<DatatypeType> types = NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(datatypes, unres);
+ Assert( types.size()==1 );
+ return TypeNode::fromType( types[0] );
+ }
+}
+
+TypeNode CegGrammarConstructor::mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
+ const std::string& fun ) {
+ unsigned tcount = 0;
+ return mkSygusTemplateTypeRec( templ, templ_arg, templ_arg_sygus_type, bvl, fun, tcount );
+}
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.h b/src/theory/quantifiers/sygus/sygus_grammar_cons.h
new file mode 100644
index 000000000..4e486f88f
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.h
@@ -0,0 +1,131 @@
+/********************* */
+/*! \file sygus_grammar_cons.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief class for constructing inductive datatypes that correspond to
+ ** grammars that encode syntactic restrictions for SyGuS.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_CONS_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_CONS_H
+
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegConjecture;
+
+/** utility for constructing datatypes that correspond to syntactic restrictions,
+* and applying the deep embedding from Section 4 of Reynolds et al CAV 2015.
+*/
+class CegGrammarConstructor
+{
+public:
+ CegGrammarConstructor(QuantifiersEngine* qe, CegConjecture* p);
+ ~CegGrammarConstructor() {}
+ /** process
+ * This converts node q based on its deep embedding
+ * (Section 4 of Reynolds et al CAV 2015).
+ * The syntactic restrictions are associated with
+ * the functions-to-synthesize using the attribute
+ * SygusSynthGrammarAttribute.
+ * The arguments templates and template_args
+ * indicate templates for the function to synthesize,
+ * in particular the solution for the i^th function
+ * to synthesis must be of the form
+ * templates[i]{ templates_arg[i] -> t }
+ * for some t if !templates[i].isNull().
+ */
+ Node process(Node q,
+ std::map<Node, Node>& templates,
+ std::map<Node, Node>& templates_arg);
+ /** is the syntax restricted? */
+ bool isSyntaxRestricted() { return d_is_syntax_restricted; }
+ /** does the syntax allow ITE expressions? */
+ bool hasSyntaxITE() { return d_has_ite; }
+ /** make the default sygus datatype type corresponding to builtin type range
+ * bvl is the set of free variables to include in the grammar
+ * fun is for naming
+ * extra_cons is a set of extra constant symbols to include in the grammar
+ * term_irrelevant is a set of terms that should not be included in the
+ * grammar.
+ */
+ static TypeNode mkSygusDefaultType(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant);
+ /** make the default sygus datatype type corresponding to builtin type range */
+ static TypeNode mkSygusDefaultType(TypeNode range,
+ Node bvl,
+ const std::string& fun)
+ {
+ std::map<TypeNode, std::vector<Node> > extra_cons;
+ std::unordered_set<Node, NodeHashFunction> term_irrelevant;
+ return mkSygusDefaultType(range, bvl, fun, extra_cons, term_irrelevant);
+ }
+ /** make the sygus datatype type that encodes the solution space (lambda
+ * templ_arg. templ[templ_arg]) where templ_arg
+ * has syntactic restrictions encoded by sygus type templ_arg_sygus_type
+ * bvl is the set of free variables to include in the grammar
+ * fun is for naming
+ */
+ static TypeNode mkSygusTemplateType( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl, const std::string& fun );
+private:
+ /** reference to quantifier engine */
+ QuantifiersEngine * d_qe;
+ /** parent conjecture
+ * This contains global information about the synthesis conjecture.
+ */
+ CegConjecture* d_parent;
+ /** is the syntax restricted? */
+ bool d_is_syntax_restricted;
+ /** does the syntax allow ITE expressions? */
+ bool d_has_ite;
+ /** collect terms */
+ void collectTerms( Node n, std::map< TypeNode, std::vector< Node > >& consts );
+ /** convert node n based on deep embedding (Section 4 of Reynolds et al CAV 2015) */
+ Node convertToEmbedding( Node n, std::map< Node, Node >& synth_fun_vars );
+ //---------------- grammar construction
+ // helper for mkSygusDefaultGrammar (makes unresolved type for mutually recursive datatype construction)
+ static TypeNode mkUnresolvedType(const std::string& name, std::set<Type>& unres);
+ // make the builtin constants for type type that should be included in a sygus grammar
+ static void mkSygusConstantsForType( TypeNode type, std::vector<CVC4::Node>& ops );
+ // collect the list of types that depend on type range
+ static void collectSygusGrammarTypesFor( TypeNode range, std::vector< TypeNode >& types, std::map< TypeNode, std::vector< DatatypeConstructorArg > >& sels );
+ /** helper function for function mkSygusDefaultType
+ * Collects a set of mutually recursive datatypes "datatypes" corresponding to
+ * encoding type "range" to SyGuS.
+ * unres is used for the resulting call to mkMutualDatatypeTypes
+ */
+ static void mkSygusDefaultGrammar(
+ TypeNode range,
+ Node bvl,
+ const std::string& fun,
+ std::map<TypeNode, std::vector<Node> >& extra_cons,
+ std::unordered_set<Node, NodeHashFunction>& term_irrelevant,
+ std::vector<CVC4::Datatype>& datatypes,
+ std::set<Type>& unres);
+ // helper function for mkSygusTemplateType
+ static TypeNode mkSygusTemplateTypeRec( Node templ, Node templ_arg, TypeNode templ_arg_sygus_type, Node bvl,
+ const std::string& fun, unsigned& tcount );
+ //---------------- end grammar construction
+};
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_norm.cpp b/src/theory/quantifiers/sygus/sygus_grammar_norm.cpp
new file mode 100644
index 000000000..73311b0bd
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_grammar_norm.cpp
@@ -0,0 +1,492 @@
+/********************* */
+/*! \file sygus_grammar_norm.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Haniel Barbosa
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief implementation of class for for simplifying SyGuS grammars after they
+ ** are encoded into datatypes.
+ **/
+
+#include "theory/quantifiers/sygus/sygus_grammar_norm.h"
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "printer/sygus_print_callback.h"
+#include "smt/smt_engine.h"
+#include "smt/smt_engine_scope.h"
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers/sygus/sygus_grammar_red.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+#include <numeric> // for std::iota
+
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+bool OpPosTrie::getOrMakeType(TypeNode tn,
+ TypeNode& unres_tn,
+ const std::vector<unsigned>& op_pos,
+ unsigned ind)
+{
+ if (ind == op_pos.size())
+ {
+ /* Found type */
+ if (!d_unres_tn.isNull())
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tFound type " << d_unres_tn << "\n";
+ unres_tn = d_unres_tn;
+ return true;
+ }
+ /* Creating unresolved type */
+ std::stringstream ss;
+ ss << tn << "_";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ ss << "_" << std::to_string(op_pos[i]);
+ }
+ d_unres_tn = NodeManager::currentNM()->mkSort(
+ ss.str(), ExprManager::SORT_FLAG_PLACEHOLDER);
+ Trace("sygus-grammar-normalize-trie")
+ << "\tCreating type " << d_unres_tn << "\n";
+ unres_tn = d_unres_tn;
+ return false;
+ }
+ /* Go to next node */
+ return d_children[op_pos[ind]].getOrMakeType(tn, unres_tn, op_pos, ind + 1);
+}
+
+void SygusGrammarNorm::TypeObject::addConsInfo(SygusGrammarNorm* sygus_norm,
+ const DatatypeConstructor& cons)
+{
+ Trace("sygus-grammar-normalize") << "...for " << cons.getName() << "\n";
+ /* Recover the sygus operator to not lose reference to the original
+ * operator (NOT, ITE, etc) */
+ Node exp_sop_n = Node::fromExpr(
+ smt::currentSmtEngine()->expandDefinitions(cons.getSygusOp()));
+ d_ops.push_back(Rewriter::rewrite(exp_sop_n));
+ Trace("sygus-grammar-normalize-defs")
+ << "\tOriginal op: " << cons.getSygusOp()
+ << "\n\tExpanded one: " << exp_sop_n
+ << "\n\tRewritten one: " << d_ops.back() << "\n\n";
+ d_cons_names.push_back(cons.getName());
+ d_pc.push_back(cons.getSygusPrintCallback());
+ d_weight.push_back(cons.getWeight());
+ d_cons_args_t.push_back(std::vector<Type>());
+ for (const DatatypeConstructorArg& arg : cons)
+ {
+ /* Collect unresolved type nodes corresponding to the typenode of the
+ * arguments */
+ d_cons_args_t.back().push_back(
+ sygus_norm
+ ->normalizeSygusRec(TypeNode::fromType(
+ static_cast<SelectorType>(arg.getType()).getRangeType()))
+ .toType());
+ }
+}
+
+void SygusGrammarNorm::TypeObject::buildDatatype(SygusGrammarNorm* sygus_norm,
+ const Datatype& dt)
+{
+ /* Use the sygus type to not lose reference to the original types (Bool,
+ * Int, etc) */
+ d_dt.setSygus(dt.getSygusType(),
+ sygus_norm->d_sygus_vars.toExpr(),
+ dt.getSygusAllowConst(),
+ dt.getSygusAllowAll());
+ for (unsigned i = 0, size_d_ops = d_ops.size(); i < size_d_ops; ++i)
+ {
+ d_dt.addSygusConstructor(d_ops[i].toExpr(),
+ d_cons_names[i],
+ d_cons_args_t[i],
+ d_pc[i],
+ d_weight[i]);
+ }
+ Trace("sygus-grammar-normalize") << "...built datatype " << d_dt << " ";
+ /* Add to global accumulators */
+ sygus_norm->d_dt_all.push_back(d_dt);
+ sygus_norm->d_unres_t_all.insert(d_unres_tn.toType());
+ Trace("sygus-grammar-normalize") << "---------------------------------\n";
+}
+
+void SygusGrammarNorm::TransfDrop::buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos)
+{
+ std::vector<unsigned> difference;
+ std::set_difference(op_pos.begin(),
+ op_pos.end(),
+ d_drop_indices.begin(),
+ d_drop_indices.end(),
+ std::back_inserter(difference));
+ op_pos = difference;
+}
+
+/* TODO #1304: have more operators and types. Moreover, have more general ways
+ of finding kind of operator, e.g. if op is (\lambda xy. x + y) this
+ function should realize that it is chainable for integers */
+bool SygusGrammarNorm::TransfChain::isChainable(TypeNode tn, Node op)
+{
+ /* Checks whether operator occurs chainable for its type */
+ if (tn.isInteger() && NodeManager::currentNM()->operatorToKind(op) == PLUS)
+ {
+ return true;
+ }
+ return false;
+}
+
+/* TODO #1304: have more operators and types. Moreover, have more general ways
+ of finding kind of operator, e.g. if op is (\lambda xy. x + y) this
+ function should realize that it is chainable for integers */
+bool SygusGrammarNorm::TransfChain::isId(TypeNode tn, Node op, Node n)
+{
+ if (tn.isInteger() && NodeManager::currentNM()->operatorToKind(op) == PLUS
+ && n == TermUtil::mkTypeValue(tn, 0))
+ {
+ return true;
+ }
+ return false;
+}
+
+void SygusGrammarNorm::TransfChain::buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ std::vector<unsigned> claimed(d_elem_pos);
+ claimed.push_back(d_chain_op_pos);
+ unsigned nb_op_pos = op_pos.size();
+ /* TODO do this properly */
+ /* Remove from op_pos the positions claimed by the transformation */
+ std::sort(op_pos.begin(), op_pos.end());
+ std::sort(claimed.begin(), claimed.end());
+ std::vector<unsigned> difference;
+ std::set_difference(op_pos.begin(),
+ op_pos.end(),
+ claimed.begin(),
+ claimed.end(),
+ std::back_inserter(difference));
+ op_pos = difference;
+ if (Trace.isOn("sygus-grammar-normalize-chain"))
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << "OP at " << d_chain_op_pos << "\n"
+ << d_elem_pos.size() << " d_elem_pos: ";
+ for (unsigned i = 0, size = d_elem_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-chain") << d_elem_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-chain")
+ << "\n"
+ << op_pos.size() << " remaining op_pos: ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-chain") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-chain") << "\n";
+ }
+ /* Build identity operator and empty callback */
+ Node iden_op =
+ SygusGrammarNorm::getIdOp(TypeNode::fromType(dt.getSygusType()));
+ /* If all operators are claimed, create a monomial */
+ if (nb_op_pos == d_elem_pos.size() + 1)
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << "\tCreating id type for " << d_elem_pos.back() << "\n";
+ /* creates type for element */
+ std::vector<unsigned> tmp;
+ tmp.push_back(d_elem_pos.back());
+ Type t = sygus_norm->normalizeSygusRec(to.d_tn, dt, tmp).toType();
+ /* consumes element */
+ d_elem_pos.pop_back();
+ /* adds to Root: "type" */
+ to.d_ops.push_back(iden_op);
+ to.d_cons_names.push_back("id");
+ to.d_pc.push_back(printer::SygusEmptyPrintCallback::getEmptyPC());
+ /* Identity operators should not increase the size of terms */
+ to.d_weight.push_back(0);
+ to.d_cons_args_t.push_back(std::vector<Type>());
+ to.d_cons_args_t.back().push_back(t);
+ Trace("sygus-grammar-normalize-chain")
+ << "\tAdding " << t << " to " << to.d_unres_tn << "\n";
+ /* adds to Root: "type + Root" */
+ to.d_ops.push_back(nm->operatorOf(PLUS));
+ to.d_cons_names.push_back(kindToString(PLUS));
+ to.d_pc.push_back(nullptr);
+ to.d_weight.push_back(-1);
+ to.d_cons_args_t.push_back(std::vector<Type>());
+ to.d_cons_args_t.back().push_back(t);
+ to.d_cons_args_t.back().push_back(to.d_unres_tn.toType());
+ Trace("sygus-grammar-normalize-chain")
+ << "\tAdding PLUS to " << to.d_unres_tn << " with arg types "
+ << to.d_unres_tn << " and " << t << "\n";
+ }
+ /* In the initial case if not all operators claimed always creates a next */
+ Assert(nb_op_pos != d_elem_pos.size() + 1 || d_elem_pos.size() > 1);
+ /* TODO #1304: consider case in which CHAIN op has different types than
+ to.d_tn */
+ /* If no more elements to chain, finish */
+ if (d_elem_pos.size() == 0)
+ {
+ return;
+ }
+ /* Creates a type do be added to root representing next step in the chain */
+ /* Add + to elems */
+ d_elem_pos.push_back(d_chain_op_pos);
+ if (Trace.isOn("sygus-grammar-normalize-chain"))
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << "\tCreating type for next entry with sygus_ops ";
+ for (unsigned i = 0, size = d_elem_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-chain")
+ << dt[d_elem_pos[i]].getSygusOp() << " ";
+ }
+ Trace("sygus-grammar-normalize-chain") << "\n";
+ }
+ /* adds to Root: (\lambda x. x ) Next */
+ to.d_ops.push_back(iden_op);
+ to.d_cons_names.push_back("id_next");
+ to.d_pc.push_back(printer::SygusEmptyPrintCallback::getEmptyPC());
+ to.d_weight.push_back(0);
+ to.d_cons_args_t.push_back(std::vector<Type>());
+ to.d_cons_args_t.back().push_back(
+ sygus_norm->normalizeSygusRec(to.d_tn, dt, d_elem_pos).toType());
+}
+
+std::map<TypeNode, Node> SygusGrammarNorm::d_tn_to_id = {};
+
+/* Traverse the constructors of dt according to the positions in op_pos. Collect
+ * those that fit the kinds established by to_collect. Remove collected operator
+ * positions from op_pos. Accumulate collected positions in collected
+ *
+ * returns true if collected anything
+ */
+std::unique_ptr<SygusGrammarNorm::Transf> SygusGrammarNorm::inferTransf(
+ TypeNode tn, const Datatype& dt, const std::vector<unsigned>& op_pos)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ TypeNode sygus_tn = TypeNode::fromType(dt.getSygusType());
+ Trace("sygus-gnorm") << "Infer transf for " << dt.getName() << "..."
+ << std::endl;
+ Trace("sygus-gnorm") << " #cons = " << op_pos.size() << " / "
+ << dt.getNumConstructors() << std::endl;
+ // look for redundant constructors to drop
+ if (options::sygusMinGrammar() && dt.getNumConstructors() == op_pos.size())
+ {
+ SygusRedundantCons src;
+ src.initialize(d_qe, tn);
+ std::vector<unsigned> rindices;
+ src.getRedundant(rindices);
+ if (!rindices.empty())
+ {
+ Trace("sygus-gnorm") << "...drop transf, " << rindices.size() << "/"
+ << op_pos.size() << " constructors." << std::endl;
+ Assert(rindices.size() < op_pos.size());
+ return std::unique_ptr<Transf>(new TransfDrop(rindices));
+ }
+ }
+
+ // if normalization option is not enabled, we do not infer the transformations
+ // below
+ if (!options::sygusGrammarNorm())
+ {
+ return nullptr;
+ }
+
+ /* TODO #1304: step 1: look for singleton */
+ /* step 2: look for chain */
+ unsigned chain_op_pos = dt.getNumConstructors();
+ std::vector<unsigned> elem_pos;
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Assert(op_pos[i] < dt.getNumConstructors());
+ Expr sop = dt[op_pos[i]].getSygusOp();
+ /* Collects a chainable operator such as PLUS */
+ if (sop.getKind() == BUILTIN
+ && TransfChain::isChainable(sygus_tn, Node::fromExpr(sop)))
+ {
+ Assert(nm->operatorToKind(Node::fromExpr(sop)) == PLUS);
+ /* TODO #1304: be robust for this case */
+ /* For now only transforms applications whose arguments have the same type
+ * as the root */
+ bool same_type_plus = true;
+ for (const DatatypeConstructorArg& arg : dt[op_pos[i]])
+ {
+ if (TypeNode::fromType(
+ static_cast<SelectorType>(arg.getType()).getRangeType())
+ != tn)
+ {
+ same_type_plus = false;
+ break;
+ }
+ }
+ if (!same_type_plus)
+ {
+ Trace("sygus-grammar-normalize-infer")
+ << "\tFor OP " << PLUS << " did not collecting sop " << sop
+ << " in position " << op_pos[i] << "\n";
+ continue;
+ }
+ Assert(chain_op_pos == dt.getNumConstructors());
+ Trace("sygus-grammar-normalize-infer")
+ << "\tCollecting chainable OP " << sop << " in position " << op_pos[i]
+ << "\n";
+ chain_op_pos = op_pos[i];
+ continue;
+ }
+ /* TODO #1304: check this for each operator */
+ /* Collects elements that are not the identity (e.g. 0 is the id of PLUS) */
+ if (!TransfChain::isId(sygus_tn, nm->operatorOf(PLUS), Node::fromExpr(sop)))
+ {
+ Trace("sygus-grammar-normalize-infer")
+ << "\tCollecting for NON_ID_ELEMS the sop " << sop
+ << " in position " << op_pos[i] << "\n";
+ elem_pos.push_back(op_pos[i]);
+ }
+ }
+ /* Typenode admits a chain transformation for normalization */
+ if (chain_op_pos != dt.getNumConstructors() && !elem_pos.empty())
+ {
+ Trace("sygus-gnorm") << "...chain transf." << std::endl;
+ Trace("sygus-grammar-normalize-infer")
+ << "\tInfering chain transformation\n";
+ return std::unique_ptr<Transf>(new TransfChain(chain_op_pos, elem_pos));
+ }
+ return nullptr;
+}
+
+TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos)
+{
+ /* Corresponding type node to tn with the given operator positions. To be
+ * retrieved (if cached) or defined (otherwise) */
+ TypeNode unres_tn;
+ if (Trace.isOn("sygus-grammar-normalize-trie"))
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tRecursing on " << tn << " with op_positions ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-trie") << "\n";
+ }
+ /* Checks if unresolved type already created (and returns) or creates it
+ * (and then proceeds to definition) */
+ std::sort(op_pos.begin(), op_pos.end());
+ if (d_tries[tn].getOrMakeType(tn, unres_tn, op_pos))
+ {
+ if (Trace.isOn("sygus-grammar-normalize-trie"))
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tTypenode " << tn << " has already been normalized with op_pos ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-trie") << " with tn " << unres_tn << "\n";
+ }
+ return unres_tn;
+ }
+ if (Trace.isOn("sygus-grammar-normalize-trie"))
+ {
+ Trace("sygus-grammar-normalize-trie")
+ << "\tTypenode " << tn << " not yet normalized with op_pos ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-trie") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize-trie") << "\n";
+ }
+ /* Creates type object for normalization */
+ TypeObject to(tn, unres_tn);
+
+ /* Determine normalization transformation based on sygus type and given
+ * operators */
+ std::unique_ptr<Transf> transformation = inferTransf(tn, dt, op_pos);
+ /* If a transformation was selected, apply it */
+ if (transformation != nullptr)
+ {
+ transformation->buildType(this, to, dt, op_pos);
+ }
+
+ /* Remaining operators are rebuilt as they are */
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Assert(op_pos[i] < dt.getNumConstructors());
+ to.addConsInfo(this, dt[op_pos[i]]);
+ }
+ /* Build normalize datatype */
+ if (Trace.isOn("sygus-grammar-normalize"))
+ {
+ Trace("sygus-grammar-normalize") << "\nFor positions ";
+ for (unsigned i = 0, size = op_pos.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize") << op_pos[i] << " ";
+ }
+ Trace("sygus-grammar-normalize") << " and datatype " << dt << " \n";
+ }
+ to.buildDatatype(this, dt);
+ return to.d_unres_tn;
+}
+
+TypeNode SygusGrammarNorm::normalizeSygusRec(TypeNode tn)
+{
+ /* Collect all operators for normalization */
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ std::vector<unsigned> op_pos(dt.getNumConstructors());
+ std::iota(op_pos.begin(), op_pos.end(), 0);
+ return normalizeSygusRec(tn, dt, op_pos);
+}
+
+TypeNode SygusGrammarNorm::normalizeSygusType(TypeNode tn, Node sygus_vars)
+{
+ /* Normalize all types in tn */
+ d_sygus_vars = sygus_vars;
+ normalizeSygusRec(tn);
+ /* Resolve created types */
+ Assert(!d_dt_all.empty() && !d_unres_t_all.empty());
+ if (Trace.isOn("sygus-grammar-normalize-build"))
+ {
+ Trace("sygus-grammar-normalize-build")
+ << "making mutual datatyes with datatypes \n";
+ for (unsigned i = 0, size = d_dt_all.size(); i < size; ++i)
+ {
+ Trace("sygus-grammar-normalize-build") << d_dt_all[i];
+ }
+ Trace("sygus-grammar-normalize-build") << " and unresolved types\n";
+ for (const Type& unres_t : d_unres_t_all)
+ {
+ Trace("sygus-grammar-normalize-build") << unres_t << " ";
+ }
+ Trace("sygus-grammar-normalize-build") << "\n";
+ }
+ Assert(d_dt_all.size() == d_unres_t_all.size());
+ std::vector<DatatypeType> types =
+ NodeManager::currentNM()->toExprManager()->mkMutualDatatypeTypes(
+ d_dt_all, d_unres_t_all);
+ Assert(types.size() == d_dt_all.size());
+ /* Clear accumulators */
+ d_dt_all.clear();
+ d_unres_t_all.clear();
+ /* By construction the normalized type node will be the last one considered */
+ return TypeNode::fromType(types.back());
+}
+
+} // namespace quantifiers
+} // namespace theory
+} // namespace CVC4
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_norm.h b/src/theory/quantifiers/sygus/sygus_grammar_norm.h
new file mode 100644
index 000000000..f72a83e5a
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_grammar_norm.h
@@ -0,0 +1,455 @@
+/********************* */
+/*! \file sygus_grammar_norm.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Haniel Barbosa
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief class for simplifying SyGuS grammars after they are encoded into
+ ** datatypes.
+ **/
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_NORM_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_NORM_H
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "expr/datatype.h"
+#include "expr/node.h"
+#include "expr/node_manager_attributes.h" // for VarNameAttr
+#include "expr/type.h"
+#include "expr/type_node.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class SygusGrammarNorm;
+
+/** Operator position trie class
+ *
+ * This data structure stores an unresolved type corresponding to the
+ * normalization of a type. This unresolved type is indexed by the positions of
+ * the construtors of the datatype associated with the original type. The list
+ * of positions represent the operators, associated with the respective
+ * considered constructors, that were used for building the unresolved type.
+ *
+ * Example:
+ *
+ * Let A be a type defined by the grammar "A -> x | 0 | 1 | A + A". In its
+ * datatype representation the operator for "x" is in position 0, for "0" in
+ * position "1" and so on. Consider entries (T, [op_1, ..., op_n]) -> T' to
+ * represent that a type T is normalized with operators [op_1, ..., op_n] into
+ * the type T'. For entries
+ *
+ * (A, [x, 0, 1, +]) -> A1
+ * (A, [x, 1, +]) -> A2
+ * (A, [1, +]) -> A3
+ * (A, [0]) -> AZ
+ * (A, [x]) -> AX
+ * (A, [1]) -> AO
+ *
+ * the OpPosTrie T we build for this type is :
+ *
+ * T[A] :
+ * T[A].d_children[0] : AX
+ * T[A].d_children[0].d_children[1] :
+ * T[A].d_children[0].d_children[1].d_children[2] :
+ * T[A].d_children[0].d_children[1].d_children[2].d_children[3] : A1
+ * T[A].d_children[0].d_children[2] :
+ * T[A].d_children[0].d_children[2].d_children[3] : A2
+ * T[A].d_children[1] : AZ
+ * T[A].d_children[2] : AO
+ * T[A].d_children[2].d_children[4] : A3
+ *
+ * Nodes store the types built for the path of positions up to that point, if
+ * any.
+ */
+class OpPosTrie
+{
+ public:
+ /** type retrieval/addition
+ *
+ * if type indexed by the given operator positions is already in the trie then
+ * unres_t becomes the indexed type and true is returned. Otherwise a new type
+ * is created, indexed by the given positions, and assigned to unres_t, with
+ * false being returned.
+ */
+ bool getOrMakeType(TypeNode tn,
+ TypeNode& unres_tn,
+ const std::vector<unsigned>& op_pos,
+ unsigned ind = 0);
+ /** clear all data from this trie */
+ void clear() { d_children.clear(); }
+
+ private:
+ /** the data (only set for the final node of an inserted path) */
+ TypeNode d_unres_tn;
+ /* the children of the trie node */
+ std::map<unsigned, OpPosTrie> d_children;
+}; /* class OpPosTrie */
+
+/** Utility for normalizing SyGuS grammars to avoid spurious enumerations
+ *
+ * Uses the datatype representation of a SyGuS grammar to identify entries that
+ * can normalized in order to have less possible enumerations. An example is
+ * with integer types, e.g.:
+ *
+ * Int -> x | y | Int + Int | 0 | 1 | ite(Bool, Int, Int)
+ *
+ * becomes
+ *
+ * Int0 -> IntZ | Int1
+ * IntZ -> 0
+ * Int1 -> IntX | IntX + Int1 | Int2
+ * IntX -> x
+ * Int2 -> IntY | IntY + Int2 | Int3
+ * IntY -> y
+ * Int3 -> IntO | IntO + Int3 | Int4
+ * IntO -> 1
+ * Int4 -> IntITE | IntITE + Int4
+ * IntITE -> ite(Bool, Int0, Int0)
+ *
+ * TODO: #1304 normalize more complex grammars
+ *
+ * This class also performs more straightforward normalizations, such as
+ * expanding definitions of functions declared with a "define-fun" command.
+ * These lighweight transformations are always applied, independently of the
+ * normalization option being enabled.
+ */
+class SygusGrammarNorm
+{
+ public:
+ SygusGrammarNorm(QuantifiersEngine* qe)
+ : d_qe(qe), d_tds(d_qe->getTermDatabaseSygus())
+ {
+ }
+ ~SygusGrammarNorm() {}
+ /** creates a normalized typenode from a given one.
+ *
+ * In a normalized typenode all typenodes it contains are normalized.
+ * Normalized typenodes can be structurally identicial to their original
+ * counterparts.
+ *
+ * sygus_vars are the input variables for the function to be synthesized,
+ * which are used as input for the built datatypes.
+ *
+ * This is the function that will resolve all types and datatypes built during
+ * normalization. This operation can only be performed after all types
+ * contained in "tn" have been normalized, since the resolution of datatypes
+ * depends on all types involved being defined.
+ */
+ TypeNode normalizeSygusType(TypeNode tn, Node sygus_vars);
+
+ /* Retrives, or, if none, creates, stores and returns, the node for the
+ * identity operator (\lambda x. x) for the given type node */
+ static inline Node getIdOp(TypeNode tn)
+ {
+ auto it = d_tn_to_id.find(tn);
+ if (it == d_tn_to_id.end())
+ {
+ std::vector<Node> vars = {NodeManager::currentNM()->mkBoundVar(tn)};
+ Node n = NodeManager::currentNM()->mkNode(
+ kind::LAMBDA,
+ NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars),
+ vars.back());
+ d_tn_to_id[tn] = n;
+ return n;
+ }
+ return it->second;
+ }
+
+ private:
+ /** Keeps the necessary information for bulding a normalized type:
+ *
+ * the original typenode, from which the datatype representation can be
+ * extracted
+ *
+ * the operators, names, print callbacks and list of argument types for each
+ * constructor
+ *
+ * the unresolved type node used as placeholder for references of the yet to
+ * be built normalized type
+ *
+ * a datatype to represent the structure of the type node for the normalized
+ * type
+ */
+ class TypeObject
+ {
+ public:
+ /* Stores the original type node and the unresolved placeholder. The
+ * datatype for the latter is created with the respective name. */
+ TypeObject(TypeNode src_tn, TypeNode unres_tn)
+ : d_tn(src_tn),
+ d_unres_tn(unres_tn),
+ d_dt(Datatype(unres_tn.getAttribute(expr::VarNameAttr())))
+ {
+ }
+ ~TypeObject() {}
+
+ /** adds information in "cons" (operator, name, print callback, argument
+ * types) as it is into "to"
+ *
+ * A side effect of this procedure is to expand the definitions in the sygus
+ * operator of "cons"
+ *
+ * The types of the arguments of "cons" are recursively normalized
+ */
+ void addConsInfo(SygusGrammarNorm* sygus_norm,
+ const DatatypeConstructor& cons);
+
+ /** builds a datatype with the information in the type object
+ *
+ * "dt" is the datatype of the original typenode. It is necessary for
+ * retrieving ancillary information during the datatype building, such as
+ * its sygus type (e.g. Int)
+ *
+ * The built datatype and its unresolved type are saved in the global
+ * accumulators of "sygus_norm"
+ */
+ void buildDatatype(SygusGrammarNorm* sygus_norm, const Datatype& dt);
+
+ //---------- information stored from original type node
+
+ /* The original typenode this TypeObject is built from */
+ TypeNode d_tn;
+
+ //---------- information to build normalized type node
+
+ /* Operators for each constructor. */
+ std::vector<Node> d_ops;
+ /* Names for each constructor. */
+ std::vector<std::string> d_cons_names;
+ /* Print callbacks for each constructor */
+ std::vector<std::shared_ptr<SygusPrintCallback>> d_pc;
+ /* Weights for each constructor */
+ std::vector<int> d_weight;
+ /* List of argument types for each constructor */
+ std::vector<std::vector<Type>> d_cons_args_t;
+ /* Unresolved type node placeholder */
+ TypeNode d_unres_tn;
+ /* Datatype to represent type's structure */
+ Datatype d_dt;
+ }; /* class TypeObject */
+
+ /** Transformation abstract class
+ *
+ * Classes extending this one will define specif transformationst for building
+ * normalized types based on applications of specific operators
+ */
+ class Transf
+ {
+ public:
+ virtual ~Transf() {}
+
+ /** abstract function for building normalized types
+ *
+ * Builds normalized types for the operators specifed by the positions in
+ * op_pos of constructors from dt. The built types are associated with the
+ * given type object and accumulated in the sygus_norm object, whose
+ * utilities for any extra necessary normalization.
+ */
+ virtual void buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos) = 0;
+ }; /* class Transf */
+
+ /** Drop transformation class
+ *
+ * This class builds a type by dropping a set of redundant constructors,
+ * whose indices are given as input to the constructor of this class.
+ */
+ class TransfDrop : public Transf
+ {
+ public:
+ TransfDrop(const std::vector<unsigned>& indices) : d_drop_indices(indices)
+ {
+ }
+ /** build type */
+ void buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos) override;
+
+ private:
+ std::vector<unsigned> d_drop_indices;
+ };
+
+ /** Chain transformation class
+ *
+ * Determines how to build normalized types by chaining the application of one
+ * of its operators. The resulting type should admit the same terms as the
+ * previous one modulo commutativity, associativity and identity of the
+ * neutral element.
+ *
+ * TODO: #1304:
+ * - define this transformation for more than just PLUS for Int.
+ * - improve the building such that elements that should not be entitled a
+ * "link in the chain" (such as 5 in opposition to variables and 1) do not get
+ * one
+ * - consider the case when operator is applied to different types, e.g.:
+ * A -> B + B | x; B -> 0 | 1
+ * - consider the case in which in which the operator occurs nested in an
+ * argument type of itself, e.g.:
+ * A -> (B + B) + B | x; B -> 0 | 1
+ */
+ class TransfChain : public Transf
+ {
+ public:
+ TransfChain(unsigned chain_op_pos, const std::vector<unsigned>& elem_pos)
+ : d_chain_op_pos(chain_op_pos), d_elem_pos(elem_pos){};
+
+ /** builds types encoding a chain in which each link contains a repetition
+ * of the application of the chain operator over a non-identity element
+ *
+ * Example: when considering, over the integers, the operator "+" and the
+ * elemenst "1", "x" and "y", the built chain is e.g.
+ *
+ * x + ... + x + y + ... + y + 1 + ...+ 1
+ *
+ * whose encoding in types would be e.g.
+ *
+ * A -> AX | AX + A | B
+ * AX -> x
+ * B -> BY | BY + B | C
+ * BY -> y
+ * C -> C1 | C1 + C
+ * C1 -> 1
+ *
+ * ++++++++
+ *
+ * The types composing links in the chain are built recursively by invoking
+ * sygus_norm, which caches results and handles the global normalization, on
+ * the operators not used in a given link, which will lead to recalling this
+ * transformation and so on until all operators originally given are
+ * considered.
+ */
+ void buildType(SygusGrammarNorm* sygus_norm,
+ TypeObject& to,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos) override;
+
+ /** Whether operator is chainable for the type (e.g. PLUS for Int)
+ *
+ * Since the map this function depends on cannot be built statically, this
+ * function first build maps the first time a type is checked. As a
+ * consequence the list of chainabel operators is hardcoded in the map
+ * building.
+ *
+ * TODO: #1304: Cover more types and operators, make this robust to more
+ * complex grammars
+ */
+ static bool isChainable(TypeNode tn, Node op);
+ /* Whether n is the identity for the chain operator of the type (e.g. 1 is
+ * not the identity 0 for PLUS for Int)
+ *
+ * TODO: #1304: Cover more types, make this robust to more complex grammars
+ */
+ static bool isId(TypeNode tn, Node op, Node n);
+
+ private:
+ /* TODO #1304: this should admit more than one, as well as which elements
+ * are associated with which operator */
+ /* Position of chain operator */
+ unsigned d_chain_op_pos;
+ /* Positions (of constructors in the datatype whose type is being
+ * normalized) of elements the chain operator is applied to */
+ std::vector<unsigned> d_elem_pos;
+ /** Specifies for each type node which are its chainable operators
+ *
+ * For example, for Int the map is {OP -> [+]}
+ *
+ * TODO #1304: consider more operators
+ */
+ static std::map<TypeNode, std::vector<Kind>> d_chain_ops;
+ /** Specifies for each type node and chainable operator its identity
+ *
+ * For example, for Int and PLUS the map is {Int -> {+ -> 0}}
+ *
+ * TODO #1304: consider more operators
+ */
+ static std::map<TypeNode, std::map<Kind, Node>> d_chain_op_id;
+
+ }; /* class TransfChain */
+
+ /** reference to quantifier engine */
+ QuantifiersEngine* d_qe;
+ /** sygus term database associated with this utility */
+ TermDbSygus* d_tds;
+ /** List of variable inputs of function-to-synthesize.
+ *
+ * This information is needed in the construction of each datatype
+ * representation of type nodes contained in the type node being normalized
+ */
+ TNode d_sygus_vars;
+ /* Datatypes to be resolved */
+ std::vector<Datatype> d_dt_all;
+ /* Types to be resolved */
+ std::set<Type> d_unres_t_all;
+ /* Associates type nodes with OpPosTries */
+ std::map<TypeNode, OpPosTrie> d_tries;
+ /* Map of type nodes into their identity operators (\lambda x. x) */
+ static std::map<TypeNode, Node> d_tn_to_id;
+
+ /** recursively traverses a typenode normalizing all of its elements
+ *
+ * "tn" is the typenode to be normalized
+ * "dt" is its datatype representation
+ * "op_pos" is the list of positions of construtors of dt that are being
+ * considered for the normalization
+ *
+ * The result of normalizing tn with the respective constructors is cached
+ * with an OpPosTrie. New types and datatypes created during normalization are
+ * accumulated grobally to be later resolved.
+ *
+ * The normalization occurs following some inferred transformation based on
+ * the sygus type (e.g. Int) of tn, and the operators being considered.
+ *
+ * Example: Let A be the type node encoding the grammar
+ *
+ * Int -> x | y | Int + Int | 0 | 1 | ite(Bool, Int, Int)
+ *
+ * and assume all its datatype constructors are being used for
+ * normalization. The inferred normalization transformation will consider the
+ * non-zero elements {x, y, 1, ite(...)} and the operator {+} to build a chain
+ * of monomials, as seen above. The operator for "0" is rebuilt as is (the
+ * default behaviour of operators not selected for transformations).
+ *
+ * recursion depth is limited by the height of the types, which is small
+ */
+ TypeNode normalizeSygusRec(TypeNode tn,
+ const Datatype& dt,
+ std::vector<unsigned>& op_pos);
+
+ /** wrapper for the above function
+ *
+ * invoked when all operators of "tn" are to be considered for normalization
+ */
+ TypeNode normalizeSygusRec(TypeNode tn);
+
+ /** infers a transformation for normalizing dt when allowed to use the
+ * operators in the positions op_pos.
+ *
+ * TODO: #1304: Infer more complex transformations
+ */
+ std::unique_ptr<Transf> inferTransf(TypeNode tn,
+ const Datatype& dt,
+ const std::vector<unsigned>& op_pos);
+}; /* class SygusGrammarNorm */
+
+} // namespace quantifiers
+} // namespace theory
+} // namespace CVC4
+
+#endif
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_red.cpp b/src/theory/quantifiers/sygus/sygus_grammar_red.cpp
new file mode 100644
index 000000000..939788e4d
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_grammar_red.cpp
@@ -0,0 +1,136 @@
+/********************* */
+/*! \file sygus_grammar_red.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of sygus_grammar_red
+ **/
+
+#include "theory/quantifiers/sygus/sygus_grammar_red.h"
+
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace std;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void SygusRedundantCons::initialize(QuantifiersEngine* qe, TypeNode tn)
+{
+ Assert(qe != nullptr);
+ Trace("sygus-red") << "Compute redundant cons for " << tn << std::endl;
+ d_type = tn;
+ Assert(tn.isDatatype());
+ TermDbSygus* tds = qe->getTermDatabaseSygus();
+ tds->registerSygusType(tn);
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Assert(dt.isSygus());
+ TypeNode btn = TypeNode::fromType(dt.getSygusType());
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ Trace("sygus-red") << " Is " << dt[i].getName() << " a redundant operator?"
+ << std::endl;
+ std::map<int, Node> pre;
+ Node g = tds->mkGeneric(dt, i, pre);
+ Trace("sygus-red-debug") << " ...pre-rewrite : " << g << std::endl;
+ Assert(g.getNumChildren() == dt[i].getNumArgs());
+ d_gen_terms[i] = g;
+ for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
+ {
+ pre[j] = g[j];
+ }
+ std::vector<Node> glist;
+ getGenericList(tds, dt, i, 0, pre, glist);
+ // call the extended rewriter
+ bool red = false;
+ for (const Node& gr : glist)
+ {
+ Trace("sygus-red-debug") << " ...variant : " << gr << std::endl;
+ std::map<Node, unsigned>::iterator itg = d_gen_cons.find(gr);
+ if (itg != d_gen_cons.end() && itg->second != i)
+ {
+ red = true;
+ Trace("sygus-red") << " ......redundant, since a variant of " << g
+ << " and " << d_gen_terms[itg->second]
+ << " both rewrite to " << gr << std::endl;
+ break;
+ }
+ else
+ {
+ d_gen_cons[gr] = i;
+ Trace("sygus-red") << " ......not redundant." << std::endl;
+ }
+ }
+ d_sygus_red_status.push_back(red ? 1 : 0);
+ }
+}
+
+void SygusRedundantCons::getRedundant(std::vector<unsigned>& indices)
+{
+ const Datatype& dt = static_cast<DatatypeType>(d_type.toType()).getDatatype();
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ if (isRedundant(i))
+ {
+ indices.push_back(i);
+ }
+ }
+}
+
+bool SygusRedundantCons::isRedundant(unsigned i)
+{
+ Assert(i < d_sygus_red_status.size());
+ return d_sygus_red_status[i] == 1;
+}
+
+void SygusRedundantCons::getGenericList(TermDbSygus* tds,
+ const Datatype& dt,
+ unsigned c,
+ unsigned index,
+ std::map<int, Node>& pre,
+ std::vector<Node>& terms)
+{
+ if (index == dt[c].getNumArgs())
+ {
+ Node gt = tds->mkGeneric(dt, c, pre);
+ gt = tds->getExtRewriter()->extendedRewrite(gt);
+ terms.push_back(gt);
+ return;
+ }
+ // with no swap
+ getGenericList(tds, dt, c, index + 1, pre, terms);
+ // swapping is exponential, only use for operators with small # args.
+ if (dt[c].getNumArgs() <= 5)
+ {
+ TypeNode atype = tds->getArgType(dt[c], index);
+ for (unsigned s = index + 1, nargs = dt[c].getNumArgs(); s < nargs; s++)
+ {
+ if (tds->getArgType(dt[c], s) == atype)
+ {
+ // swap s and index
+ Node tmp = pre[s];
+ pre[s] = pre[index];
+ pre[index] = tmp;
+ getGenericList(tds, dt, c, index + 1, pre, terms);
+ // revert
+ tmp = pre[s];
+ pre[s] = pre[index];
+ pre[index] = tmp;
+ }
+ }
+ }
+}
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_red.h b/src/theory/quantifiers/sygus/sygus_grammar_red.h
new file mode 100644
index 000000000..d0484aa57
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_grammar_red.h
@@ -0,0 +1,119 @@
+/********************* */
+/*! \file sygus_grammar_red.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief sygus_grammar_red
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H
+
+#include <map>
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** SygusRedundantCons
+ *
+ * This class computes the subset of indices of the constructors of a sygus type
+ * that are redundant. To use this class, first call initialize( qe, tn ),
+ * where tn is a sygus tn. Then, use getRedundant and/or isRedundant to get the
+ * indicies of the constructors of tn that are redundant.
+ */
+class SygusRedundantCons
+{
+ public:
+ SygusRedundantCons() {}
+ ~SygusRedundantCons() {}
+ /** register type tn
+ *
+ * qe : pointer to the quantifiers engine,
+ * tn : the (sygus) type to compute redundant constructors for
+ */
+ void initialize(QuantifiersEngine* qe, TypeNode tn);
+ /** Get the indices of the redundant constructors of the register type */
+ void getRedundant(std::vector<unsigned>& indices);
+ /**
+ * This function returns true if the i^th constructor of the registered type
+ * is redundant.
+ */
+ bool isRedundant(unsigned i);
+
+ private:
+ /** the registered type */
+ TypeNode d_type;
+ /** redundant status
+ *
+ * For each constructor, status indicating whether the constructor is
+ * redundant, where:
+ *
+ * 0 : not redundant,
+ * 1 : redundant since another constructor can be used to construct values for
+ * this constructor.
+ *
+ * For example, for grammar:
+ * A -> C > B | B < C | not D
+ * B -> x | y
+ * C -> 0 | 1 | C+C
+ * D -> B >= C
+ * If A is register with this class, then we store may store { 0, 1, 0 },
+ * noting that the second constructor of A can be simulated with the first.
+ * Notice that the third constructor is not considered redundant.
+ */
+ std::vector<int> d_sygus_red_status;
+ /**
+ * Map from constructor indices to the generic term for that constructor,
+ * where the generic term for a constructor is the (canonical) term returned
+ * by a call to TermDbSygus::mkGeneric.
+ */
+ std::map<unsigned, Node> d_gen_terms;
+ /**
+ * Map from the rewritten form of generic terms for constructors of the
+ * registered type to their corresponding constructor index.
+ */
+ std::map<Node, unsigned> d_gen_cons;
+ /** get generic list
+ *
+ * This function constructs all well-typed variants of a term of the form
+ * op( x1, ..., xn )
+ * where op is the builtin operator for dt[c], and xi = pre[i] for i=1,...,n.
+ *
+ * It constructs a list of terms of the form g * sigma, where sigma
+ * is an automorphism on { x1...xn } such that for all xi -> xj in sigma,
+ * the type for arguments i and j of dt[c] are the same. We store this
+ * list of terms in terms.
+ *
+ * This function recurses on the arguments of g, index is the current argument
+ * we are processing, and pre stores the current arguments of
+ *
+ * For example, for a sygus grammar
+ * A -> and( A, A, B )
+ * B -> false
+ * passing arguments such that g=and( x1, x2, x3 ) to this function will add:
+ * and( x1, x2, x3 ) and and( x2, x1, x3 )
+ * to terms.
+ */
+ void getGenericList(TermDbSygus* tds,
+ const Datatype& dt,
+ unsigned c,
+ unsigned index,
+ std::map<int, Node>& pre,
+ std::vector<Node>& terms);
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_GRAMMAR_RED_H */
diff --git a/src/theory/quantifiers/sygus/sygus_invariance.cpp b/src/theory/quantifiers/sygus/sygus_invariance.cpp
new file mode 100644
index 000000000..6b4c6488d
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_invariance.cpp
@@ -0,0 +1,229 @@
+/********************* */
+/*! \file sygus_invariance.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of techniques for sygus invariance tests.
+ **/
+
+#include "theory/quantifiers/sygus/sygus_invariance.h"
+
+#include "theory/quantifiers/sygus/ce_guided_conjecture.h"
+#include "theory/quantifiers/sygus/sygus_pbe.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void EvalSygusInvarianceTest::init(Node conj, Node var, Node res)
+{
+ d_conj = conj;
+ d_var = var;
+ d_result = res;
+}
+
+Node EvalSygusInvarianceTest::doEvaluateWithUnfolding(TermDbSygus* tds, Node n)
+{
+ return tds->evaluateWithUnfolding(n, d_visited);
+}
+
+bool EvalSygusInvarianceTest::invariant(TermDbSygus* tds, Node nvn, Node x)
+{
+ TNode tnvn = nvn;
+ Node conj_subs = d_conj.substitute(d_var, tnvn);
+ Node conj_subs_unfold = doEvaluateWithUnfolding(tds, conj_subs);
+ 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;
+ }
+ return false;
+}
+
+void EquivSygusInvarianceTest::init(
+ TermDbSygus* tds, TypeNode tn, CegConjecture* aconj, Node e, Node bvr)
+{
+ // compute the current examples
+ d_bvr = bvr;
+ if (aconj->getPbe()->hasExamples(e))
+ {
+ d_conj = aconj;
+ d_enum = e;
+ unsigned nex = aconj->getPbe()->getNumExamples(e);
+ for (unsigned i = 0; i < nex; i++)
+ {
+ d_exo.push_back(d_conj->getPbe()->evaluateBuiltin(tn, bvr, e, i));
+ }
+ }
+}
+
+bool EquivSygusInvarianceTest::invariant(TermDbSygus* tds, Node nvn, Node x)
+{
+ TypeNode tn = nvn.getType();
+ Node nbv = tds->sygusToBuiltin(nvn, tn);
+ Node nbvr = tds->getExtRewriter()->extendedRewrite(nbv);
+ Trace("sygus-sb-mexp-debug") << " min-exp check : " << nbv << " -> " << nbvr
+ << std::endl;
+ bool exc_arg = false;
+ // equivalent / singular up to normalization
+ if (nbvr == d_bvr)
+ {
+ // gives the same result : then the explanation for the child is irrelevant
+ exc_arg = true;
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn)
+ << " is rewritten to " << nbvr;
+ Trace("sygus-sb-mexp") << " regardless of the content of "
+ << tds->sygusToBuiltin(x) << std::endl;
+ }
+ else
+ {
+ if (nbvr.isVar())
+ {
+ TypeNode xtn = x.getType();
+ if (xtn == tn)
+ {
+ Node bx = tds->sygusToBuiltin(x, xtn);
+ Assert(bx.getType() == nbvr.getType());
+ if (nbvr == bx)
+ {
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn)
+ << " always rewrites to argument " << nbvr
+ << std::endl;
+ // rewrites to the variable : then the explanation of this is
+ // irrelevant as well
+ exc_arg = true;
+ d_bvr = nbvr;
+ }
+ }
+ }
+ }
+ // equivalent under examples
+ if (!exc_arg)
+ {
+ if (!d_enum.isNull())
+ {
+ bool ex_equiv = true;
+ for (unsigned j = 0; j < d_exo.size(); j++)
+ {
+ Node nbvr_ex = d_conj->getPbe()->evaluateBuiltin(tn, nbvr, d_enum, j);
+ if (nbvr_ex != d_exo[j])
+ {
+ ex_equiv = false;
+ break;
+ }
+ }
+ if (ex_equiv)
+ {
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn);
+ Trace("sygus-sb-mexp")
+ << " is the same w.r.t. examples regardless of the content of "
+ << tds->sygusToBuiltin(x) << std::endl;
+ exc_arg = true;
+ }
+ }
+ }
+ return exc_arg;
+}
+
+bool DivByZeroSygusInvarianceTest::invariant(TermDbSygus* tds, Node nvn, Node x)
+{
+ TypeNode tn = nvn.getType();
+ Node nbv = tds->sygusToBuiltin(nvn, tn);
+ Node nbvr = tds->getExtRewriter()->extendedRewrite(nbv);
+ if (tds->involvesDivByZero(nbvr))
+ {
+ Trace("sygus-sb-mexp") << "sb-min-exp : " << tds->sygusToBuiltin(nvn)
+ << " involves div-by-zero regardless of "
+ << tds->sygusToBuiltin(x) << std::endl;
+ return true;
+ }
+ return false;
+}
+
+void NegContainsSygusInvarianceTest::init(CegConjecture* conj,
+ Node e,
+ std::vector<Node>& exo,
+ std::vector<unsigned>& ncind)
+{
+ if (conj->getPbe()->hasExamples(e))
+ {
+ Assert(conj->getPbe()->getNumExamples(e) == exo.size());
+ d_enum = e;
+ d_exo.insert(d_exo.end(), exo.begin(), exo.end());
+ d_neg_con_indices.insert(
+ d_neg_con_indices.end(), ncind.begin(), ncind.end());
+ d_conj = conj;
+ }
+}
+
+bool NegContainsSygusInvarianceTest::invariant(TermDbSygus* tds,
+ Node nvn,
+ Node x)
+{
+ if (!d_enum.isNull())
+ {
+ TypeNode tn = nvn.getType();
+ Node nbv = tds->sygusToBuiltin(nvn, tn);
+ Node nbvr = tds->getExtRewriter()->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 = d_conj->getPbe()->evaluateBuiltin(tn, nbvr, d_enum, ii);
+ Node out = d_exo[ii];
+ Node cont =
+ NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, out, nbvre);
+ Trace("sygus-pbe-cterm-debug") << "Check: " << cont << std::endl;
+ 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;
+ d_conj->getPbe()->getExample(d_enum, 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;
+ }
+ Trace("sygus-pbe-cterm-debug2")
+ << "...check failed, rewrites to : " << contr << std::endl;
+ }
+ }
+ return false;
+}
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
diff --git a/src/theory/quantifiers/sygus/sygus_invariance.h b/src/theory/quantifiers/sygus/sygus_invariance.h
new file mode 100644
index 000000000..a43e38719
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_invariance.h
@@ -0,0 +1,276 @@
+/********************* */
+/*! \file sygus_invariance.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief sygus invariance tests
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "expr/node.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class TermDbSygus;
+class CegConjecture;
+
+/* SygusInvarianceTest
+*
+* This class is the standard interface for term generalization
+* in SyGuS. Its interface is a single function is_variant,
+* which is a virtual condition for SyGuS terms.
+*
+* The common use case of invariance tests is when constructing
+* minimal explanations for refinement lemmas in the
+* counterexample-guided inductive synthesis (CEGIS) loop.
+* See sygus_explain.h for more details.
+*/
+class SygusInvarianceTest
+{
+ public:
+ virtual ~SygusInvarianceTest() {}
+
+ /** Is nvn invariant with respect to this test ?
+ *
+ * - nvn is the term to check whether it is invariant.
+ * - x is a variable such that the previous call to
+ * is_invariant (if any) was with term nvn_prev, and
+ * nvn is equal to nvn_prev with some subterm
+ * position replaced by x. This is typically used
+ * for debugging only.
+ */
+ bool is_invariant(TermDbSygus* tds, Node nvn, Node x)
+ {
+ if (invariant(tds, nvn, x))
+ {
+ d_update_nvn = nvn;
+ return true;
+ }
+ return false;
+ }
+ /** get updated term */
+ Node getUpdatedTerm() { return d_update_nvn; }
+ /** set updated term */
+ void setUpdatedTerm(Node n) { d_update_nvn = n; }
+ protected:
+ /** result of the node that satisfies this invariant */
+ Node d_update_nvn;
+ /** check whether nvn[ x ] is invariant */
+ virtual bool invariant(TermDbSygus* tds, Node nvn, Node x) = 0;
+};
+
+/** EquivSygusInvarianceTest
+*
+* This class tests whether a term evaluates via evaluation
+* operators in the deep embedding (Section 4 of Reynolds
+* et al. CAV 2015) to fixed term d_result.
+*
+* For example, consider a SyGuS evaluation function eval
+* for a synthesis conjecture with arguments x and y.
+* Notice that the term t = (mult x y) is such that:
+* eval( t, 0, 1 ) ----> 0
+* This test is invariant on the content of the second
+* argument of t, noting that:
+* eval( (mult x _), 0, 1 ) ----> 0
+* as well, via a call to EvalSygusInvarianceTest::invariant.
+*
+* Another example, t = ite( gt( x, y ), x, y ) is such that:
+* eval( t, 2, 3 ) ----> 3
+* This test is invariant on the second child of t, noting:
+* eval( ite( gt( x, y ), _, y ), 2, 3 ) ----> 3
+*/
+class EvalSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ EvalSygusInvarianceTest() {}
+
+ /** initialize this invariance test
+ * This sets d_conj/d_var/d_result, where
+ * we are checking whether:
+ * d_conj { d_var -> n } ----> d_result.
+ * for terms n.
+ */
+ void init(Node conj, Node var, Node res);
+
+ /** do evaluate with unfolding, using the cache of this class */
+ Node doEvaluateWithUnfolding(TermDbSygus* tds, Node n);
+
+ protected:
+ /** does d_conj{ d_var -> nvn } still rewrite to d_result? */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+
+ private:
+ /** the formula we are evaluating */
+ Node d_conj;
+ /** the variable */
+ TNode d_var;
+ /** the result of the evaluation */
+ Node d_result;
+ /** cache of n -> the simplified form of eval( n ) */
+ std::unordered_map<Node, Node, NodeHashFunction> d_visited;
+};
+
+/** EquivSygusInvarianceTest
+*
+* This class tests whether a builtin version of a
+* sygus term is equivalent up to rewriting to a RHS value bvr.
+*
+* For example,
+*
+* ite( t>0, 0, 0 ) + s*0 ----> 0
+*
+* This test is invariant on the condition t>0 and s, since:
+*
+* ite( _, 0, 0 ) + _*0 ----> 0
+*
+* for any values of _.
+*
+* It also manages the case where the rewriting is invariant
+* wrt a finite set of examples occurring in the conjecture.
+* (EX1) : For example if our input examples are:
+* (x,y,z) = (3,2,4), (5,2,6), (3,2,1)
+* On these examples, we have:
+*
+* ite( x>y, z, 0) ---> 4,6,1
+*
+* which is invariant on the second argument:
+*
+* ite( x>y, z, _) ---> 4,6,1
+*
+* For details, see Reynolds et al SYNT 2017.
+*/
+class EquivSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ EquivSygusInvarianceTest() : d_conj(nullptr) {}
+
+ /** initialize this invariance test
+ * tn is the sygus type for e
+ * aconj/e are used for conjecture-specific symmetry breaking
+ * bvr is the builtin version of the right hand side of the rewrite that we
+ * are checking for invariance
+ */
+ void init(
+ TermDbSygus* tds, TypeNode tn, CegConjecture* aconj, Node e, Node bvr);
+
+ protected:
+ /** checks whether the analog of nvn still rewrites to d_bvr */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+
+ private:
+ /** the conjecture associated with the enumerator d_enum */
+ CegConjecture* d_conj;
+ /** the enumerator associated with the term for which this test is for */
+ Node d_enum;
+ /** the RHS of the evaluation */
+ Node d_bvr;
+ /** the result of the examples
+ * In (EX1), this is (4,6,1)
+ */
+ std::vector<Node> d_exo;
+};
+
+/** DivByZeroSygusInvarianceTest
+ *
+ * This class tests whether a sygus term involves
+ * division by zero.
+ *
+ * For example the test for:
+ * ( x + ( y/0 )*2 )
+ * is invariant on the contents of _ below:
+ * ( _ + ( _/0 )*_ )
+ */
+class DivByZeroSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ DivByZeroSygusInvarianceTest() {}
+
+ protected:
+ /** checks whether nvn involves division by zero. */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+};
+
+/** NegContainsSygusInvarianceTest
+*
+* This class is used to construct a minimal shape of a term that cannot
+* be contained in at least one output of an I/O pair.
+*
+* Say our PBE conjecture is:
+*
+* exists f.
+* f( "abc" ) = "abc abc" ^
+* f( "de" ) = "de de"
+*
+* Then, this class is used when there is a candidate solution t[x1]
+* such that either:
+* contains( "abc abc", t["abc"] ) ---> false or
+* contains( "de de", t["de"] ) ---> false
+*
+* It is used to determine whether certain generalizations of t[x1]
+* are still sufficient to falsify one of the above containments.
+*
+* For example:
+*
+* The test for str.++( x1, "d" ) is invariant on its first argument
+* ...since contains( "abc abc", str.++( _, "d" ) ) ---> false
+* The test for str.replace( "de", x1, "b" ) is invariant on its third argument
+* ...since contains( "abc abc", str.replace( "de", "abc", _ ) ) ---> false
+*/
+class NegContainsSygusInvarianceTest : public SygusInvarianceTest
+{
+ public:
+ NegContainsSygusInvarianceTest() : d_conj(nullptr) {}
+
+ /** initialize this invariance test
+ * cpbe is the conjecture utility.
+ * e is the enumerator which we are reasoning about (associated with a synth
+ * fun).
+ * exo is the list of outputs of the PBE conjecture.
+ * ncind is the set of possible indices of the PBE conjecture to check
+ * invariance of non-containment.
+ * For example, in the above example, when t[x1] = "ab", then this
+ * has the index 1 since contains("de de", "ab") ---> false but not
+ * the index 0 since contains("abc abc","ab") ---> true.
+ */
+ void init(CegConjecture* conj,
+ Node e,
+ std::vector<Node>& exo,
+ std::vector<unsigned>& ncind);
+
+ protected:
+ /** checks if contains( out_i, nvn[in_i] ) --> false for some I/O pair i. */
+ bool invariant(TermDbSygus* tds, Node nvn, Node x);
+
+ private:
+ /** The enumerator whose value we are considering in this invariance test */
+ Node d_enum;
+ /** The output examples for the enumerator */
+ std::vector<Node> d_exo;
+ /** The set of I/O pair indices i such that
+ * contains( out_i, nvn[in_i] ) ---> false
+ */
+ std::vector<unsigned> d_neg_con_indices;
+ /** reference to the conjecture associated with this test */
+ CegConjecture* d_conj;
+};
+
+} /* CVC4::theory::quantifiers namespace */
+} /* CVC4::theory namespace */
+} /* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__SYGUS_INVARIANCE_H */
diff --git a/src/theory/quantifiers/sygus/sygus_pbe.cpp b/src/theory/quantifiers/sygus/sygus_pbe.cpp
new file mode 100644
index 000000000..17c4c482d
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_pbe.cpp
@@ -0,0 +1,2460 @@
+/********************* */
+/*! \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/sygus/sygus_pbe.h"
+
+#include "expr/datatype.h"
+#include "options/quantifiers_options.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/datatypes/datatypes_rewriter.h"
+#include "util/random.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+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" );
+ }
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, EnumRole r)
+{
+ switch(r){
+ case enum_invalid: os << "INVALID"; break;
+ case enum_io: os << "IO"; break;
+ case enum_ite_condition: os << "CONDITION"; break;
+ case enum_concat_term: os << "CTERM"; break;
+ default: os << "enum_" << static_cast<unsigned>(r); break;
+ }
+ return os;
+}
+
+std::ostream& operator<<(std::ostream& os, NodeRole r)
+{
+ switch (r)
+ {
+ case role_equal: os << "equal"; break;
+ case role_string_prefix: os << "string_prefix"; break;
+ case role_string_suffix: os << "string_suffix"; break;
+ case role_ite_condition: os << "ite_condition"; break;
+ default: os << "role_" << static_cast<unsigned>(r); break;
+ }
+ return os;
+}
+
+EnumRole getEnumeratorRoleForNodeRole(NodeRole r)
+{
+ switch (r)
+ {
+ case role_equal: return enum_io; break;
+ case role_string_prefix: return enum_concat_term; break;
+ case role_string_suffix: return enum_concat_term; break;
+ case role_ite_condition: return enum_ite_condition; break;
+ default: break;
+ }
+ return enum_invalid;
+}
+
+std::ostream& operator<<(std::ostream& os, StrategyType st)
+{
+ switch (st)
+ {
+ case strat_ITE: os << "ITE"; break;
+ case strat_CONCAT_PREFIX: os << "CONCAT_PREFIX"; break;
+ case strat_CONCAT_SUFFIX: os << "CONCAT_SUFFIX"; break;
+ case strat_ID: os << "ID"; break;
+ default: os << "strat_" << static_cast<unsigned>(st); break;
+ }
+ return os;
+}
+
+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, role_equal);
+ // 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()->registerEnumerator(
+ candidates[i], candidates[i], d_parent);
+ }
+ }
+}
+
+Node CegConjecturePbe::PbeTrie::addPbeExample(TypeNode etn, Node e, Node b,
+ CegConjecturePbe* cpbe,
+ unsigned index, unsigned 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
+ Assert(cpbe->d_examples.find(e) != cpbe->d_examples.end());
+ Assert(index < cpbe->d_examples[e].size());
+ ex = cpbe->d_examples[e][index];
+ addPbeExampleEval(etn, e, d_lazy_child, ex, cpbe, index, ntotal);
+ Assert(!d_children.empty());
+ d_lazy_child = Node::null();
+ }
+ } else {
+ Assert(cpbe->d_examples.find(e) != cpbe->d_examples.end());
+ Assert(index < cpbe->d_examples[e].size());
+ ex = cpbe->d_examples[e][index];
+ }
+ return addPbeExampleEval(etn, e, b, ex, cpbe, index, ntotal);
+ }
+}
+
+Node CegConjecturePbe::PbeTrie::addPbeExampleEval(TypeNode etn, Node e, Node b,
+ std::vector<Node>& ex,
+ CegConjecturePbe* cpbe,
+ unsigned index,
+ unsigned ntotal) {
+ Node eb = cpbe->d_tds->evaluateBuiltin(etn, b, ex);
+ return d_children[eb].addPbeExample(etn, e, b, cpbe, index + 1, ntotal);
+}
+
+bool CegConjecturePbe::hasExamples(Node e) {
+ if (isPbe()) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
+ if (itx == d_examples_invalid.end()) {
+ return d_examples.find(e) != d_examples.end();
+ }
+ }
+ return false;
+}
+
+unsigned CegConjecturePbe::getNumExamples(Node e) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, std::vector<std::vector<Node> > >::iterator it =
+ d_examples.find(e);
+ if (it != d_examples.end()) {
+ return it->second.size();
+ } else {
+ return 0;
+ }
+}
+
+void CegConjecturePbe::getExample(Node e, unsigned i, std::vector<Node>& ex) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, std::vector<std::vector<Node> > >::iterator it =
+ d_examples.find(e);
+ if (it != d_examples.end()) {
+ Assert(i < it->second.size());
+ ex.insert(ex.end(), it->second[i].begin(), it->second[i].end());
+ } else {
+ Assert(false);
+ }
+}
+
+Node CegConjecturePbe::getExampleOut(Node e, unsigned i) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, std::vector<Node> >::iterator it = d_examples_out.find(e);
+ if (it != d_examples_out.end()) {
+ Assert(i < it->second.size());
+ return it->second[i];
+ } else {
+ Assert(false);
+ return Node::null();
+ }
+}
+
+Node CegConjecturePbe::addSearchVal(TypeNode tn, Node e, Node bvr) {
+ Assert(isPbe());
+ Assert(!e.isNull());
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
+ if (itx == d_examples_invalid.end()) {
+ unsigned nex = d_examples[e].size();
+ 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 CegConjecturePbe::evaluateBuiltin(TypeNode tn, Node bn, Node e,
+ unsigned i) {
+ e = d_tds->getSynthFunForEnumerator(e);
+ Assert(!e.isNull());
+ std::map<Node, bool>::iterator itx = d_examples_invalid.find(e);
+ if (itx == d_examples_invalid.end()) {
+ std::map<Node, std::vector<std::vector<Node> > >::iterator it =
+ d_examples.find(e);
+ if (it != d_examples.end()) {
+ Assert(i < it->second.size());
+ return d_tds->evaluateBuiltin(tn, bn, it->second[i]);
+ }
+ }
+ return Rewriter::rewrite(bn);
+}
+
+// ----------------------------- establishing enumeration types
+
+void CegConjecturePbe::registerEnumerator(
+ Node et, Node c, TypeNode tn, EnumRole enum_role, bool inSearch)
+{
+ if (d_einfo.find(et) == d_einfo.end())
+ {
+ Trace("sygus-unif-debug")
+ << "...register " << et << " for "
+ << ((DatatypeType)tn.toType()).getDatatype().getName();
+ Trace("sygus-unif-debug") << ", role = " << enum_role
+ << ", in search = " << inSearch << std::endl;
+ d_einfo[et].initialize(c, 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()->registerEnumerator(et, c, d_parent, true);
+ d_einfo[et].d_active_guard =
+ d_qe->getTermDatabaseSygus()->getActiveGuardForEnumerator(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,
+ NodeRole nrole)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ 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 );
+ }
+ EnumTypeInfo& eti = d_cinfo[e].d_tinfo[tn];
+ std::map<NodeRole, StrategyNode>::iterator itsn = eti.d_snodes.find(nrole);
+ if (itsn != eti.d_snodes.end())
+ {
+ // already initialized
+ return;
+ }
+ StrategyNode& snode = eti.d_snodes[nrole];
+
+ // get the enumerator for this
+ EnumRole erole = getEnumeratorRoleForNodeRole(nrole);
+
+ Node ee;
+ std::map<EnumRole, Node>::iterator iten = eti.d_enum.find(erole);
+ if (iten == eti.d_enum.end())
+ {
+ ee = nm->mkSkolem("ee", tn);
+ eti.d_enum[erole] = ee;
+ Trace("sygus-unif-debug")
+ << "...enumerator " << ee << " for "
+ << ((DatatypeType)tn.toType()).getDatatype().getName()
+ << ", role = " << erole << std::endl;
+ }
+ else
+ {
+ ee = iten->second;
+ }
+
+ // roles that we do not recurse on
+ if (nrole == role_ite_condition)
+ {
+ Trace("sygus-unif-debug") << "...this register (non-io)" << std::endl;
+ registerEnumerator(ee, e, tn, erole, true);
+ return;
+ }
+
+ // look at information on how we will construct solutions for this type
+ Assert(tn.isDatatype());
+ const Datatype& dt = static_cast<DatatypeType>(tn.toType()).getDatatype();
+ Assert(dt.isSygus());
+
+ std::map<Node, std::vector<StrategyType> > cop_to_strat;
+ std::map<Node, unsigned> cop_to_cindex;
+ 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, std::vector<unsigned> > cop_to_carg_list;
+ std::map<Node, std::vector<TypeNode> > cop_to_child_types;
+ std::map<Node, std::vector<Node> > cop_to_sks;
+
+ // whether we will enumerate the current type
+ bool search_this = false;
+ for (unsigned j = 0, ncons = dt.getNumConstructors(); j < ncons; j++)
+ {
+ Node cop = Node::fromExpr(dt[j].getConstructor());
+ Node op = Node::fromExpr(dt[j].getSygusOp());
+ Trace("sygus-unif-debug") << "--- Infer strategy from " << cop
+ << " with sygus op " << op << "..." << std::endl;
+
+ // expand the evaluation to see if this constuctor induces a strategy
+ std::vector<Node> utchildren;
+ utchildren.push_back(cop);
+ std::vector<Node> sks;
+ std::vector<TypeNode> sktns;
+ for (unsigned k = 0, nargs = dt[j].getNumArgs(); k < nargs; k++)
+ {
+ Type t = dt[j][k].getRangeType();
+ TypeNode ttn = TypeNode::fromType(t);
+ Node kv = nm->mkSkolem("ut", ttn);
+ sks.push_back(kv);
+ cop_to_sks[cop].push_back(kv);
+ sktns.push_back(ttn);
+ utchildren.push_back(kv);
+ }
+ Node ut = nm->mkNode(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 (const Node& sbv : sbvl)
+ {
+ echildren.push_back(sbv);
+ }
+ Node eut = nm->mkNode(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;
+ Trace("sygus-unif-debug2") << ", type : " << eut.getType() << std::endl;
+
+ // candidate strategy
+ if (eut.getKind() == ITE)
+ {
+ cop_to_strat[cop].push_back(strat_ITE);
+ }
+ else if (eut.getKind() == STRING_CONCAT)
+ {
+ if (nrole != role_string_suffix)
+ {
+ cop_to_strat[cop].push_back(strat_CONCAT_PREFIX);
+ }
+ if (nrole != role_string_prefix)
+ {
+ cop_to_strat[cop].push_back(strat_CONCAT_SUFFIX);
+ }
+ }
+ else if (dt[j].isSygusIdFunc())
+ {
+ cop_to_strat[cop].push_back(strat_ID);
+ }
+
+ // the kinds for which there is a strategy
+ if (cop_to_strat.find(cop) != cop_to_strat.end())
+ {
+ // infer an injection from the arguments of the datatype
+ 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, sksize = sks.size(); k < sksize; k++)
+ {
+ Assert(sks[k].getType().isDatatype());
+ const Datatype& cdt =
+ static_cast<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 = nm->mkNode(APPLY_UF, echildren);
+ vs.push_back(esk);
+ Node tvar = nm->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") << "Constructor " << j << ", base term is "
+ << eut << std::endl;
+ std::map<unsigned, Node> test_args;
+ if (dt[j].isSygusIdFunc())
+ {
+ test_args[0] = eut;
+ }
+ else
+ {
+ for (unsigned k = 0, size = eut.getNumChildren(); k < size; k++)
+ {
+ test_args[k] = eut[k];
+ }
+ }
+
+ // TODO : prefix grouping prefix/suffix
+ bool isAssoc = TermUtil::isAssoc(eut.getKind());
+ Trace("sygus-unif-debug2") << eut.getKind() << " isAssoc = " << isAssoc
+ << std::endl;
+ std::map<unsigned, std::vector<unsigned> > assoc_combine;
+ std::vector<unsigned> assoc_waiting;
+ int assoc_last_valid_index = -1;
+ for (std::pair<const unsigned, Node>& ta : test_args)
+ {
+ unsigned k = ta.first;
+ Node eut_c = ta.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-debug")
+ << "...fail: could not find injection (range)." << std::endl;
+ cop_to_strat.erase(cop);
+ break;
+ }
+ std::map<unsigned, unsigned>::iterator itti = templ_injection.find(k);
+ if (itti != templ_injection.end())
+ {
+ // if associative, combine arguments if it is the same variable
+ if (isAssoc && assoc_last_valid_index >= 0
+ && itti->second == templ_injection[assoc_last_valid_index])
+ {
+ templ_injection.erase(k);
+ assoc_combine[assoc_last_valid_index].push_back(k);
+ }
+ else
+ {
+ assoc_last_valid_index = (int)k;
+ if (!assoc_waiting.empty())
+ {
+ assoc_combine[k].insert(assoc_combine[k].end(),
+ assoc_waiting.begin(),
+ assoc_waiting.end());
+ assoc_waiting.clear();
+ }
+ assoc_combine[k].push_back(k);
+ }
+ }
+ else
+ {
+ // a ground argument
+ if (!isAssoc)
+ {
+ Trace("sygus-unif-debug")
+ << "...fail: could not find injection (functional)."
+ << std::endl;
+ cop_to_strat.erase(cop);
+ break;
+ }
+ else
+ {
+ if (assoc_last_valid_index >= 0)
+ {
+ assoc_combine[assoc_last_valid_index].push_back(k);
+ }
+ else
+ {
+ assoc_waiting.push_back(k);
+ }
+ }
+ }
+ }
+ if (cop_to_strat.find(cop) != cop_to_strat.end())
+ {
+ // construct the templates
+ if (!assoc_waiting.empty())
+ {
+ // could not find a way to fit some arguments into injection
+ cop_to_strat.erase(cop);
+ }
+ else
+ {
+ for (std::pair<const unsigned, Node>& ta : test_args)
+ {
+ unsigned k = ta.first;
+ Trace("sygus-unif-debug2") << "- processing argument " << k << "..."
+ << std::endl;
+ if (templ_injection.find(k) != templ_injection.end())
+ {
+ unsigned sk_index = templ_injection[k];
+ if (std::find(cop_to_carg_list[cop].begin(),
+ cop_to_carg_list[cop].end(),
+ sk_index)
+ == cop_to_carg_list[cop].end())
+ {
+ cop_to_carg_list[cop].push_back(sk_index);
+ }else{
+ Trace("sygus-unif-debug") << "...fail: duplicate argument used"
+ << std::endl;
+ cop_to_strat.erase(cop);
+ break;
+ }
+ // also store the template information, if necessary
+ Node teut;
+ if (isAssoc)
+ {
+ std::vector<unsigned>& ac = assoc_combine[k];
+ Assert(!ac.empty());
+ std::vector<Node> children;
+ for (unsigned ack = 0, size_ac = ac.size(); ack < size_ac;
+ ack++)
+ {
+ children.push_back(eut[ac[ack]]);
+ }
+ teut = children.size() == 1
+ ? children[0]
+ : nm->mkNode(eut.getKind(), children);
+ teut = Rewriter::rewrite(teut);
+ }
+ else
+ {
+ teut = ta.second;
+ }
+
+ if (!teut.isVar())
+ {
+ cop_to_child_templ[cop][k] = teut;
+ cop_to_child_templ_arg[cop][k] = ss[sk_index];
+ Trace("sygus-unif-debug")
+ << " Arg " << k << " (template : " << teut << " arg "
+ << ss[sk_index] << "), index " << sk_index << std::endl;
+ }
+ else
+ {
+ Trace("sygus-unif-debug") << " Arg " << k << ", index "
+ << sk_index << std::endl;
+ Assert(teut == ss[sk_index]);
+ }
+ }
+ else
+ {
+ Assert(isAssoc);
+ }
+ }
+ }
+ }
+ }
+ if (cop_to_strat.find(cop) == cop_to_strat.end())
+ {
+ Trace("sygus-unif") << "...constructor " << cop
+ << " does not correspond to a strategy." << std::endl;
+ search_this = true;
+ }
+ else
+ {
+ Trace("sygus-unif") << "-> constructor " << cop
+ << " matches strategy for " << eut.getKind() << "..."
+ << std::endl;
+ // collect children types
+ for (unsigned k = 0, size = cop_to_carg_list[cop].size(); k < size; k++)
+ {
+ TypeNode tn = sktns[cop_to_carg_list[cop][k]];
+ Trace("sygus-unif-debug")
+ << " Child type " << k << " : "
+ << static_cast<DatatypeType>(tn.toType()).getDatatype().getName()
+ << std::endl;
+ cop_to_child_types[cop].push_back(tn);
+ }
+ }
+ }
+
+ // check whether we should also enumerate the current type
+ Trace("sygus-unif-debug2") << " register this enumerator..." << std::endl;
+ registerEnumerator(ee, e, tn, erole, search_this);
+
+ if (cop_to_strat.empty())
+ {
+ Trace("sygus-unif") << "...consider " << dt.getName() << " a basic type"
+ << std::endl;
+ }
+ else
+ {
+ for (std::pair<const Node, std::vector<StrategyType> >& cstr : cop_to_strat)
+ {
+ Node cop = cstr.first;
+ Trace("sygus-unif-debug") << "Constructor " << cop << " has "
+ << cstr.second.size() << " strategies..."
+ << std::endl;
+ for (unsigned s = 0, ssize = cstr.second.size(); s < ssize; s++)
+ {
+ EnumTypeInfoStrat* cons_strat = new EnumTypeInfoStrat;
+ StrategyType strat = cstr.second[s];
+
+ cons_strat->d_this = strat;
+ cons_strat->d_cons = cop;
+ Trace("sygus-unif-debug") << "Process strategy #" << s
+ << " for operator : " << cop << " : " << strat
+ << std::endl;
+ Assert(cop_to_child_types.find(cop) != cop_to_child_types.end());
+ std::vector<TypeNode>& childTypes = cop_to_child_types[cop];
+ Assert(cop_to_carg_list.find(cop) != cop_to_carg_list.end());
+ std::vector<unsigned>& cargList = cop_to_carg_list[cop];
+
+ std::vector<Node> sol_templ_children;
+ sol_templ_children.resize(cop_to_sks[cop].size());
+
+ for (unsigned j = 0, csize = childTypes.size(); j < csize; j++)
+ {
+ // calculate if we should allocate a new enumerator : should be true
+ // if we have a new role
+ NodeRole nrole_c = nrole;
+ if (strat == strat_ITE)
+ {
+ if (j == 0)
+ {
+ nrole_c = role_ite_condition;
+ }
+ }
+ else if (strat == strat_CONCAT_PREFIX)
+ {
+ if ((j + 1) < childTypes.size())
+ {
+ nrole_c = role_string_prefix;
+ }
+ }
+ else if (strat == strat_CONCAT_SUFFIX)
+ {
+ if (j > 0)
+ {
+ nrole_c = role_string_suffix;
+ }
+ }
+ // in all other cases, role is same as parent
+
+ // register the child type
+ TypeNode ct = childTypes[j];
+ Node csk = cop_to_sks[cop][cargList[j]];
+ cons_strat->d_sol_templ_args.push_back(csk);
+ sol_templ_children[cargList[j]] = csk;
+
+ EnumRole erole_c = getEnumeratorRoleForNodeRole(nrole_c);
+ // 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 = nm->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, erole_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()
+ << ", node role = " << nrole_c << std::endl;
+ collectEnumeratorTypes(e, ct, nrole_c);
+ // otherwise use the previous
+ Assert(d_cinfo[e].d_tinfo[ct].d_enum.find(erole_c)
+ != d_cinfo[e].d_tinfo[ct].d_enum.end());
+ et = d_cinfo[e].d_tinfo[ct].d_enum[erole_c];
+ }
+ Trace("sygus-unif-debug") << "Register child enumerator " << et
+ << ", arg " << j << " of " << cop
+ << ", role = " << erole_c << std::endl;
+ Assert(!et.isNull());
+ cons_strat->d_cenum.push_back(std::pair<Node, NodeRole>(et, nrole_c));
+ }
+ // children that are unused in the strategy can be arbitrary
+ for (unsigned j = 0, stsize = sol_templ_children.size(); j < stsize;
+ j++)
+ {
+ if (sol_templ_children[j].isNull())
+ {
+ sol_templ_children[j] = cop_to_sks[cop][j].getType().mkGroundTerm();
+ }
+ }
+ sol_templ_children.insert(sol_templ_children.begin(), cop);
+ cons_strat->d_sol_templ =
+ nm->mkNode(APPLY_CONSTRUCTOR, sol_templ_children);
+ if (strat == strat_CONCAT_SUFFIX)
+ {
+ std::reverse(cons_strat->d_cenum.begin(), cons_strat->d_cenum.end());
+ std::reverse(cons_strat->d_sol_templ_args.begin(),
+ cons_strat->d_sol_templ_args.end());
+ }
+ if (Trace.isOn("sygus-unif"))
+ {
+ Trace("sygus-unif") << "Initialized strategy " << strat;
+ Trace("sygus-unif") << " for " << ((DatatypeType)tn.toType()).getDatatype().getName() << ", operator " << cop;
+ Trace("sygus-unif") << ", #children = " << cons_strat->d_cenum.size()
+ << ", solution template = (lambda ( ";
+ for (const Node& targ : cons_strat->d_sol_templ_args)
+ {
+ Trace("sygus-unif") << targ << " ";
+ }
+ Trace("sygus-unif") << ") " << cons_strat->d_sol_templ << ")";
+ Trace("sygus-unif") << std::endl;
+ }
+ // make the strategy
+ snode.d_strats.push_back(cons_strat);
+ }
+ }
+ }
+}
+
+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 ){
+ // two distinct variables in this term, we fail
+ 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 = " << itns->second.getRole()
+ << std::endl;
+ }
+ }
+ Trace("sygus-unif") << std::endl;
+ Trace("sygus-unif") << "Strategy for candidate " << c << " is : " << std::endl;
+ std::map<Node, std::map<NodeRole, bool> > visited;
+ std::map<Node, std::map<unsigned, bool> > needs_cons;
+ staticLearnRedundantOps(c,
+ d_cinfo[c].getRootEnumerator(),
+ role_equal,
+ visited,
+ needs_cons,
+ 0,
+ false);
+ // now, check the needs_cons map
+ for (std::pair<const Node, std::map<unsigned, bool> >& nce : needs_cons)
+ {
+ Node em = nce.first;
+ const Datatype& dt =
+ static_cast<DatatypeType>(em.getType().toType()).getDatatype();
+ for (std::pair<const unsigned, bool>& nc : nce.second)
+ {
+ Assert(nc.first < dt.getNumConstructors());
+ if (!nc.second)
+ {
+ Node tst =
+ datatypes::DatatypesRewriter::mkTester(em, nc.first, dt).negate();
+ if (std::find(lemmas.begin(), lemmas.end(), tst) == lemmas.end())
+ {
+ Trace("sygus-unif") << "...can exclude based on : " << tst
+ << std::endl;
+ lemmas.push_back(tst);
+ }
+ }
+ }
+ }
+}
+
+void CegConjecturePbe::staticLearnRedundantOps(
+ Node c,
+ Node e,
+ NodeRole nrole,
+ std::map<Node, std::map<NodeRole, bool> >& visited,
+ std::map<Node, std::map<unsigned, bool> >& needs_cons,
+ int ind,
+ bool isCond)
+{
+ std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
+ Assert( itn!=d_einfo.end() );
+
+ if (visited[e].find(nrole) == visited[e].end()
+ || (isCond && !itn->second.isConditional()))
+ {
+ visited[e][nrole] = true;
+ // if conditional
+ if (isCond)
+ {
+ itn->second.setConditional();
+ }
+ indent("sygus-unif", ind);
+ Trace("sygus-unif") << e << " :: node role : " << nrole;
+ Trace("sygus-unif")
+ << ", type : "
+ << ((DatatypeType)e.getType().toType()).getDatatype().getName();
+ if (isCond)
+ {
+ Trace("sygus-unif") << ", conditional";
+ }
+ Trace("sygus-unif") << ", enum role : " << itn->second.getRole();
+
+ if( itn->second.isTemplated() ){
+ Trace("sygus-unif") << ", templated : (lambda "
+ << itn->second.d_template_arg << " "
+ << itn->second.d_template << ")" << std::endl;
+ }else{
+ Trace("sygus-unif") << std::endl;
+ TypeNode etn = e.getType();
+
+ // enumerator type info
+ std::map< TypeNode, EnumTypeInfo >::iterator itt = d_cinfo[c].d_tinfo.find( etn );
+ Assert( itt!=d_cinfo[c].d_tinfo.end() );
+ EnumTypeInfo& tinfo = itt->second;
+
+ // strategy info
+ std::map<NodeRole, StrategyNode>::iterator itsn =
+ tinfo.d_snodes.find(nrole);
+ Assert(itsn != tinfo.d_snodes.end());
+ StrategyNode& snode = itsn->second;
+
+ if (snode.d_strats.empty())
+ {
+ return;
+ }
+ std::map<unsigned, bool> needs_cons_curr;
+ // various strategies
+ for (unsigned j = 0, size = snode.d_strats.size(); j < size; j++)
+ {
+ EnumTypeInfoStrat* etis = snode.d_strats[j];
+ StrategyType strat = etis->d_this;
+ bool newIsCond = isCond || strat == strat_ITE;
+ indent("sygus-unif", ind + 1);
+ Trace("sygus-unif") << "Strategy : " << strat
+ << ", from cons : " << etis->d_cons << std::endl;
+ int cindex = Datatype::indexOf(etis->d_cons.toExpr());
+ Assert(cindex != -1);
+ needs_cons_curr[static_cast<unsigned>(cindex)] = false;
+ for (std::pair<Node, NodeRole>& cec : etis->d_cenum)
+ {
+ // recurse
+ staticLearnRedundantOps(c,
+ cec.first,
+ cec.second,
+ visited,
+ needs_cons,
+ ind + 2,
+ newIsCond);
+ }
+ }
+ // get the master enumerator for the type of this enumerator
+ std::map<TypeNode, Node>::iterator itse =
+ d_cinfo[c].d_search_enum.find(etn);
+ if (itse == d_cinfo[c].d_search_enum.end())
+ {
+ return;
+ }
+ Node em = itse->second;
+ Assert(!em.isNull());
+ // get the current datatype
+ const Datatype& dt =
+ static_cast<DatatypeType>(etn.toType()).getDatatype();
+ // all constructors that are not a part of a strategy are needed
+ for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
+ {
+ if (needs_cons_curr.find(j) == needs_cons_curr.end())
+ {
+ needs_cons_curr[j] = true;
+ }
+ }
+ // update the constructors that the master enumerator needs
+ if (needs_cons.find(em) == needs_cons.end())
+ {
+ needs_cons[em] = needs_cons_curr;
+ }
+ else
+ {
+ for (unsigned j = 0, size = dt.getNumConstructors(); j < size; j++)
+ {
+ needs_cons[em][j] = needs_cons[em][j] || needs_cons_curr[j];
+ }
+ }
+ }
+ }else{
+ indent("sygus-unif", ind);
+ Trace("sygus-unif") << e << " :: node role : " << nrole << std::endl;
+ }
+}
+
+// ------------------------------------------- solution construction from enumeration
+
+void CegConjecturePbe::getCandidateList( std::vector< Node >& candidates, std::vector< Node >& clist ) {
+ Valuation& valuation = d_qe->getValuation();
+ 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() );
+ Node gstatus = valuation.getSatValue(it->second.d_active_guard);
+ if (!gstatus.isNull() && gstatus.getConst<bool>())
+ {
+ 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() );
+ Node gstatus = d_qe->getValuation().getSatValue(it->second.d_active_guard);
+ if (gstatus.isNull() || !gstatus.getConst<bool>())
+ {
+ Trace("sygus-pbe-enum-debug") << " ...guard is inactive." << std::endl;
+ return;
+ }
+ 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;
+ // The explanation for why the current value should be excluded in future
+ // iterations.
+ 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());
+ std::vector<Node> base_results;
+ // compte the results
+ for (unsigned j = 0, size = itx->second.size(); j < size; j++)
+ {
+ Node res = d_tds->evaluateBuiltin(xtn, bv, itx->second[j]);
+ Trace("sygus-pbe-enum-debug")
+ << "...got res = " << res << " from " << bv << std::endl;
+ base_results.push_back(res);
+ }
+ // is it excluded for domain-specific reason?
+ std::vector<Node> exp_exc_vec;
+ if (getExplanationForEnumeratorExclude(
+ c, x, v, base_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(AND, exp_exc_vec);
+ Trace("sygus-pbe-enum")
+ << " ...fail : term is excluded (domain-specific)" << std::endl;
+ }
+ else
+ {
+ // 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.getRole() == 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, size = base_results.size(); j < size; j++)
+ {
+ Node res = base_results[j];
+ 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.getRole() == 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.getRole() == enum_io)
+ {
+ // latter is the degenerate case of no examples
+ if (cond_vals.find(d_true) != cond_vals.end() || cond_vals.empty())
+ {
+ //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{
+ // 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 );
+ }
+ }
+ }
+ }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->getExplain()->getExplanationForConstantEquality(x, v);
+ }
+ Node exlem =
+ NodeManager::currentNM()->mkNode(OR, g.negate(), exp_exc.negate());
+ Trace("sygus-pbe-enum-lemma")
+ << "CegConjecturePbe : enumeration exclude lemma : " << exlem
+ << std::endl;
+ lems.push_back(exlem);
+}
+
+bool CegConjecturePbe::useStrContainsEnumeratorExclude(Node x, EnumInfo& ei)
+{
+ TypeNode xbt = d_tds->sygusToBuiltinType(x.getType());
+ if (xbt.isString())
+ {
+ std::map<Node, bool>::iterator itx = d_use_str_contains_eexc.find(x);
+ if (itx != d_use_str_contains_eexc.end())
+ {
+ return itx->second;
+ }
+ Trace("sygus-pbe-enum-debug")
+ << "Is " << x << " is str.contains exclusion?" << std::endl;
+ d_use_str_contains_eexc[x] = true;
+ for (const Node& sn : ei.d_enum_slave)
+ {
+ std::map<Node, EnumInfo>::iterator itv = d_einfo.find(sn);
+ EnumRole er = itv->second.getRole();
+ if (er != enum_io && er != enum_concat_term)
+ {
+ Trace("sygus-pbe-enum-debug") << " incompatible slave : " << sn
+ << ", role = " << er << std::endl;
+ d_use_str_contains_eexc[x] = false;
+ return false;
+ }
+ if (itv->second.isConditional())
+ {
+ Trace("sygus-pbe-enum-debug")
+ << " conditional slave : " << sn << std::endl;
+ d_use_str_contains_eexc[x] = false;
+ return false;
+ }
+ }
+ Trace("sygus-pbe-enum-debug")
+ << "...can use str.contains exclusion." << std::endl;
+ return d_use_str_contains_eexc[x];
+ }
+ return false;
+}
+
+bool CegConjecturePbe::getExplanationForEnumeratorExclude(
+ Node c,
+ Node x,
+ Node v,
+ std::vector<Node>& results,
+ EnumInfo& ei,
+ std::vector<Node>& exp)
+{
+ if (useStrContainsEnumeratorExclude(x, ei))
+ {
+ NodeManager* nm = NodeManager::currentNM();
+ // This check whether the example evaluates to something that is larger than
+ // the output for some input/output pair. If so, then this term is never
+ // useful. We generalize its explanation below.
+
+ 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 str.contains." << std::endl;
+ std::vector<unsigned> cmp_indices;
+ for (unsigned i = 0, size = results.size(); i < size; i++)
+ {
+ Assert(results[i].isConst());
+ Assert(itxo->second[i].isConst());
+ Trace("sygus-pbe-cterm-debug")
+ << " " << results[i] << " <> " << itxo->second[i];
+ Node cont = nm->mkNode(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;
+ }
+ }
+ if (!cmp_indices.empty())
+ {
+ // we check invariance with respect to a negative contains test
+ NegContainsSygusInvarianceTest ncset;
+ ncset.init(d_parent, x, itxo->second, cmp_indices);
+ // construct the generalized explanation
+ d_tds->getExplain()->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( getRole()==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::initialize(Node c, EnumRole role)
+{
+ d_parent_candidate = c;
+ d_role = role;
+}
+
+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<EnumRole, 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, role_equal, 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 = Random::getRandom().pickDouble(0.0, 1.0);
+ 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() );
+ std::random_shuffle(strs.begin(), strs.end());
+ // prefer one that has incremented by more than 0
+ for (const Node& ns : strs)
+ {
+ if (total_inc[ns] > 0)
+ {
+ return ns;
+ }
+ }
+ return strs[0];
+}
+
+Node CegConjecturePbe::constructSolution(
+ Node c, Node e, NodeRole nrole, UnifContext& x, int ind)
+{
+ TypeNode etn = e.getType();
+ if (Trace.isOn("sygus-pbe-dt-debug"))
+ {
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "ConstructPBE: (" << e << ", " << nrole
+ << ") for type " << etn << " in context ";
+ print_val("sygus-pbe-dt-debug", x.d_vals);
+ if (x.d_has_string_pos != role_invalid)
+ {
+ Trace("sygus-pbe-dt-debug") << ", string context [" << x.d_has_string_pos;
+ for (unsigned i = 0, size = x.d_str_pos.size(); i < size; i++)
+ {
+ Trace("sygus-pbe-dt-debug") << " " << x.d_str_pos[i];
+ }
+ Trace("sygus-pbe-dt-debug") << "]";
+ }
+ Trace("sygus-pbe-dt-debug") << std::endl;
+ }
+ // enumerator type info
+ std::map<TypeNode, EnumTypeInfo>::iterator itt = d_cinfo[c].d_tinfo.find(etn);
+ Assert(itt != d_cinfo[c].d_tinfo.end());
+ EnumTypeInfo& tinfo = itt->second;
+
+ // get the enumerator information
+ std::map< Node, EnumInfo >::iterator itn = d_einfo.find( e );
+ Assert( itn!=d_einfo.end() );
+ EnumInfo& einfo = itn->second;
+
+ Node ret_dt;
+ if (nrole == role_equal)
+ {
+ if (!x.isReturnValueModified())
+ {
+ if (einfo.isSolved())
+ {
+ // this type has a complete solution
+ ret_dt = einfo.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
+ {
+ // could be conditionally solved
+ std::vector<Node> subsumed_by;
+ einfo.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 (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;
+ std::map<EnumRole, Node>::iterator itnt =
+ tinfo.d_enum.find(enum_concat_term);
+ if( itnt != itt->second.d_enum.end() ){
+ Node et = itnt->second;
+ itet = d_einfo.find( et );
+ Assert(itet != d_einfo.end());
+ }else{
+ success = false;
+ }
+ if (success)
+ {
+ // 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<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, size = itet->second.d_enum_vals.size(); i < 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 (nrole == role_string_prefix || nrole == role_string_suffix)
+ {
+ // check if each return value is a prefix/suffix of all open examples
+ if (!x.isReturnValueModified() || x.d_has_string_pos == nrole)
+ {
+ std::map<Node, std::vector<unsigned> > incr;
+ bool isPrefix = nrole == role_string_prefix;
+ 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<String> ex_vals;
+ x.getCurrentStrings(this, itx->second, ex_vals);
+ if (Trace.isOn("sygus-pbe-dt-debug"))
+ {
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "current strings : " << std::endl;
+ for (unsigned i = 0, size = ex_vals.size(); i < size; i++)
+ {
+ indent("sygus-pbe-dt-debug", ind + 1);
+ Trace("sygus-pbe-dt-debug") << ex_vals[i] << std::endl;
+ }
+ }
+
+ // check if there is a value for which is a prefix/suffix of all active
+ // examples
+ Assert(einfo.d_enum_vals.size() == einfo.d_enum_vals_res.size());
+
+ for (unsigned i = 0, size = einfo.d_enum_vals.size(); i < size; i++)
+ {
+ Node val_t = einfo.d_enum_vals[i];
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "increment string values : " << val_t
+ << " : ";
+ Assert(einfo.d_enum_vals_res[i].size() == itx->second.size());
+ unsigned tot = 0;
+ bool exsuccess = x.getStringIncrement(this,
+ isPrefix,
+ ex_vals,
+ einfo.d_enum_vals_res[i],
+ incr[val_t],
+ tot);
+ 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())
+ {
+ ret_dt = constructBestStringToConcat(inc_strs, total_inc, incr, x);
+ Assert(!ret_dt.isNull());
+ indent("sygus-pbe-dt", ind);
+ Trace("sygus-pbe-dt") << "PBE: CONCAT strategy : choose "
+ << (isPrefix ? "pre" : "suf") << "fix value "
+ << d_tds->sygusToBuiltin(ret_dt) << std::endl;
+ // update the context
+ bool ret = x.updateStringPosition(this, incr[ret_dt]);
+ AlwaysAssert(ret == (total_inc[ret_dt] > 0));
+ x.d_has_string_pos = nrole;
+ }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;
+ }
+ }
+ if (ret_dt.isNull() && !einfo.isTemplated())
+ {
+ // we will try a single strategy
+ EnumTypeInfoStrat* etis = nullptr;
+ std::map<NodeRole, StrategyNode>::iterator itsn =
+ tinfo.d_snodes.find(nrole);
+ if (itsn != tinfo.d_snodes.end())
+ {
+ // strategy info
+ StrategyNode& snode = itsn->second;
+ if (x.d_visit_role[e].find(nrole) == x.d_visit_role[e].end())
+ {
+ x.d_visit_role[e][nrole] = true;
+ // try a random strategy
+ if (snode.d_strats.size() > 1)
+ {
+ std::random_shuffle(snode.d_strats.begin(), snode.d_strats.end());
+ }
+ // get an eligible strategy index
+ unsigned sindex = 0;
+ while (sindex < snode.d_strats.size()
+ && !x.isValidStrategy(snode.d_strats[sindex]))
+ {
+ sindex++;
+ }
+ // if we found a eligible strategy
+ if (sindex < snode.d_strats.size())
+ {
+ etis = snode.d_strats[sindex];
+ }
+ }
+ }
+ if (etis != nullptr)
+ {
+ StrategyType strat = etis->d_this;
+ indent("sygus-pbe-dt", ind + 1);
+ Trace("sygus-pbe-dt") << "...try STRATEGY " << strat << "..."
+ << std::endl;
+
+ std::map<unsigned, Node> look_ahead_solved_children;
+ std::vector<Node> dt_children_cons;
+ bool success = true;
+
+ // for ITE
+ Node split_cond_enum;
+ int split_cond_res_index = -1;
+
+ for (unsigned sc = 0, size = etis->d_cenum.size(); sc < size; sc++)
+ {
+ indent("sygus-pbe-dt", ind + 1);
+ Trace("sygus-pbe-dt") << "construct PBE child #" << sc << "..."
+ << std::endl;
+ Node rec_c;
+ 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
+ {
+ std::pair<Node, NodeRole>& cenum = etis->d_cenum[sc];
+
+ // update the context
+ std::vector<Node> prev;
+ if (strat == strat_ITE && sc > 0)
+ {
+ 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;
+ bool ret = x.updateContext(
+ this,
+ itnc->second.d_enum_vals_res[split_cond_res_index],
+ sc == 1);
+ AlwaysAssert(ret);
+ }
+
+ // recurse
+ if (strat == strat_ITE && sc == 0)
+ {
+ Node ce = cenum.first;
+
+ // register the condition enumerator
+ std::map<Node, EnumInfo>::iterator itnc = d_einfo.find(ce);
+ Assert(itnc != d_einfo.end());
+ EnumInfo& einfo_child = itnc->second;
+
+ // 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;
+ Assert(einfo_child.d_enum_vals.size()
+ == einfo_child.d_enum_vals_res.size());
+ for (unsigned i = 1; i <= 2; i++)
+ {
+ std::pair<Node, NodeRole>& te_pair = etis->d_cenum[i];
+ Node te = te_pair.first;
+ 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, size = einfo_child.d_enum_vals.size();
+ k < size;
+ k++)
+ {
+ Node cond = einfo_child.d_enum_vals[k];
+ std::vector<Node> solved;
+ itnt->second.d_term_trie.getSubsumedBy(
+ this,
+ einfo_child.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-debug2")
+ << " reg : PBE: ..." << d_tds->sygusToBuiltin(slv)
+ << " is a solution under branch " << i;
+ Trace("sygus-pbe-dt-debug2")
+ << " 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
+ einfo_child.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())
+ {
+ if (Trace.isOn("sygus-pbe-dt-debug"))
+ {
+ indent("sygus-pbe-dt-debug", ind + 1);
+ Trace("sygus-pbe-dt-debug")
+ << "PBE : We have " << itpc->second.size()
+ << " distinguishable conditionals:" << std::endl;
+ for (Node& cond : itpc->second)
+ {
+ indent("sygus-pbe-dt-debug", ind + 2);
+ Trace("sygus-pbe-dt-debug") << d_tds->sygusToBuiltin(cond)
+ << 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 (Node& cond : itpc->second)
+ {
+ 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 + 1);
+ 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::pair<const unsigned, Node>& las : itla->second)
+ {
+ look_ahead_solved_children[las.first] = las.second;
+ }
+ break;
+ }
+ n--;
+ }
+ }
+
+ // 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 (#1250) : 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(einfo_child.d_enum_val_to_index.find(rec_c)
+ != einfo_child.d_enum_val_to_index.end());
+ split_cond_res_index = einfo_child.d_enum_val_to_index[rec_c];
+ split_cond_enum = ce;
+ Assert(split_cond_res_index >= 0);
+ Assert(split_cond_res_index
+ < (int)einfo_child.d_enum_vals_res.size());
+ }
+ }
+ else
+ {
+ rec_c = constructSolution(c, cenum.first, cenum.second, x, ind + 2);
+ }
+
+ // undo update the context
+ if (strat == strat_ITE && sc > 0)
+ {
+ x.d_vals = prev;
+ }
+ }
+ if (!rec_c.isNull())
+ {
+ dt_children_cons.push_back(rec_c);
+ }
+ else
+ {
+ success = false;
+ break;
+ }
+ }
+ if (success)
+ {
+ Assert(dt_children_cons.size() == etis->d_sol_templ_args.size());
+ // ret_dt = NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR,
+ // dt_children );
+ ret_dt = etis->d_sol_templ;
+ ret_dt = ret_dt.substitute(etis->d_sol_templ_args.begin(),
+ etis->d_sol_templ_args.end(),
+ dt_children_cons.begin(),
+ dt_children_cons.end());
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug")
+ << "PBE: success : constructed for strategy " << strat << std::endl;
+ }else{
+ indent("sygus-pbe-dt-debug", ind);
+ Trace("sygus-pbe-dt-debug") << "PBE: failed for strategy " << strat
+ << 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;
+ }
+ }
+ }
+ if (changed)
+ {
+ d_visit_role.clear();
+ }
+ 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;
+ }
+ }
+ if (changed)
+ {
+ d_visit_role.clear();
+ }
+ return changed;
+}
+
+bool CegConjecturePbe::UnifContext::isReturnValueModified() {
+ if (d_has_string_pos != role_invalid)
+ {
+ return true;
+ }
+ return false;
+}
+
+bool CegConjecturePbe::UnifContext::isValidStrategy(EnumTypeInfoStrat* etis)
+{
+ StrategyType st = etis->d_this;
+ if (d_has_string_pos == role_string_prefix && st == strat_CONCAT_SUFFIX)
+ {
+ return false;
+ }
+ if (d_has_string_pos == role_string_suffix && st == strat_CONCAT_PREFIX)
+ {
+ return false;
+ }
+ return true;
+}
+
+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 );
+ }
+ }
+ }
+ d_visit_role.clear();
+}
+
+void CegConjecturePbe::UnifContext::getCurrentStrings(
+ CegConjecturePbe* pbe,
+ const std::vector<Node>& vals,
+ std::vector<String>& ex_vals)
+{
+ bool isPrefix = d_has_string_pos == role_string_prefix;
+ 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 != role_invalid);
+ 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,
+ const std::vector<String>& ex_vals,
+ const 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() );
+ 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,
+ const std::vector<String>& ex_vals,
+ const 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() );
+ String mystr = vals[j].getConst<String>();
+ if( ex_vals[j]!=mystr ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+CegConjecturePbe::StrategyNode::~StrategyNode()
+{
+ for (unsigned j = 0, size = d_strats.size(); j < size; j++)
+ {
+ delete d_strats[j];
+ }
+ d_strats.clear();
+}
+}
+}
+}
diff --git a/src/theory/quantifiers/sygus/sygus_pbe.h b/src/theory/quantifiers/sygus/sygus_pbe.h
new file mode 100644
index 000000000..ce1f2bf5e
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_pbe.h
@@ -0,0 +1,802 @@
+/********************* */
+/*! \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 "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** roles for enumerators
+ *
+ * This indicates the role of an enumerator that is allocated by approaches
+ * for synthesis-by-unification (see details below).
+ * io : the enumerator should enumerate values that are overall solutions
+ * for the function-to-synthesize,
+ * ite_condition : the enumerator should enumerate values that are useful
+ * in ite conditions in the ITE strategy,
+ * concat_term : the enumerator should enumerate values that are used as
+ * components of string concatenation solutions.
+ */
+enum EnumRole
+{
+ enum_invalid,
+ enum_io,
+ enum_ite_condition,
+ enum_concat_term,
+};
+std::ostream& operator<<(std::ostream& os, EnumRole r);
+
+/** roles for strategy nodes
+ *
+ * This indicates the role of a strategy node, which is a subprocedure of
+ * CegConjecturePbe::constructSolution (see details below).
+ * equal : the node constructed must be equal to the overall solution for
+ * the function-to-synthesize,
+ * string_prefix/suffix : the node constructed must be a prefix/suffix
+ * of the function-to-synthesize,
+ * ite_condition : the node constructed must be a condition that makes some
+ * active input examples true and some input examples false.
+ */
+enum NodeRole
+{
+ role_invalid,
+ role_equal,
+ role_string_prefix,
+ role_string_suffix,
+ role_ite_condition,
+};
+std::ostream& operator<<(std::ostream& os, NodeRole r);
+
+/** enumerator role for node role */
+EnumRole getEnumeratorRoleForNodeRole(NodeRole r);
+
+/** strategy types
+ *
+ * This indicates a strategy for synthesis-by-unification (see details below).
+ * ITE : strategy for constructing if-then-else solutions via decision
+ * tree learning techniques,
+ * CONCAT_PREFIX/SUFFIX : strategy for constructing string concatenation
+ * solutions via a divide and conquer approach,
+ * ID : identity strategy used for calling strategies on child type through
+ * an identity function.
+ */
+enum StrategyType
+{
+ strat_INVALID,
+ strat_ITE,
+ strat_CONCAT_PREFIX,
+ strat_CONCAT_SUFFIX,
+ strat_ID,
+};
+std::ostream& operator<<(std::ostream& os, StrategyType st);
+
+class CegConjecture;
+
+/** CegConjecturePbe
+*
+* This class implements optimizations that target synthesis conjectures
+* that are in Programming-By-Examples (PBE) form.
+*
+* [EX#1] An example of a synthesis conjecture in PBE form is :
+* exists f. forall x.
+* ( x = 0 => f( x ) = 2 ) ^ ( x = 5 => f( x ) = 7 ) ^ ( x = 6 => f( x ) = 8 )
+*
+* We say that the above conjecture has I/O examples (0)->2, (5)->7, (6)->8.
+*
+* Internally, this class does the following for SyGuS inputs:
+*
+* (1) Infers whether the input conjecture is in PBE form or not.
+* (2) Based on this information and on the syntactic restrictions, it
+* devises a strategy for enumerating terms and construction solutions,
+* which is inspired by Alur et al. "Scaling Enumerative Program Synthesis
+* via Divide and Conquer" TACAS 2017. In particular, it may consider
+* strategies for constructing decision trees when the grammar permits ITEs
+* and a strategy for divide-and-conquer string synthesis when the grammar
+* permits string concatenation. This is stored in a set of data structures
+* within d_cinfo.
+* (3) It makes (possibly multiple) calls to
+* TermDatabaseSygus::registerMeasuredTerm(...) based
+* on the strategy, which inform the rest of the system to enumerate values
+* of particular types in the grammar through use of fresh variables which
+* we call "enumerators".
+*
+* Points (1)-(3) happen within a call to CegConjecturePbe::initialize(...).
+*
+* Notice that each enumerator is associated with a single
+* function-to-synthesize, but a function-to-sythesize may be mapped to multiple
+* enumerators. Some public functions of this class expect an enumerator as
+* input, which we map to a function-to-synthesize via
+* TermDatabaseSygus::getSynthFunFor(e).
+*
+* An enumerator is initially "active" but may become inactive if the enumeration
+* exhausts all possible values in the datatype corresponding to syntactic
+* restrictions for it. The search may continue unless all enumerators become
+* inactive.
+*
+* (4) During search, the extension of quantifier-free datatypes procedure for
+* SyGuS datatypes may ask this class whether current candidates can be
+* discarded based on
+* inferring when two candidate solutions are equivalent up to examples.
+* For example, the candidate solutions:
+* f = \x ite( x<0, x+1, x ) and f = \x x
+* are equivalent up to examples on the above conjecture, since they have the
+* same value on the points x = 0,5,6. Hence, we need only consider one of
+* them. The interface for querying this is
+* CegConjecturePbe::addSearchVal(...).
+* For details, see Reynolds et al. SYNT 2017.
+*
+* (5) When the extension of quantifier-free datatypes procedure for SyGuS
+* datatypes terminates with a model, the parent of this class calls
+* CegConjecturePbe::getCandidateList(...), where this class returns the list
+* of active enumerators.
+* (6) The parent class subsequently calls
+* CegConjecturePbe::constructValues(...), which
+* informs this class that new values have been enumerated for active
+* enumerators, as indicated by the current model. This call also requests
+* that based on these
+* newly enumerated values, whether this class is now able to construct a
+* solution based on the high-level strategy (stored in d_c_info).
+*
+* This class is not designed to work in incremental mode, since there is no way
+* to specify incremental problems in SyguS.
+*/
+class CegConjecturePbe {
+ public:
+ CegConjecturePbe(QuantifiersEngine* qe, CegConjecture* p);
+ ~CegConjecturePbe();
+
+ /** initialize this class
+ *
+ * n is the "base instantiation" of the deep-embedding version of
+ * the synthesis conjecture under "candidates".
+ * (see CegConjecture::d_base_inst)
+ *
+ * This function may add lemmas to the vector lemmas corresponding
+ * to initial lemmas regarding static analysis of enumerators it
+ * introduced. For example, we may say that the top-level symbol
+ * of an enumerator is not ITE if it is being used to construct
+ * return values for decision trees.
+ */
+ void initialize(Node n,
+ std::vector<Node>& candidates,
+ std::vector<Node>& lemmas);
+ /** get candidate list
+ * Adds all active enumerators associated with functions-to-synthesize in
+ * candidates to clist.
+ */
+ void getCandidateList(std::vector<Node>& candidates,
+ std::vector<Node>& clist);
+ /** construct candidates
+ * (1) Indicates that the list of enumerators in "enums" currently have model
+ * values "enum_values".
+ * (2) Asks whether based on these new enumerated values, we can construct a
+ * solution for
+ * the functions-to-synthesize in "candidates". If so, this function
+ * returns "true" and
+ * adds solutions for candidates into "candidate_values".
+ * During this class, this class may add auxiliary lemmas to "lems", which the
+ * caller should send on the output channel via lemma(...).
+ */
+ bool constructCandidates(std::vector<Node>& enums,
+ std::vector<Node>& enum_values,
+ std::vector<Node>& candidates,
+ std::vector<Node>& candidate_values,
+ std::vector<Node>& lems);
+ /** is PBE enabled for any enumerator? */
+ bool isPbe() { return d_is_pbe; }
+ /** is the enumerator e associated with I/O example pairs? */
+ bool hasExamples(Node e);
+ /** get number of I/O example pairs for enumerator e */
+ unsigned getNumExamples(Node e);
+ /** get the input arguments for i^th I/O example for e, which is added to the
+ * vector ex */
+ void getExample(Node e, unsigned i, std::vector<Node>& ex);
+ /** get the output value of the i^th I/O example for enumerator e */
+ Node getExampleOut(Node e, unsigned i);
+
+ /** add the search val
+ * This function is called by the extension of quantifier-free datatypes
+ * procedure for SyGuS datatypes when we are considering a value of
+ * enumerator e of sygus type tn whose analog in the signature of builtin
+ * theory is bvr.
+ *
+ * For example, bvr = x + 1 when e is the datatype value Plus( x(), One() ) and
+ * tn is a sygus datatype that encodes a subsignature of the integers.
+ *
+ * This returns either:
+ * - A SyGuS term whose analog is equivalent to bvr up to examples
+ * In the above example,
+ * it may return a term t of the form Plus( One(), x() ), such that this
+ * function was previously called with t as input.
+ * - e, indicating that no previous terms are equivalent to e up to examples.
+ */
+ Node addSearchVal(TypeNode tn, Node e, Node bvr);
+ /** evaluate builtin
+ * This returns the evaluation of bn on the i^th example for the
+ * function-to-synthesis
+ * associated with enumerator e. If there are not at least i examples, it
+ * returns the rewritten form of bn.
+ * For example, if bn = x+5, e is an enumerator for f in the above example
+ * [EX#1], then
+ * evaluateBuiltin( tn, bn, e, 0 ) = 7
+ * evaluateBuiltin( tn, bn, e, 1 ) = 9
+ * evaluateBuiltin( tn, bn, e, 2 ) = 10
+ */
+ Node evaluateBuiltin(TypeNode tn, Node bn, Node e, unsigned i);
+
+ private:
+ /** quantifiers engine associated with this class */
+ QuantifiersEngine* d_qe;
+ /** sygus term database of d_qe */
+ quantifiers::TermDbSygus * d_tds;
+ /** true and false nodes */
+ Node d_true;
+ Node d_false;
+ /** A reference to the conjecture that owns this class. */
+ CegConjecture* d_parent;
+ /** is this a PBE conjecture for any function? */
+ bool d_is_pbe;
+ /** for each candidate variable f (a function-to-synthesize), whether the
+ * conjecture is purely PBE for that variable
+ * In other words, all occurrences of f are guarded by equalities that
+ * constraint its arguments to constants.
+ */
+ std::map< Node, bool > d_examples_invalid;
+ /** for each candidate variable (function-to-synthesize), whether the
+ * conjecture is purely PBE for that variable.
+ * An example of a conjecture for which d_examples_invalid is false but
+ * d_examples_out_invalid is true is:
+ * exists f. forall x. ( x = 0 => f( x ) > 2 )
+ * another example is:
+ * exists f. forall x. ( ( x = 0 => f( x ) = 2 ) V ( x = 3 => f( x ) = 3 ) )
+ * since the formula is not a conjunction (the example values are not
+ * entailed).
+ * However, the domain of f in both cases is finite, which can be used for
+ * search space pruning.
+ */
+ std::map< Node, bool > d_examples_out_invalid;
+ /** for each candidate variable (function-to-synthesize), input of I/O
+ * examples */
+ std::map< Node, std::vector< std::vector< Node > > > d_examples;
+ /** for each candidate variable (function-to-synthesize), output of I/O
+ * examples */
+ std::map< Node, std::vector< Node > > d_examples_out;
+ /** the list of example terms
+ * For the example [EX#1] above, this is f( 0 ), f( 5 ), f( 6 )
+ */
+ std::map< Node, std::vector< Node > > d_examples_term;
+ /** collect the PBE examples in n
+ * This is called on the input conjecture, and will populate the above vectors.
+ * hasPol/pol denote the polarity of n in the conjecture.
+ */
+ void collectExamples( Node n, std::map< Node, bool >& visited, bool hasPol, bool pol );
+
+ //--------------------------------- PBE search values
+ /** this class is an index of candidate solutions for PBE synthesis */
+ class PbeTrie {
+ 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, CegConjecturePbe* cpbe,
+ unsigned index, unsigned ntotal);
+
+ private:
+ Node addPbeExampleEval(TypeNode etn, Node e, Node b, std::vector<Node>& ex,
+ CegConjecturePbe* cpbe, unsigned index,
+ unsigned ntotal);
+ };
+ /** trie of candidate solutions tried
+ * This stores information for each (enumerator, type),
+ * where type is a type in the grammar of the space of solutions for a subterm
+ * of e. This is used for symmetry breaking in quantifier-free reasoning
+ * about SyGuS datatypes.
+ */
+ std::map<Node, std::map<TypeNode, PbeTrie> > d_pbe_trie;
+ //--------------------------------- end PBE search values
+
+ // -------------------------------- decision tree learning
+ // index filter
+ 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 );
+ };
+ // subsumption trie
+ class SubsumeTrie {
+ public:
+ SubsumeTrie(){}
+ // 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 );
+ // 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 );
+ /** is this trie empty? */
+ bool isEmpty() { return d_term.isNull() && d_children.empty(); }
+ /** clear this trie */
+ void clear() {
+ d_term = Node::null();
+ d_children.clear();
+ }
+
+ private:
+ /** the term at this node */
+ Node d_term;
+ /** the children nodes of this trie */
+ std::map<Node, SubsumeTrie> d_children;
+ /** helper function for above functions */
+ 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);
+ /** helper function for above functions */
+ void getLeavesInternal(CegConjecturePbe* pbe,
+ std::vector<Node>& vals,
+ bool pol,
+ std::map<int, std::vector<Node> >& v,
+ IndexFilter* f,
+ unsigned index,
+ int status);
+ };
+ // -------------------------------- end decision tree learning
+
+ //------------------------------ representation of a enumeration strategy
+
+ /** information about an enumerator
+ *
+ * We say an enumerator is a master enumerator if it is the variable that
+ * we use to enumerate values for its sort. Master enumerators may have
+ * (possibly multiple) slave enumerators, stored in d_enum_slave,
+ */
+ class EnumInfo {
+ public:
+ EnumInfo() : d_role(enum_io), d_is_conditional(false) {}
+ /** initialize this class
+ * c is the parent function-to-synthesize
+ * role is the "role" the enumerator plays in the high-level strategy,
+ * which is one of enum_* above.
+ */
+ void initialize(Node c, EnumRole role);
+ /** is this enumerator associated with a template? */
+ bool isTemplated() { return !d_template.isNull(); }
+ /** set conditional
+ *
+ * This flag is set to true if this enumerator may not apply to all
+ * input/output examples. For example, if this enumerator is used
+ * as an output value beneath a conditional in an instance of strat_ITE,
+ * then this enumerator is conditional.
+ */
+ void setConditional() { d_is_conditional = true; }
+ /** is conditional */
+ bool isConditional() { return d_is_conditional; }
+ 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; }
+ EnumRole getRole() { return d_role; }
+ Node d_parent_candidate;
+ // for template
+ Node d_template;
+ Node d_template_arg;
+
+ 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;
+
+ private:
+ /**
+ * Whether an enumerated value for this conjecture has solved the entire
+ * conjecture.
+ */
+ Node d_enum_solved;
+ /** the role of this enumerator (one of enum_* above). */
+ EnumRole d_role;
+ /** is this enumerator conditional */
+ bool d_is_conditional;
+ };
+ /** maps enumerators to the information above */
+ std::map< Node, EnumInfo > d_einfo;
+
+ class CandidateInfo;
+
+ /** represents a strategy for a SyGuS datatype type
+ *
+ * This represents a possible strategy to apply when processing a strategy
+ * node in constructSolution. When applying the strategy represented by this
+ * class, we may make recursive calls to the children of the strategy,
+ * given in d_cenum. If all recursive calls to constructSolution are
+ * successful, say:
+ * constructSolution( c, d_cenum[1], ... ) = t1,
+ * ...,
+ * constructSolution( c, d_cenum[n], ... ) = tn,
+ * Then, the solution returned by this strategy is
+ * d_sol_templ * { d_sol_templ_args -> (t1,...,tn) }
+ */
+ class EnumTypeInfoStrat {
+ public:
+ /** the type of strategy this represents */
+ StrategyType d_this;
+ /** the sygus datatype constructor that induced this strategy
+ *
+ * For example, this may be a sygus datatype whose sygus operator is ITE,
+ * if the strategy type above is strat_ITE.
+ */
+ Node d_cons;
+ /** children of this strategy */
+ std::vector<std::pair<Node, NodeRole> > d_cenum;
+ /** the arguments for the (templated) solution */
+ std::vector<Node> d_sol_templ_args;
+ /** the template for the solution */
+ Node d_sol_templ;
+ };
+
+ /** represents a node in the strategy graph
+ *
+ * It contains a list of possible strategies which are tried during calls
+ * to constructSolution.
+ */
+ class StrategyNode
+ {
+ public:
+ StrategyNode() {}
+ ~StrategyNode();
+ /** the set of strategies to try at this node in the strategy graph */
+ std::vector<EnumTypeInfoStrat*> d_strats;
+ };
+
+ /** stores enumerators and strategies for a SyGuS datatype type */
+ class EnumTypeInfo {
+ public:
+ EnumTypeInfo() : d_parent( NULL ){}
+ /** the parent candidate info (see below) */
+ CandidateInfo * d_parent;
+ /** the type that this information is for */
+ TypeNode d_this_type;
+ /** map from enum roles to enumerators for this type */
+ std::map<EnumRole, Node> d_enum;
+ /** map from node roles to strategy nodes */
+ std::map<NodeRole, StrategyNode> d_snodes;
+ };
+
+ /** stores strategy and enumeration information for a function-to-synthesize
+ */
+ class CandidateInfo {
+ public:
+ CandidateInfo() : d_check_sol( false ), d_cond_count( 0 ){}
+ Node d_this_candidate;
+ /**
+ * The root sygus datatype for the function-to-synthesize,
+ * which encodes the overall syntactic restrictions on the space
+ * of solutions.
+ */
+ TypeNode d_root;
+ /** Info for sygus datatype type occurring in a field of d_root */
+ std::map< TypeNode, EnumTypeInfo > d_tinfo;
+ /** list of all enumerators for the function-to-synthesize */
+ std::vector< Node > d_esym_list;
+ /**
+ * Maps sygus datatypes to their search enumerator. This is the (single)
+ * enumerator of that type that we enumerate values for.
+ */
+ 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();
+ };
+ /** maps a function-to-synthesize to the above information */
+ std::map< Node, CandidateInfo > d_cinfo;
+
+ //------------------------------ representation of an enumeration strategy
+ /** add enumerated value
+ *
+ * We have enumerated the value v for x. This function adds x->v to the
+ * relevant data structures that are used for strategy-specific construction
+ * of solutions when necessary, and returns a set of lemmas, which are added
+ * to the input argument lems. These lemmas are used to rule out models where
+ * x = v, to force that a new value is enumerated for x.
+ */
+ void addEnumeratedValue( Node x, Node v, std::vector< Node >& lems );
+ /** domain-specific enumerator exclusion techniques
+ *
+ * Returns true if the value v for x can be excluded based on a
+ * domain-specific exclusion technique like the ones below.
+ *
+ * c : the candidate variable that x is enumerating for,
+ * results : the values of v under the input examples of c,
+ * ei : the enumerator information for x,
+ * exp : if this function returns true, then exp contains a (possibly
+ * generalize) explanation for why v can be excluded.
+ */
+ bool getExplanationForEnumeratorExclude( Node c, Node x, Node v, std::vector< Node >& results, EnumInfo& ei, std::vector< Node >& exp );
+ /** returns true if we can exlude values of x based on negative str.contains
+ *
+ * Values v for x may be excluded if we realize that the value of v under the
+ * substitution for some input example will never be contained in some output
+ * example. For details on this technique, see NegContainsSygusInvarianceTest
+ * in sygus_invariance.h.
+ *
+ * This function depends on whether x is being used to enumerate values
+ * for any node that is conditional in the strategy graph. For example,
+ * nodes that are children of ITE strategy nodes are conditional. If any node
+ * is conditional, then this function returns false.
+ */
+ bool useStrContainsEnumeratorExclude(Node x, EnumInfo& ei);
+ /** cache for the above function */
+ std::map<Node, bool> d_use_str_contains_eexc;
+
+ //------------------------------ strategy registration
+ /** collect enumerator types
+ *
+ * This builds the strategy for enumerated values of type tn for the given
+ * role of nrole, for solutions to function-to-synthesize c.
+ */
+ void collectEnumeratorTypes(Node c, TypeNode tn, NodeRole nrole);
+ /** register enumerator
+ *
+ * This registers that et is an enumerator for function-to-synthesize c
+ * of type tn, having enumerator role enum_role.
+ *
+ * inSearch is whether we will enumerate values based on this enumerator.
+ * A strategy node is represented by a (enumerator, node role) pair. Hence,
+ * we may use enumerators for which this flag is false to represent strategy
+ * nodes that have child strategies.
+ */
+ void registerEnumerator(
+ Node et, Node c, TypeNode tn, EnumRole enum_role, bool inSearch);
+ /** infer template */
+ bool inferTemplate(unsigned k,
+ Node n,
+ std::map<Node, unsigned>& templ_var_index,
+ std::map<unsigned, unsigned>& templ_injection);
+ /** static learn redundant operators
+ *
+ * This learns static lemmas for pruning enumerative space based on the
+ * strategy for the function-to-synthesize c, and stores these into lemmas.
+ */
+ void staticLearnRedundantOps(Node c, std::vector<Node>& lemmas);
+ /** helper for static learn redundant operators
+ *
+ * (e, nrole) specify the strategy node in the graph we are currently
+ * analyzing, visited stores the nodes we have already visited.
+ *
+ * This method builds the mapping needs_cons, which maps (master) enumerators
+ * to a map from the constructors that it needs.
+ *
+ * ind is the depth in the strategy graph we are at (for debugging).
+ *
+ * isCond is whether the current enumerator is conditional (beneath a
+ * conditional of an strat_ITE strategy).
+ */
+ void staticLearnRedundantOps(
+ Node c,
+ Node e,
+ NodeRole nrole,
+ std::map<Node, std::map<NodeRole, bool> >& visited,
+ std::map<Node, std::map<unsigned, bool> >& needs_cons,
+ int ind,
+ bool isCond);
+ //------------------------------ end strategy registration
+
+ //------------------------------ constructing solutions
+ class UnifContext {
+ public:
+ UnifContext() : d_has_string_pos(role_invalid) {}
+ /** this intiializes this context for function-to-synthesize c */
+ void initialize(CegConjecturePbe* pbe, Node c);
+
+ //----------for ITE strategy
+ /** the value of the context conditional
+ *
+ * This stores a list of Boolean constants that is the same length of the
+ * number of input/output example pairs we are considering. For each i,
+ * if d_vals[i] = true, i/o pair #i is active according to this context
+ * if d_vals[i] = false, i/o pair #i is inactive according to this context
+ */
+ std::vector<Node> d_vals;
+ /** update the examples
+ *
+ * if pol=true, this method updates d_vals to d_vals & vals
+ * if pol=false, this method updates d_vals to d_vals & ( ~vals )
+ */
+ bool updateContext(CegConjecturePbe* pbe, std::vector<Node>& vals, bool pol);
+ //----------end for ITE strategy
+
+ //----------for CONCAT strategies
+ /** the position in the strings
+ *
+ * For each i/o example pair, this stores the length of the current solution
+ * for the input of the pair, where the solution for that input is a prefix
+ * or
+ * suffix of the output of the pair. For example, if our i/o pairs are:
+ * f( "abcd" ) = "abcdcd"
+ * f( "aa" ) = "aacd"
+ * If the solution we have currently constructed is str.++( x1, "c", ... ),
+ * then d_str_pos = ( 5, 3 ), where notice that
+ * str.++( "abc", "c" ) is a prefix of "abcdcd" and
+ * str.++( "aa", "c" ) is a prefix of "aacd".
+ */
+ std::vector<unsigned> d_str_pos;
+ /** has string position
+ *
+ * Whether the solution positions indicate a prefix or suffix of the output
+ * examples. If this is role_invalid, then we have not updated the string
+ * position.
+ */
+ NodeRole d_has_string_pos;
+ /** update the string examples
+ *
+ * This method updates d_str_pos to d_str_pos + pos.
+ */
+ bool updateStringPosition(CegConjecturePbe* pbe, std::vector<unsigned>& pos);
+ /** get current strings
+ *
+ * This returns the prefix/suffix of the string constants stored in vals
+ * of size d_str_pos, and stores the result in ex_vals. For example, if vals
+ * is (abcdcd", "aacde") and d_str_pos = ( 5, 3 ), then we add
+ * "d" and "de" to ex_vals.
+ */
+ void getCurrentStrings(CegConjecturePbe* pbe,
+ const std::vector<Node>& vals,
+ std::vector<String>& ex_vals);
+ /** get string increment
+ *
+ * If this method returns true, then inc and tot are updated such that
+ * for all active indices i,
+ * vals[i] is a prefix (or suffix if isPrefix=false) of ex_vals[i], and
+ * inc[i] = str.len(vals[i])
+ * for all inactive indices i, inc[i] = 0
+ * We set tot to the sum of inc[i] for i=1,...,n. This indicates the total
+ * number of characters incremented across all examples.
+ */
+ bool getStringIncrement(CegConjecturePbe* pbe,
+ bool isPrefix,
+ const std::vector<String>& ex_vals,
+ const std::vector<Node>& vals,
+ std::vector<unsigned>& inc,
+ unsigned& tot);
+ /** returns true if ex_vals[i] = vals[i] for all active indices i. */
+ bool isStringSolved(CegConjecturePbe* pbe,
+ const std::vector<String>& ex_vals,
+ const std::vector<Node>& vals);
+ //----------end for CONCAT strategies
+
+ /** is return value modified?
+ *
+ * This returns true if we are currently in a state where the return value
+ * of the solution has been modified, e.g. by a previous node that solved
+ * for a prefix.
+ */
+ bool isReturnValueModified();
+ /** returns true if argument is valid strategy in this context */
+ bool isValidStrategy(EnumTypeInfoStrat* etis);
+ /** visited role
+ *
+ * This is the current set of enumerator/node role pairs we are currently
+ * visiting. This set is cleared when the context is updated.
+ */
+ std::map<Node, std::map<NodeRole, bool> > d_visit_role;
+
+ /** unif context enumerator information */
+ class UEnumInfo
+ {
+ public:
+ UEnumInfo() {}
+ /** map from conditions and branch positions to a solved node
+ *
+ * For example, if we have:
+ * f( 1 ) = 2 ^ f( 3 ) = 4 ^ f( -1 ) = 1
+ * Then, valid entries in this map is:
+ * d_look_ahead_sols[x>0][1] = x+1
+ * d_look_ahead_sols[x>0][2] = 1
+ * For the first entry, notice that for all input examples such that x>0
+ * evaluates to true, which are (1) and (3), we have that their output
+ * values for x+1 under the substitution that maps x to the input value,
+ * resulting in 2 and 4, are equal to the output value for the respective
+ * pairs.
+ */
+ std::map<Node, std::map<unsigned, Node> > d_look_ahead_sols;
+ };
+ /** map from enumerators to the above info class */
+ std::map< Node, UEnumInfo > d_uinfo;
+ };
+
+ /** construct solution
+ *
+ * This method tries to construct a solution for function-to-synthesize c
+ * based on the strategy stored for c in d_cinfo, which may include
+ * synthesis-by-unification approaches for ite and string concatenation terms.
+ * These approaches include the work of Alur et al. TACAS 2017.
+ * If it cannot construct a solution, it returns the null node.
+ */
+ Node constructSolution( Node c );
+ /** helper function for construct solution.
+ *
+ * Construct a solution based on enumerator e for function-to-synthesize c
+ * with node role nrole in context x.
+ *
+ * ind is the term depth of the context (for debugging).
+ */
+ Node constructSolution(
+ Node c, Node e, NodeRole nrole, UnifContext& x, int ind);
+ /** Heuristically choose the best solved term from solved in context x,
+ * currently return the first. */
+ Node constructBestSolvedTerm( std::vector< Node >& solved, UnifContext& x );
+ /** Heuristically choose the best solved string term from solved in context
+ * x, currently return the first. */
+ Node constructBestStringSolvedTerm( std::vector< Node >& solved, UnifContext& x );
+ /** Heuristically choose the best solved conditional term from solved in
+ * context x, currently random */
+ Node constructBestSolvedConditional( std::vector< Node >& solved, UnifContext& x );
+ /** Heuristically choose the best conditional term from conds in context x,
+ * currently random */
+ Node constructBestConditional( std::vector< Node >& conds, UnifContext& x );
+ /** Heuristically choose the best string to concatenate from strs to the
+ * solution in context x, currently random
+ * incr stores the vector of indices that are incremented by this solution in
+ * example outputs.
+ * total_inc[x] is the sum of incr[x] for each x in strs.
+ */
+ Node constructBestStringToConcat( std::vector< Node > strs,
+ std::map< Node, unsigned > total_inc,
+ std::map< Node, std::vector< unsigned > > incr,
+ UnifContext& x );
+ //------------------------------ end constructing solutions
+};
+
+}/* namespace CVC4::theory::quantifiers */
+}/* namespace CVC4::theory */
+}/* namespace CVC4 */
+
+#endif
diff --git a/src/theory/quantifiers/sygus/sygus_process_conj.cpp b/src/theory/quantifiers/sygus/sygus_process_conj.cpp
new file mode 100644
index 000000000..a961c9780
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_process_conj.cpp
@@ -0,0 +1,798 @@
+/********************* */
+/*! \file sygus_process_conj.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of techniqures for static preprocessing and analysis
+ ** of sygus conjectures.
+ **/
+#include "theory/quantifiers/sygus/sygus_process_conj.h"
+
+#include <stack>
+
+#include "expr/datatype.h"
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+#include "theory/quantifiers/term_util.h"
+
+using namespace CVC4::kind;
+using namespace std;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+void CegConjectureProcessFun::init(Node f)
+{
+ d_synth_fun = f;
+ Assert(f.getType().isFunction());
+
+ // initialize the arguments
+ std::unordered_map<TypeNode, unsigned, TypeNodeHashFunction>
+ type_to_init_deq_id;
+ std::vector<Type> argTypes =
+ static_cast<FunctionType>(f.getType().toType()).getArgTypes();
+ for (unsigned j = 0; j < argTypes.size(); j++)
+ {
+ TypeNode atn = TypeNode::fromType(argTypes[j]);
+ std::stringstream ss;
+ ss << "a" << j;
+ Node k = NodeManager::currentNM()->mkBoundVar(ss.str(), atn);
+ d_arg_vars.push_back(k);
+ d_arg_var_num[k] = j;
+ d_arg_props.push_back(CegConjectureProcessArg());
+ }
+}
+
+bool CegConjectureProcessFun::checkMatch(
+ Node cn, Node n, std::unordered_map<unsigned, Node>& n_arg_map)
+{
+ std::vector<Node> vars;
+ std::vector<Node> subs;
+ for (std::unordered_map<unsigned, Node>::iterator it = n_arg_map.begin();
+ it != n_arg_map.end();
+ ++it)
+ {
+ Assert(it->first < d_arg_vars.size());
+ Assert(
+ it->second.getType().isComparableTo(d_arg_vars[it->first].getType()));
+ vars.push_back(d_arg_vars[it->first]);
+ subs.push_back(it->second);
+ }
+ Node cn_subs =
+ cn.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
+ cn_subs = Rewriter::rewrite(cn_subs);
+ Assert(Rewriter::rewrite(n) == n);
+ return cn_subs == n;
+}
+
+bool CegConjectureProcessFun::isArgVar(Node n, unsigned& arg_index)
+{
+ if (n.isVar())
+ {
+ std::unordered_map<Node, unsigned, NodeHashFunction>::iterator ita =
+ d_arg_var_num.find(n);
+ if (ita != d_arg_var_num.end())
+ {
+ arg_index = ita->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+Node CegConjectureProcessFun::inferDefinition(
+ Node n,
+ std::unordered_map<Node, unsigned, NodeHashFunction>& term_to_arg_carry,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars)
+{
+ std::unordered_map<TNode, Node, TNodeHashFunction> visited;
+ std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it;
+ std::stack<TNode> visit;
+ TNode cur;
+ visit.push(n);
+ do
+ {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+ if (it == visited.end())
+ {
+ // if it is ground, we can use it
+ if (free_vars[cur].empty())
+ {
+ visited[cur] = cur;
+ }
+ else
+ {
+ // if it is term used by another argument, use it
+ std::unordered_map<Node, unsigned, NodeHashFunction>::iterator itt =
+ term_to_arg_carry.find(cur);
+ if (itt != term_to_arg_carry.end())
+ {
+ visited[cur] = d_arg_vars[itt->second];
+ }
+ else if (cur.getNumChildren() > 0)
+ {
+ // try constructing children
+ visited[cur] = Node::null();
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ visit.push(cur[i]);
+ }
+ }
+ else
+ {
+ return Node::null();
+ }
+ }
+ }
+ else if (it->second.isNull())
+ {
+ Node ret = cur;
+ bool childChanged = false;
+ std::vector<Node> children;
+ if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
+ {
+ children.push_back(cur.getOperator());
+ }
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ it = visited.find(cur[i]);
+ Assert(it != visited.end());
+ Assert(!it->second.isNull());
+ childChanged = childChanged || cur[i] != it->second;
+ children.push_back(it->second);
+ }
+ if (childChanged)
+ {
+ ret = NodeManager::currentNM()->mkNode(cur.getKind(), children);
+ }
+ visited[cur] = ret;
+ }
+ } while (!visit.empty());
+ Assert(visited.find(n) != visited.end());
+ Assert(!visited.find(n)->second.isNull());
+ return visited[n];
+}
+
+unsigned CegConjectureProcessFun::assignRelevantDef(Node def,
+ std::vector<unsigned>& args)
+{
+ unsigned id = 0;
+ if (def.isNull())
+ {
+ // prefer one that already has a definition, if one exists
+ for (unsigned j = 0; j < args.size(); j++)
+ {
+ unsigned i = args[j];
+ if (!d_arg_props[i].d_template.isNull())
+ {
+ id = j;
+ break;
+ }
+ }
+ }
+ unsigned rid = args[id];
+ // for merging previously equivalent definitions
+ std::unordered_map<Node, unsigned, NodeHashFunction> prev_defs;
+ for (unsigned j = 0; j < args.size(); j++)
+ {
+ unsigned i = args[j];
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << i;
+ if (!d_arg_props[i].d_template.isNull())
+ {
+ if (d_arg_props[i].d_template == def)
+ {
+ // definition was consistent
+ }
+ else
+ {
+ Node t = d_arg_props[i].d_template;
+ std::unordered_map<Node, unsigned, NodeHashFunction>::iterator itt =
+ prev_defs.find(t);
+ if (itt != prev_defs.end())
+ {
+ // merge previously equivalent definitions
+ d_arg_props[i].d_template = d_arg_vars[itt->second];
+ Trace("sygus-process-arg-deps")
+ << " (merged equivalent def from argument ";
+ Trace("sygus-process-arg-deps") << itt->second << ")." << std::endl;
+ }
+ else
+ {
+ // store this as previous
+ prev_defs[t] = i;
+ // now must be relevant
+ d_arg_props[i].d_relevant = true;
+ Trace("sygus-process-arg-deps")
+ << " (marked relevant, overwrite definition)." << std::endl;
+ }
+ }
+ }
+ else
+ {
+ if (def.isNull())
+ {
+ if (i != rid)
+ {
+ // marked as relevant, but template can be set equal to master
+ d_arg_props[i].d_template = d_arg_vars[rid];
+ Trace("sygus-process-arg-deps") << " (new definition, map to master "
+ << d_arg_vars[rid] << ")."
+ << std::endl;
+ }
+ else
+ {
+ d_arg_props[i].d_relevant = true;
+ Trace("sygus-process-arg-deps") << " (marked relevant)." << std::endl;
+ }
+ }
+ else
+ {
+ // has new definition
+ d_arg_props[i].d_template = def;
+ Trace("sygus-process-arg-deps") << " (new definition " << def << ")."
+ << std::endl;
+ }
+ }
+ }
+ return rid;
+}
+
+void CegConjectureProcessFun::processTerms(
+ std::vector<Node>& ns,
+ std::vector<Node>& ks,
+ Node nf,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars)
+{
+ Assert(ns.size() == ks.size());
+ Trace("sygus-process-arg-deps") << "Process " << ns.size()
+ << " applications of " << d_synth_fun << "..."
+ << std::endl;
+
+ // get the relevant variables
+ // relevant variables are those that appear in the body of the conjunction
+ std::unordered_set<Node, NodeHashFunction> rlv_vars;
+ Assert(free_vars.find(nf) != free_vars.end());
+ rlv_vars = free_vars[nf];
+
+ // get the single occurrence variables
+ // single occurrence variables are those that appear in only one position,
+ // as an argument to the function-to-synthesize.
+ std::unordered_map<Node, bool, NodeHashFunction> single_occ_variables;
+ for (unsigned index = 0; index < ns.size(); index++)
+ {
+ Node n = ns[index];
+ for (unsigned i = 0; i < n.getNumChildren(); i++)
+ {
+ Node nn = n[i];
+ if (nn.isVar())
+ {
+ std::unordered_map<Node, bool, NodeHashFunction>::iterator its =
+ single_occ_variables.find(nn);
+ if (its == single_occ_variables.end())
+ {
+ // only irrelevant variables
+ single_occ_variables[nn] = rlv_vars.find(nn) == rlv_vars.end();
+ }
+ else
+ {
+ single_occ_variables[nn] = false;
+ }
+ }
+ else
+ {
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>::iterator itf = free_vars.find(nn);
+ Assert(itf != free_vars.end());
+ for (std::unordered_set<Node, NodeHashFunction>::iterator itfv =
+ itf->second.begin();
+ itfv != itf->second.end();
+ ++itfv)
+ {
+ single_occ_variables[*itfv] = false;
+ }
+ }
+ }
+ }
+
+ // update constant argument information
+ for (unsigned index = 0; index < ns.size(); index++)
+ {
+ Node n = ns[index];
+ Trace("sygus-process-arg-deps")
+ << " Analyze argument information for application #" << index << ": "
+ << n << std::endl;
+
+ // in the following, we say an argument a "carries" a term t if
+ // the function to synthesize would use argument a to construct
+ // the term t in its definition.
+
+ // map that assumes all arguments carry their respective term
+ std::unordered_map<unsigned, Node> n_arg_map;
+ // terms to the argument that is carrying it.
+ // the arguments in the range of this map must be marked as relevant.
+ std::unordered_map<Node, unsigned, NodeHashFunction> term_to_arg_carry;
+ // map of terms to (unprocessed) arguments where it occurs
+ std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>
+ term_to_args;
+
+ // initialize
+ for (unsigned a = 0; a < n.getNumChildren(); a++)
+ {
+ n_arg_map[a] = n[a];
+ }
+
+ for (unsigned a = 0; a < n.getNumChildren(); a++)
+ {
+ bool processed = false;
+ if (d_arg_props[a].d_relevant)
+ {
+ // we can assume all relevant arguments carry their terms
+ processed = true;
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << a
+ << " (already relevant)." << std::endl;
+ if (term_to_arg_carry.find(n[a]) == term_to_arg_carry.end())
+ {
+ Trace("sygus-process-arg-deps") << " carry " << n[a]
+ << " by argument #" << a << std::endl;
+ term_to_arg_carry[n[a]] = a;
+ }
+ }
+ else
+ {
+ // first, check if single occurrence variable
+ // check if an irrelevant variable
+ if (n[a].isVar() && synth_fv.find(n[a]) != synth_fv.end())
+ {
+ Assert(single_occ_variables.find(n[a]) != single_occ_variables.end());
+ // may be able to make this more precise?
+ // check if a single-occurrence variable
+ if (single_occ_variables[n[a]])
+ {
+ // if we do not already have a template definition, or the
+ // template is a single occurrence variable
+ if (d_arg_props[a].d_template.isNull()
+ || d_arg_props[a].d_var_single_occ)
+ {
+ processed = true;
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << a;
+ Trace("sygus-process-arg-deps")
+ << " (single occurrence variable ";
+ Trace("sygus-process-arg-deps") << n[a] << ")." << std::endl;
+ d_arg_props[a].d_var_single_occ = true;
+ d_arg_props[a].d_template = n[a];
+ }
+ }
+ }
+ if (!processed && !d_arg_props[a].d_template.isNull()
+ && !d_arg_props[a].d_var_single_occ)
+ {
+ // argument already has a definition, see if it is maintained
+ if (checkMatch(d_arg_props[a].d_template, n[a], n_arg_map))
+ {
+ processed = true;
+ Trace("sygus-process-arg-deps") << " ...processed arg #" << a;
+ Trace("sygus-process-arg-deps") << " (consistent definition "
+ << n[a];
+ Trace("sygus-process-arg-deps")
+ << " with " << d_arg_props[a].d_template << ")." << std::endl;
+ }
+ }
+ }
+ if (!processed)
+ {
+ // otherwise, add it to the list of arguments for this term
+ term_to_args[n[a]].push_back(a);
+ }
+ }
+
+ Trace("sygus-process-arg-deps") << " Look at argument terms..."
+ << std::endl;
+
+ // list of all arguments
+ std::vector<Node> arg_list;
+ // now look at the terms for unprocessed arguments
+ for (std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
+ iterator it = term_to_args.begin();
+ it != term_to_args.end();
+ ++it)
+ {
+ Node nn = it->first;
+ arg_list.push_back(nn);
+ if (Trace.isOn("sygus-process-arg-deps"))
+ {
+ Trace("sygus-process-arg-deps") << " argument " << nn;
+ Trace("sygus-process-arg-deps") << " (" << it->second.size()
+ << " positions)";
+ // check the status of this term
+ if (nn.isVar() && synth_fv.find(nn) != synth_fv.end())
+ {
+ // is it relevant?
+ if (rlv_vars.find(nn) != rlv_vars.end())
+ {
+ Trace("sygus-process-arg-deps") << " is a relevant variable."
+ << std::endl;
+ }
+ else
+ {
+ Trace("sygus-process-arg-deps") << " is an irrelevant variable."
+ << std::endl;
+ }
+ }
+ else
+ {
+ // this can be more precise
+ Trace("sygus-process-arg-deps") << " is a relevant term."
+ << std::endl;
+ }
+ }
+ }
+
+ unsigned arg_list_counter = 0;
+ // sort arg_list by term size?
+
+ while (arg_list_counter < arg_list.size())
+ {
+ Node infer_def_t;
+ do
+ {
+ infer_def_t = Node::null();
+ // see if we can infer a definition
+ for (std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
+ iterator it = term_to_args.begin();
+ it != term_to_args.end();
+ ++it)
+ {
+ Node def = inferDefinition(it->first, term_to_arg_carry, free_vars);
+ if (!def.isNull())
+ {
+ Trace("sygus-process-arg-deps") << " *** Inferred definition "
+ << def << " for " << it->first
+ << std::endl;
+ // assign to each argument
+ assignRelevantDef(def, it->second);
+ // term_to_arg_carry[it->first] = rid;
+ infer_def_t = it->first;
+ break;
+ }
+ }
+ if (!infer_def_t.isNull())
+ {
+ term_to_args.erase(infer_def_t);
+ }
+ } while (!infer_def_t.isNull());
+
+ // decide to make an argument relevant
+ bool success = false;
+ while (arg_list_counter < arg_list.size() && !success)
+ {
+ Node curr = arg_list[arg_list_counter];
+ std::unordered_map<Node, std::vector<unsigned>, NodeHashFunction>::
+ iterator it = term_to_args.find(curr);
+ if (it != term_to_args.end())
+ {
+ Trace("sygus-process-arg-deps") << " *** Decide relevant " << curr
+ << std::endl;
+ // assign relevant to each
+ Node null_def;
+ unsigned rid = assignRelevantDef(null_def, it->second);
+ term_to_arg_carry[curr] = rid;
+ Trace("sygus-process-arg-deps")
+ << " carry " << curr << " by argument #" << rid << std::endl;
+ term_to_args.erase(curr);
+ success = true;
+ }
+ arg_list_counter++;
+ }
+ }
+ }
+}
+
+bool CegConjectureProcessFun::isArgRelevant(unsigned i)
+{
+ return d_arg_props[i].d_relevant;
+}
+
+void CegConjectureProcessFun::getIrrelevantArgs(
+ std::unordered_set<unsigned>& args)
+{
+ for (unsigned i = 0; i < d_arg_vars.size(); i++)
+ {
+ if (!d_arg_props[i].d_relevant)
+ {
+ args.insert(i);
+ }
+ }
+}
+
+CegConjectureProcess::CegConjectureProcess(QuantifiersEngine* qe) {}
+CegConjectureProcess::~CegConjectureProcess() {}
+Node CegConjectureProcess::preSimplify(Node q)
+{
+ Trace("sygus-process") << "Pre-simplify conjecture : " << q << std::endl;
+ return q;
+}
+
+Node CegConjectureProcess::postSimplify(Node q)
+{
+ Trace("sygus-process") << "Post-simplify conjecture : " << q << std::endl;
+ Assert(q.getKind() == FORALL);
+
+ // initialize the information about each function to synthesize
+ for (unsigned i = 0; i < q[0].getNumChildren(); i++)
+ {
+ Node f = q[0][i];
+ if (f.getType().isFunction())
+ {
+ d_sf_info[f].init(f);
+ }
+ }
+
+ // get the base on the conjecture
+ Node base = q[1];
+ std::unordered_set<Node, NodeHashFunction> synth_fv;
+ if (base.getKind() == NOT && base[0].getKind() == FORALL)
+ {
+ for (unsigned j = 0; j < base[0][0].getNumChildren(); j++)
+ {
+ synth_fv.insert(base[0][0][j]);
+ }
+ base = base[0][1];
+ }
+ std::vector<Node> conjuncts;
+ getComponentVector(AND, base, conjuncts);
+
+ // process the conjunctions
+ for (std::map<Node, CegConjectureProcessFun>::iterator it = d_sf_info.begin();
+ it != d_sf_info.end();
+ ++it)
+ {
+ Node f = it->first;
+ for (unsigned i = 0; i < conjuncts.size(); i++)
+ {
+ processConjunct(conjuncts[i], f, synth_fv);
+ }
+ }
+
+ return q;
+}
+
+void CegConjectureProcess::initialize(Node n, std::vector<Node>& candidates)
+{
+ if (Trace.isOn("sygus-process"))
+ {
+ Trace("sygus-process") << "Process conjecture : " << n
+ << " with candidates: " << std::endl;
+ for (unsigned i = 0; i < candidates.size(); i++)
+ {
+ Trace("sygus-process") << " " << candidates[i] << std::endl;
+ }
+ }
+}
+
+bool CegConjectureProcess::isArgRelevant(Node f, unsigned i)
+{
+ std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
+ if (its != d_sf_info.end())
+ {
+ return its->second.isArgRelevant(i);
+ }
+ Assert(false);
+ return true;
+}
+
+bool CegConjectureProcess::getIrrelevantArgs(Node f,
+ std::unordered_set<unsigned>& args)
+{
+ std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
+ if (its != d_sf_info.end())
+ {
+ its->second.getIrrelevantArgs(args);
+ return true;
+ }
+ return false;
+}
+
+void CegConjectureProcess::processConjunct(
+ Node n, Node f, std::unordered_set<Node, NodeHashFunction>& synth_fv)
+{
+ Trace("sygus-process-arg-deps") << "Process conjunct: " << std::endl;
+ Trace("sygus-process-arg-deps") << " " << n << " for synth fun " << f
+ << "..." << std::endl;
+
+ // first, flatten the conjunct
+ // make a copy of free variables since we may add new ones
+ std::unordered_set<Node, NodeHashFunction> synth_fv_n = synth_fv;
+ std::unordered_map<Node, Node, NodeHashFunction> defs;
+ Node nf = flatten(n, f, synth_fv_n, defs);
+
+ Trace("sygus-process-arg-deps") << "Flattened to: " << std::endl;
+ Trace("sygus-process-arg-deps") << " " << nf << std::endl;
+
+ // get free variables in nf
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>
+ free_vars;
+ getFreeVariables(nf, synth_fv_n, free_vars);
+ // get free variables in each application
+ std::vector<Node> ns;
+ std::vector<Node> ks;
+ for (std::unordered_map<Node, Node, NodeHashFunction>::iterator it =
+ defs.begin();
+ it != defs.end();
+ ++it)
+ {
+ getFreeVariables(it->second, synth_fv_n, free_vars);
+ ns.push_back(it->second);
+ ks.push_back(it->first);
+ }
+
+ // process the applications of synthesis functions
+ if (!ns.empty())
+ {
+ std::map<Node, CegConjectureProcessFun>::iterator its = d_sf_info.find(f);
+ if (its != d_sf_info.end())
+ {
+ its->second.processTerms(ns, ks, nf, synth_fv_n, free_vars);
+ }
+ }
+}
+
+Node CegConjectureProcess::CegConjectureProcess::flatten(
+ Node n,
+ Node f,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node, Node, NodeHashFunction>& defs)
+{
+ std::unordered_map<Node, Node, NodeHashFunction> visited;
+ std::unordered_map<Node, Node, NodeHashFunction>::iterator it;
+ std::stack<Node> visit;
+ Node cur;
+ visit.push(n);
+ do
+ {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+
+ if (it == visited.end())
+ {
+ visited[cur] = Node::null();
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ visit.push(cur[i]);
+ }
+ }
+ else if (it->second.isNull())
+ {
+ Node ret = cur;
+ bool childChanged = false;
+ std::vector<Node> children;
+ if (cur.getMetaKind() == kind::metakind::PARAMETERIZED)
+ {
+ children.push_back(cur.getOperator());
+ }
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ it = visited.find(cur[i]);
+ Assert(it != visited.end());
+ Assert(!it->second.isNull());
+ childChanged = childChanged || cur[i] != it->second;
+ children.push_back(it->second);
+ }
+ if (childChanged)
+ {
+ ret = NodeManager::currentNM()->mkNode(cur.getKind(), children);
+ }
+ // is it the function to synthesize?
+ if (cur.getKind() == APPLY_UF && cur.getOperator() == f)
+ {
+ // if so, flatten
+ Node k = NodeManager::currentNM()->mkBoundVar("vf", cur.getType());
+ defs[k] = ret;
+ ret = k;
+ synth_fv.insert(k);
+ }
+ // post-rewrite
+ visited[cur] = ret;
+ }
+ } while (!visit.empty());
+ Assert(visited.find(n) != visited.end());
+ Assert(!visited.find(n)->second.isNull());
+ return visited[n];
+}
+
+void CegConjectureProcess::getFreeVariables(
+ Node n,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars)
+{
+ // first must compute free variables in each subterm of n,
+ // as well as contains_synth_fun
+ std::unordered_map<Node, bool, NodeHashFunction> visited;
+ std::unordered_map<Node, bool, NodeHashFunction>::iterator it;
+ std::stack<Node> visit;
+ Node cur;
+ visit.push(n);
+ do
+ {
+ cur = visit.top();
+ visit.pop();
+ it = visited.find(cur);
+
+ if (it == visited.end())
+ {
+ visited[cur] = false;
+ visit.push(cur);
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ visit.push(cur[i]);
+ }
+ }
+ else if (!it->second)
+ {
+ free_vars[cur].clear();
+ if (synth_fv.find(cur) != synth_fv.end())
+ {
+ // it is a free variable
+ free_vars[cur].insert(cur);
+ }
+ else
+ {
+ // otherwise, carry the free variables from the children
+ for (unsigned i = 0; i < cur.getNumChildren(); i++)
+ {
+ free_vars[cur].insert(free_vars[cur[i]].begin(),
+ free_vars[cur[i]].end());
+ }
+ }
+ visited[cur] = true;
+ }
+ } while (!visit.empty());
+}
+
+Node CegConjectureProcess::getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth)
+{
+ return Node::null();
+}
+
+void CegConjectureProcess::debugPrint(const char* c) {}
+void CegConjectureProcess::getComponentVector(Kind k,
+ Node n,
+ std::vector<Node>& args)
+{
+ if (n.getKind() == k)
+ {
+ for (unsigned i = 0; i < n.getNumChildren(); i++)
+ {
+ args.push_back(n[i]);
+ }
+ }
+ else
+ {
+ args.push_back(n);
+ }
+}
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
diff --git a/src/theory/quantifiers/sygus/sygus_process_conj.h b/src/theory/quantifiers/sygus/sygus_process_conj.h
new file mode 100644
index 000000000..0b9a25532
--- /dev/null
+++ b/src/theory/quantifiers/sygus/sygus_process_conj.h
@@ -0,0 +1,365 @@
+/********************* */
+/*! \file sygus_process_conj.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Techniqures for static preprocessing and analysis of
+ ** sygus conjectures.
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__SYGUS_PROCESS_CONJ_H
+#define __CVC4__THEORY__QUANTIFIERS__SYGUS_PROCESS_CONJ_H
+
+#include <map>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+#include "expr/node.h"
+#include "expr/type_node.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+/** This file contains techniques that compute
+ * argument relevancy for synthesis functions
+ *
+ * Let F be a synthesis conjecture of the form:
+ * exists f. forall X. P( f, X )
+ *
+ * The classes below compute whether certain arguments of
+ * the function-to-synthesize f are irrelevant.
+ * Assume that f is a binary function, where possible solutions
+ * to the above conjecture are of the form:
+ * f -> (lambda (xy) t[x,y])
+ * We say e.g. that the 2nd argument of f is irrelevant if we
+ * can determine:
+ * F has a solution
+ * if and only if
+ * F has a solution of the form f -> (lambda (xy) t[x] )
+ * We conclude that arguments are irrelevant using the following
+ * techniques.
+ *
+ *
+ * (1) Argument invariance:
+ *
+ * Let s[z] be a term whose free variables are contained in { z }.
+ * If all occurrences of f-applications in F are of the form:
+ * f(t, s[t])
+ * then:
+ * f = (lambda (xy) r[x,y])
+ * is a solution to F only if:
+ * f = (lambda (xy) r[x,s[x]])
+ * is as well.
+ * Hence the second argument of f is not relevant.
+ *
+ *
+ * (2) Variable irrelevance:
+ *
+ * If F is equivalent to:
+ * exists f. forall w z u1...un. C1 ^...^Cm,
+ * where for i=1...m, Ci is of the form:
+ * ( w1 = f(tm1[z], u1) ^
+ * ... ^
+ * wn = f(tmn[z], un) ) => Pm(w1...wn, z)
+ * then the second argument of f is irrelevant.
+ * We call u1...un single occurrence variables
+ * (in Ci).
+ *
+ *
+ * TODO (#1210) others, generalize (2)?
+ *
+ */
+
+/** This structure stores information regarding
+ * an argument of a function to synthesize.
+ *
+ * It is used to store whether the argument
+ * position in the function to synthesize is
+ * relevant.
+ */
+class CegConjectureProcessArg
+{
+ public:
+ CegConjectureProcessArg() : d_var_single_occ(false), d_relevant(false) {}
+ /** template definition
+ * This is the term s[z] described
+ * under "Argument Invariance" above.
+ */
+ Node d_template;
+ /** single occurrence
+ * Whether we are trying to show this argument
+ * is irrelevant by "Variable irrelevance"
+ * described above.
+ */
+ bool d_var_single_occ;
+ /** whether this argument is relevant
+ * An argument is marked as relevant if:
+ * (A) it is explicitly marked as relevant
+ * due to a function application containing
+ * a relevant term at this argument position, or
+ * (B) if it is given conflicting template definitions.
+ */
+ bool d_relevant;
+};
+
+/** This structure stores information regarding conjecture-specific
+* analysis of a single function to synthesize within
+* a conjecture to synthesize.
+*
+* It maintains information about each of the function to
+* synthesize's arguments.
+*/
+struct CegConjectureProcessFun
+{
+ public:
+ CegConjectureProcessFun() {}
+ ~CegConjectureProcessFun() {}
+ /** initialize this class for function f */
+ void init(Node f);
+ /** process terms
+ *
+ * This is called once per conjunction in
+ * the synthesis conjecture.
+ *
+ * ns are the f-applications to process,
+ * ks are the variables we introduced to flatten them,
+ * nf is the flattened form of our conjecture to process,
+ * free_vars maps all subterms of n and nf to the set
+ * of variables (in set synth_fv) they contain.
+ *
+ * This updates information regarding which arguments
+ * of the function-to-synthesize are relevant.
+ */
+ void processTerms(
+ std::vector<Node>& ns,
+ std::vector<Node>& ks,
+ Node nf,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars);
+ /** is the i^th argument of the function-to-synthesize of this class relevant?
+ */
+ bool isArgRelevant(unsigned i);
+ /** get irrelevant arguments for the function-to-synthesize of this class */
+ void getIrrelevantArgs(std::unordered_set<unsigned>& args);
+
+ private:
+ /** the synth fun associated with this */
+ Node d_synth_fun;
+ /** properties of each argument */
+ std::vector<CegConjectureProcessArg> d_arg_props;
+ /** variables for each argument type of f
+ *
+ * These are used to express templates for argument
+ * invariance, in the data member
+ * CegConjectureProcessArg::d_template.
+ */
+ std::vector<Node> d_arg_vars;
+ /** map from d_arg_vars to the argument #
+ * they represent.
+ */
+ std::unordered_map<Node, unsigned, NodeHashFunction> d_arg_var_num;
+ /** check match
+ * This function returns true iff we can infer:
+ * cn * { x -> n_arg_map[d_arg_var_num[x]] | x in d_arg_vars } = n
+ * In other words, cn and n are equivalent
+ * via the substitution mapping argument variables to terms
+ * specified by n_arg_map. The rewriter is used for inferring
+ * this equivalence.
+ *
+ * For example, if n_arg_map contains { 1 -> t, 2 -> s }, then
+ * checkMatch( x1+x2, t+s, n_arg_map ) returns true,
+ * checkMatch( x1+1, t+1, n_arg_map ) returns true,
+ * checkMatch( 0, 0, n_arg_map ) returns true,
+ * checkMatch( x1+1, s, n_arg_map ) returns false.
+ */
+ bool checkMatch(Node cn,
+ Node n,
+ std::unordered_map<unsigned, Node>& n_arg_map);
+ /** infer definition
+ *
+ * In the following, we say a term is a "template
+ * definition" if its free variables are a subset of d_arg_vars.
+ *
+ * If this function returns a non-null node ret, then
+ * checkMatch( ret, n, term_to_arg_carry^-1 ) returns true.
+ * and ret is a template definition.
+ *
+ * The free variables for all subterms of n are stored in
+ * free_vars. The map term_to_arg_carry is injective.
+ *
+ * For example, if term_to_arg_carry contains { t -> 1, s -> 2 } and
+ * free_vars is { t -> {y}, r -> {y}, s -> {}, q -> {}, ... -> {} }, then
+ * inferDefinition( 0, term_to_arg_carry, free_vars )
+ * returns 0
+ * inferDefinition( t, term_to_arg_carry, free_vars )
+ * returns x1
+ * inferDefinition( t+s+q, term_to_arg_carry, free_vars )
+ * returns x1+x2+q
+ * inferDefinition( t+r, term_to_arg_carry, free_vars )
+ * returns null
+ *
+ * Notice that multiple definitions are possible, e.g. above:
+ * inferDefinition( s, term_to_arg_carry, free_vars )
+ * may return either s or x2
+ * TODO (#1210) : try multiple definitions?
+ */
+ Node inferDefinition(
+ Node n,
+ std::unordered_map<Node, unsigned, NodeHashFunction>& term_to_arg_carry,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars);
+ /** Assign relevant definition
+ *
+ * If def is non-null,
+ * this function assigns def as a template definition
+ * for the argument positions in args.
+ * This is called when there exists a term of the form
+ * f( t1....tn )
+ * in the synthesis conjecture that we are processing,
+ * where t_i = def * sigma for all i \in args,
+ * for some substitution sigma, where def is a template
+ * definition.
+ *
+ * If def is null, then there exists a term of the form
+ * f( t1....tn )
+ * where t_i = s for for all i \in args, and s is not
+ * a template definition. In this case, at least one
+ * argument in args must be marked as a relevant
+ * argument position.
+ *
+ * Returns a value rid such that:
+ * (1) rid occurs in args,
+ * (2) if def is null, then argument rid was marked
+ * relevant by this call.
+ */
+ unsigned assignRelevantDef(Node def, std::vector<unsigned>& args);
+ /** returns true if n is in d_arg_vars, updates arg_index
+ * to its position in d_arg_vars.
+ */
+ bool isArgVar(Node n, unsigned& arg_index);
+};
+
+/** Ceg Conjecture Process
+*
+* This class implements static techniques for preprocessing and analysis of
+* sygus conjectures.
+*
+* It is used as a back-end to CegConjecture, which calls it using the following
+* interface:
+* (1) When a sygus conjecture is asserted, we call
+* CegConjectureProcess::simplify( q ),
+* where q is the sygus conjecture in original form.
+*
+* (2) After a sygus conjecture is simplified and converted to deep
+* embedding form, we call CegConjectureProcess::initialize( n, candidates ).
+*
+* (3) During enumerative SyGuS search, calls may be made by
+* the extension of the quantifier-free datatypes decision procedure for
+* sygus to CegConjectureProcess::getSymmetryBreakingPredicate(...), which are
+* used for pruning search space based on conjecture-specific analysis.
+*/
+class CegConjectureProcess
+{
+ public:
+ CegConjectureProcess(QuantifiersEngine* qe);
+ ~CegConjectureProcess();
+ /** simplify the synthesis conjecture q
+ * Returns a formula that is equivalent to q.
+ * This simplification pass is called before all others
+ * in CegConjecture::assign.
+ *
+ * This function is intended for simplifications that
+ * impact whether or not the synthesis conjecture is
+ * single-invocation.
+ */
+ Node preSimplify(Node q);
+ /** simplify the synthesis conjecture q
+ * Returns a formula that is equivalent to q.
+ * This simplification pass is called after all others
+ * in CegConjecture::assign.
+ */
+ Node postSimplify(Node q);
+ /** initialize
+ *
+ * n is the "base instantiation" of the deep-embedding version of
+ * the synthesis conjecture under "candidates".
+ * (see CegConjecture::d_base_inst)
+ */
+ void initialize(Node n, std::vector<Node>& candidates);
+ /** is the i^th argument of the function-to-synthesize f relevant? */
+ bool isArgRelevant(Node f, unsigned i);
+ /** get irrelevant arguments for function-to-synthesize f
+ * returns true if f is a function-to-synthesize.
+ */
+ bool getIrrelevantArgs(Node f, std::unordered_set<unsigned>& args);
+ /** get symmetry breaking predicate
+ *
+ * Returns a formula that restricts the enumerative search space (for a given
+ * depth) for a term x of sygus type tn whose top symbol is the tindex^{th}
+ * constructor, where x is a subterm of enumerator e.
+ */
+ Node getSymmetryBreakingPredicate(
+ Node x, Node e, TypeNode tn, unsigned tindex, unsigned depth);
+ /** print out debug information about this conjecture */
+ void debugPrint(const char* c);
+ private:
+ /** process conjunct
+ *
+ * This sets up initial information about functions to synthesize
+ * where n is a conjunct of the synthesis conjecture, and synth_fv
+ * is the set of (inner) universal variables in the synthesis
+ * conjecture.
+ */
+ void processConjunct(Node n,
+ Node f,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv);
+ /** flatten
+ *
+ * Flattens all applications of f in term n.
+ * This may add new variables to synth_fv, which
+ * are introduced at all positions of functions
+ * to synthesize in a bottom-up fashion. For each
+ * variable k introduced for a function application
+ * f(t), we add ( k -> f(t) ) to defs and ( f -> k )
+ * to fun_to_defs.
+ */
+ Node flatten(Node n,
+ Node f,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node, Node, NodeHashFunction>& defs);
+ /** get free variables
+ * Constructs a map of all free variables that occur in n
+ * from synth_fv and stores them in the map free_vars.
+ */
+ void getFreeVariables(
+ Node n,
+ std::unordered_set<Node, NodeHashFunction>& synth_fv,
+ std::unordered_map<Node,
+ std::unordered_set<Node, NodeHashFunction>,
+ NodeHashFunction>& free_vars);
+ /** for each synth-fun, information that is specific to this conjecture */
+ std::map<Node, CegConjectureProcessFun> d_sf_info;
+
+ /** get component vector */
+ void getComponentVector(Kind k, Node n, std::vector<Node>& args);
+};
+
+} /* namespace CVC4::theory::quantifiers */
+} /* namespace CVC4::theory */
+} /* namespace CVC4 */
+
+#endif
diff --git a/src/theory/quantifiers/sygus/term_database_sygus.cpp b/src/theory/quantifiers/sygus/term_database_sygus.cpp
new file mode 100644
index 000000000..b12a23c83
--- /dev/null
+++ b/src/theory/quantifiers/sygus/term_database_sygus.cpp
@@ -0,0 +1,1487 @@
+/********************* */
+/*! \file term_database_sygus.cpp
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief Implementation of term database sygus class
+ **/
+
+#include "theory/quantifiers/sygus/term_database_sygus.h"
+
+#include "options/quantifiers_options.h"
+#include "theory/arith/arith_msum.h"
+#include "theory/quantifiers/quantifiers_attributes.h"
+#include "theory/quantifiers/term_database.h"
+#include "theory/quantifiers/term_util.h"
+#include "theory/quantifiers_engine.h"
+
+using namespace std;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory::inst;
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+TermDbSygus::TermDbSygus(context::Context* c, QuantifiersEngine* qe)
+ : d_quantEngine(qe),
+ d_syexp(new SygusExplain(this)),
+ d_ext_rw(new ExtendedRewriter(true))
+{
+ d_true = NodeManager::currentNM()->mkConst( true );
+ d_false = NodeManager::currentNM()->mkConst( false );
+}
+
+bool TermDbSygus::reset( Theory::Effort e ) {
+ return true;
+}
+
+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();
+ 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;
+ }
+ Assert( !vtn.isNull() );
+ 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[sindex][tn].push_back( v );
+ }
+ return d_fv[sindex][tn][i];
+}
+
+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 getFreeVar( tn, 0, useSygusType );
+ }else{
+ int index = it->second;
+ var_count[tn]++;
+ 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];
+}
+
+Node TermDbSygus::mkGeneric(const Datatype& dt,
+ unsigned c,
+ std::map<TypeNode, int>& var_count,
+ std::map<int, Node>& pre)
+{
+ Assert(c < dt.getNumConstructors());
+ Assert( dt.isSygus() );
+ Assert( !dt[c].getSygusOp().isNull() );
+ std::vector< Node > children;
+ Node op = Node::fromExpr( dt[c].getSygusOp() );
+ if( op.getKind()!=BUILTIN ){
+ children.push_back( op );
+ }
+ Trace("sygus-db-debug") << "mkGeneric " << dt.getName() << " " << op << " " << op.getKind() << "..." << std::endl;
+ for (unsigned i = 0, nargs = dt[c].getNumArgs(); i < nargs; i++)
+ {
+ TypeNode tna = getArgType( dt[c], i );
+ Node a;
+ std::map< int, Node >::iterator it = pre.find( i );
+ if( it!=pre.end() ){
+ a = it->second;
+ }else{
+ a = getFreeVarInc( tna, var_count, true );
+ }
+ Trace("sygus-db-debug")
+ << " child " << i << " : " << a << " : " << a.getType() << std::endl;
+ Assert( !a.isNull() );
+ children.push_back( a );
+ }
+ Node ret;
+ if( op.getKind()==BUILTIN ){
+ Trace("sygus-db-debug") << "Make builtin node..." << std::endl;
+ ret = NodeManager::currentNM()->mkNode( op, children );
+ }else{
+ Kind ok = getOperatorKind( op );
+ Trace("sygus-db-debug") << "Operator kind is " << ok << std::endl;
+ if( children.size()==1 && ok==kind::UNDEFINED_KIND ){
+ ret = children[0];
+ }else{
+ ret = NodeManager::currentNM()->mkNode( ok, children );
+ }
+ }
+ Trace("sygus-db-debug") << "...returning " << ret << std::endl;
+ return ret;
+}
+
+Node TermDbSygus::mkGeneric(const Datatype& dt, int c, std::map<int, Node>& pre)
+{
+ std::map<TypeNode, int> var_count;
+ return mkGeneric(dt, c, var_count, pre);
+}
+
+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;
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ 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, size = n.getNumChildren(); j < size; j++)
+ {
+ pre[j] = sygusToBuiltin( n[j], getArgType( dt[i], j ) );
+ }
+ Node ret = mkGeneric(dt, i, var_count, pre);
+ Trace("sygus-db-debug") << "SygusToBuiltin : Generic is " << ret << std::endl;
+ d_sygus_to_builtin[tn][n] = ret;
+ return ret;
+ }
+ if (n.hasAttribute(SygusPrintProxyAttribute()))
+ {
+ // this variable was associated by an attribute to a builtin node
+ return n.getAttribute(SygusPrintProxyAttribute());
+ }
+ Assert(isFreeVar(n));
+ // map to builtin variable type
+ int fv_num = getVarNum(n);
+ Assert(!dt.getSygusType().isNull());
+ TypeNode vtn = TypeNode::fromType(dt.getSygusType());
+ Node ret = getFreeVar(vtn, fv_num);
+ 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() );
+}
+
+unsigned TermDbSygus::getSygusTermSize( Node n ){
+ if( n.getNumChildren()==0 ){
+ return 0;
+ }else{
+ Assert(n.getKind() == APPLY_CONSTRUCTOR);
+ unsigned sum = 0;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ sum += getSygusTermSize( n[i] );
+ }
+ const Datatype& dt = Datatype::datatypeOf(n.getOperator().toExpr());
+ int cindex = Datatype::indexOf(n.getOperator().toExpr());
+ Assert(cindex >= 0 && cindex < (int)dt.getNumConstructors());
+ unsigned weight = dt[cindex].getWeight();
+ return weight + sum;
+ }
+}
+
+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::TermUtil::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 (d_quantEngine->getTermUtil()->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 = d_quantEngine->getTermUtil()->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 (d_quantEngine->getTermUtil()->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 = d_quantEngine->getTermUtil()->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 = d_quantEngine->getTermUtil()->getTypeMaxValue(c.getType());
+ Node zero_c = d_quantEngine->getTermUtil()->getTypeValue(c.getType(), 0);
+ Node one_c = d_quantEngine->getTermUtil()->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 = d_quantEngine->getTermUtil()->getTypeValue(
+ sygusToBuiltinType(tnc), 0);
+ solve_ret = getConstConsNum( tn, builtin );
+ if( solve_ret!=-1 ){
+ // t - s -----> ( 0 - s ) + t
+ rt.d_req_kind = nk == 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;
+}
+
+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;
+ 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
+ }
+ //iterate over constructors
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ Expr sop = dt[i].getSygusOp();
+ Assert( !sop.isNull() );
+ Node n = Node::fromExpr( sop );
+ Trace("sygus-db") << " Operator #" << i << " : " << sop;
+ if( sop.getKind() == kind::BUILTIN ){
+ Kind sk = NodeManager::operatorToKind( n );
+ Trace("sygus-db") << ", kind = " << sk;
+ d_kinds[tn][sk] = i;
+ d_arg_kind[tn][i] = sk;
+ }else if( sop.isConst() ){
+ Trace("sygus-db") << ", constant";
+ d_consts[tn][n] = i;
+ d_arg_const[tn][i] = n;
+ }
+ d_ops[tn][n] = i;
+ d_arg_ops[tn][i] = n;
+ Trace("sygus-db") << std::endl;
+ }
+ //register connected types
+ for( unsigned i=0; i<dt.getNumConstructors(); i++ ){
+ for( unsigned j=0; j<dt[i].getNumArgs(); j++ ){
+ registerSygusType( getArgType( dt[i], j ) );
+ }
+ }
+ }
+ }
+ }
+}
+
+void TermDbSygus::registerEnumerator(Node e,
+ Node f,
+ CegConjecture* conj,
+ bool mkActiveGuard)
+{
+ Assert(d_enum_to_conjecture.find(e) == d_enum_to_conjecture.end());
+ Trace("sygus-db") << "Register measured term : " << e << std::endl;
+ d_enum_to_conjecture[e] = conj;
+ d_enum_to_synth_fun[e] = f;
+ 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_enum_to_active_guard[e] = eg;
+ }
+}
+
+bool TermDbSygus::isEnumerator(Node e) const
+{
+ return d_enum_to_conjecture.find(e) != d_enum_to_conjecture.end();
+}
+
+CegConjecture* TermDbSygus::getConjectureForEnumerator(Node e)
+{
+ std::map<Node, CegConjecture*>::iterator itm = d_enum_to_conjecture.find(e);
+ if (itm != d_enum_to_conjecture.end()) {
+ return itm->second;
+ }else{
+ return NULL;
+ }
+}
+
+Node TermDbSygus::getSynthFunForEnumerator(Node e)
+{
+ std::map<Node, Node>::iterator itsf = d_enum_to_synth_fun.find(e);
+ if (itsf != d_enum_to_synth_fun.end())
+ {
+ return itsf->second;
+ }
+ else
+ {
+ return Node::null();
+ }
+}
+
+Node TermDbSygus::getActiveGuardForEnumerator(Node e)
+{
+ std::map<Node, Node>::iterator itag = d_enum_to_active_guard.find(e);
+ if (itag != d_enum_to_active_guard.end()) {
+ return itag->second;
+ }else{
+ return Node::null();
+ }
+}
+
+void TermDbSygus::getEnumerators(std::vector<Node>& mts)
+{
+ for (std::map<Node, CegConjecture*>::iterator itm =
+ d_enum_to_conjecture.begin();
+ itm != d_enum_to_conjecture.end(); ++itm) {
+ mts.push_back( itm->first );
+ }
+}
+
+bool TermDbSygus::isRegistered( TypeNode tn ) {
+ return d_register.find( tn )!=d_register.end();
+}
+
+TypeNode TermDbSygus::sygusToBuiltinType( TypeNode tn ) {
+ Assert( isRegistered( tn ) );
+ return d_register[tn];
+}
+
+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 (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 ) );
+ 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;
+ }
+}
+
+unsigned TermDbSygus::getSelectorWeight(TypeNode tn, Node sel)
+{
+ std::map<TypeNode, std::map<Node, unsigned> >::iterator itsw =
+ d_sel_weight.find(tn);
+ if (itsw == d_sel_weight.end())
+ {
+ d_sel_weight[tn].clear();
+ itsw = d_sel_weight.find(tn);
+ Type t = tn.toType();
+ const Datatype& dt = static_cast<DatatypeType>(t).getDatatype();
+ Trace("sygus-db") << "Compute selector weights for " << dt.getName()
+ << std::endl;
+ for (unsigned i = 0, size = dt.getNumConstructors(); i < size; i++)
+ {
+ unsigned cw = dt[i].getWeight();
+ for (unsigned j = 0, size2 = dt[i].getNumArgs(); j < size2; j++)
+ {
+ Node csel = Node::fromExpr(dt[i].getSelectorInternal(t, j));
+ std::map<Node, unsigned>::iterator its = itsw->second.find(csel);
+ if (its == itsw->second.end() || cw < its->second)
+ {
+ d_sel_weight[tn][csel] = cw;
+ Trace("sygus-db") << " w(" << csel << ") <= " << cw << std::endl;
+ }
+ }
+ }
+ }
+ Assert(itsw->second.find(sel) != itsw->second.end());
+ return itsw->second[sel];
+}
+
+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() ){
+ std::map< Kind, int >::iterator it = itt->second.find( k );
+ if( it!=itt->second.end() ){
+ return it->second;
+ }
+ }
+ return -1;
+}
+
+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() ){
+ std::map< Node, int >::iterator it = itt->second.find( n );
+ if( it!=itt->second.end() ){
+ return it->second;
+ }
+ }
+ return -1;
+}
+
+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;
+ }else{
+ return -1;
+ }
+}
+
+bool TermDbSygus::hasKind( TypeNode tn, Kind k ) {
+ return getKindConsNum( tn, k )!=-1;
+}
+bool TermDbSygus::hasConst( TypeNode tn, Node n ) {
+ return getConstConsNum( tn, n )!=-1;
+}
+bool TermDbSygus::hasOp( TypeNode tn, Node n ) {
+ return getOpConsNum( tn, n )!=-1;
+}
+
+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() ){
+ std::map< int, Node >::iterator itn = itt->second.find( i );
+ if( itn!=itt->second.end() ){
+ return itn->second;
+ }
+ }
+ return Node::null();
+}
+
+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() ){
+ std::map< int, Node >::iterator itn = itt->second.find( i );
+ if( itn!=itt->second.end() ){
+ return itn->second;
+ }
+ }
+ return Node::null();
+}
+
+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() ){
+ std::map< int, Kind >::iterator itk = itt->second.find( i );
+ if( itk!=itt->second.end() ){
+ return itk->second;
+ }
+ }
+ return UNDEFINED_KIND;
+}
+
+bool TermDbSygus::isKindArg( TypeNode tn, int i ) {
+ return getConsNumKind( tn, i )!=UNDEFINED_KIND;
+}
+
+bool TermDbSygus::isConstArg( 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() ){
+ return itt->second.find( i )!=itt->second.end();
+ }else{
+ return false;
+ }
+}
+
+TypeNode TermDbSygus::getArgType(const DatatypeConstructor& c, unsigned i)
+{
+ Assert(i < c.getNumArgs());
+ 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() ) ){
+ bool changed = false;
+ std::vector< Node > mon[2];
+ for( unsigned r=0; r<2; r++ ){
+ unsigned ro = r==0 ? 1 : 0;
+ Node c;
+ Node nc;
+ if( n[r].getKind()==PLUS ){
+ for( unsigned i=0; i<n[r].getNumChildren(); i++ ){
+ if (ArithMSum::getMonomial(n[r][i], c, nc)
+ && c.getConst<Rational>().isNegativeOne())
+ {
+ mon[ro].push_back( nc );
+ changed = true;
+ }else{
+ if( !n[r][i].isConst() || !n[r][i].getConst<Rational>().isZero() ){
+ mon[r].push_back( n[r][i] );
+ }
+ }
+ }
+ }else{
+ if (ArithMSum::getMonomial(n[r], c, nc)
+ && c.getConst<Rational>().isNegativeOne())
+ {
+ mon[ro].push_back( nc );
+ changed = true;
+ }else{
+ if( !n[r].isConst() || !n[r].getConst<Rational>().isZero() ){
+ mon[r].push_back( n[r] );
+ }
+ }
+ }
+ }
+ if( changed ){
+ Node nn[2];
+ for( unsigned r=0; r<2; r++ ){
+ nn[r] = mon[r].size()==0 ? NodeManager::currentNM()->mkConst( Rational(0) ) : ( mon[r].size()==1 ? mon[r][0] : NodeManager::currentNM()->mkNode( PLUS, mon[r] ) );
+ }
+ return NodeManager::currentNM()->mkNode( n.getKind(), nn[0], nn[1] );
+ }
+ }
+ return n;
+}
+
+Node TermDbSygus::expandBuiltinTerm( Node t ){
+ if( t.getKind()==EQUAL ){
+ if( t[0].getType().isReal() ){
+ return NodeManager::currentNM()->mkNode( AND, NodeManager::currentNM()->mkNode( LEQ, t[0], t[1] ),
+ NodeManager::currentNM()->mkNode( LEQ, t[1], t[0] ) );
+ }else if( t[0].getType().isBoolean() ){
+ return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, t[0], t[1] ),
+ NodeManager::currentNM()->mkNode( AND, t[0].negate(), t[1].negate() ) );
+ }
+ }else if( t.getKind()==ITE && t.getType().isBoolean() ){
+ return NodeManager::currentNM()->mkNode( OR, NodeManager::currentNM()->mkNode( AND, t[0], t[1] ),
+ NodeManager::currentNM()->mkNode( AND, t[0].negate(), t[2] ) );
+ }
+ return Node::null();
+}
+
+
+Kind TermDbSygus::getComparisonKind( TypeNode tn ) {
+ if( tn.isInteger() || tn.isReal() ){
+ return LT;
+ }else if( tn.isBitVector() ){
+ return BITVECTOR_ULT;
+ }else{
+ return UNDEFINED_KIND;
+ }
+}
+
+Kind TermDbSygus::getPlusKind( TypeNode tn, bool is_neg ) {
+ if( tn.isInteger() || tn.isReal() ){
+ return is_neg ? MINUS : PLUS;
+ }else if( tn.isBitVector() ){
+ return is_neg ? BITVECTOR_SUB : BITVECTOR_PLUS;
+ }else{
+ return UNDEFINED_KIND;
+ }
+}
+
+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]
+ == d_quantEngine->getTermUtil()->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;
+ while((pos = str.find(oldStr, pos)) != std::string::npos){
+ str.replace(pos, oldStr.length(), newStr);
+ pos += newStr.length();
+ }
+}
+
+Kind TermDbSygus::getOperatorKind( Node op ) {
+ Assert( op.getKind()!=BUILTIN );
+ if (op.getKind() == LAMBDA)
+ {
+ // we use APPLY_UF instead of APPLY, since the rewriter for APPLY_UF
+ // does beta-reduction but does not for APPLY
+ return APPLY_UF;
+ }else{
+ TypeNode tn = op.getType();
+ if( tn.isConstructor() ){
+ return APPLY_CONSTRUCTOR;
+ }
+ else if (tn.isSelector())
+ {
+ return APPLY_SELECTOR;
+ }
+ else if (tn.isTester())
+ {
+ return APPLY_TESTER;
+ }
+ else if (tn.isFunction())
+ {
+ return APPLY_UF;
+ }
+ return NodeManager::operatorToKind(op);
+ }
+}
+
+Node TermDbSygus::getAnchor( Node n ) {
+ if( n.getKind()==APPLY_SELECTOR_TOTAL ){
+ return getAnchor( n[0] );
+ }else{
+ return 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() ){
+ TypeNode tn = n[0].getType();
+ if( tn.isDatatype() ){
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ if( dt.isSygus() ){
+ Node f = n.getOperator();
+ if( n[0].getKind()!=APPLY_CONSTRUCTOR ){
+ if (d_eval_processed.find(n) == d_eval_processed.end())
+ {
+ Trace("sygus-eager")
+ << "TermDbSygus::eager: Register eval term : " << n
+ << std::endl;
+ d_eval_processed.insert(n);
+ d_evals[n[0]].push_back(n);
+ TypeNode tn = n[0].getType();
+ Assert(tn.isDatatype());
+ const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
+ 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++)
+ {
+ 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;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void TermDbSygus::registerModelValue( Node a, Node v, std::vector< Node >& terms, std::vector< Node >& vals, std::vector< Node >& exps ) {
+ std::map< Node, std::map< Node, bool > >::iterator its = d_subterms.find( a );
+ if( its!=d_subterms.end() ){
+ Trace("sygus-eager") << "registerModelValue : " << a << ", has " << its->second.size() << " registered subterms." << std::endl;
+ for( std::map< Node, bool >::iterator itss = its->second.begin(); itss != its->second.end(); ++itss ){
+ Node n = itss->first;
+ Trace("sygus-eager-debug") << "...process : " << n << std::endl;
+ std::map< Node, std::vector< std::vector< Node > > >::iterator it = d_eval_args.find( n );
+ if( it!=d_eval_args.end() && !it->second.empty() ){
+ TNode at = a;
+ TNode vt = v;
+ Node vn = n.substitute( at, vt );
+ vn = Rewriter::rewrite( vn );
+ unsigned start = d_node_mv_args_proc[n][vn];
+ // get explanation in terms of testers
+ std::vector< Node > antec_exp;
+ d_syexp->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();
+ Assert( dt.isSygus() );
+ Trace("sygus-eager") << "TermDbSygus::eager: Register model value : " << vn << " for " << n << std::endl;
+ Trace("sygus-eager") << "...it has " << it->second.size() << " evaluations, already processed " << start << "." << std::endl;
+ Node bTerm = sygusToBuiltin( vn, tn );
+ Trace("sygus-eager") << "Built-in term : " << bTerm << std::endl;
+ std::vector< Node > vars;
+ Node var_list = Node::fromExpr( dt.getSygusVarList() );
+ 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++ ){
+ 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() );
+ Node conj =
+ NodeManager::currentNM()->mkNode(kind::APPLY_UF, eval_children);
+ eval_children[1] = vn;
+ Node eval_fun = NodeManager::currentNM()->mkNode( kind::APPLY_UF, eval_children );
+ res = evaluateWithUnfolding(eval_fun);
+ esit.init(conj, n, res);
+ eval_children.resize( 2 );
+ eval_children[1] = n;
+
+ //evaluate with minimal explanation
+ std::vector< Node > mexp;
+ d_syexp->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( 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();
+ }
+ }
+ }
+}
+
+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] );
+ if( itv!=vtm.end() ){
+ ev = itv->second;
+ }else{
+ Assert( false );
+ }
+ Assert( en[0].getType()==ev.getType() );
+ Assert( ev.isConst() );
+ }
+ Assert( ev.getKind()==kind::APPLY_CONSTRUCTOR );
+ std::vector< Node > args;
+ for( unsigned i=1; i<en.getNumChildren(); i++ ){
+ args.push_back( en[i] );
+ }
+ const Datatype& dt = ((DatatypeType)(ev.getType()).toType()).getDatatype();
+ unsigned i = Datatype::indexOf( ev.getOperator().toExpr() );
+ if( track_exp ){
+ //explanation
+ Node ee = NodeManager::currentNM()->mkNode( kind::APPLY_TESTER, Node::fromExpr( dt[i].getTester() ), en[0] );
+ if( std::find( exp.begin(), exp.end(), ee )==exp.end() ){
+ 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;
+ 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
+ vtm[s] = ev[j];
+ }
+ cc.insert( cc.end(), args.begin(), args.end() );
+ pre[j] = NodeManager::currentNM()->mkNode( kind::APPLY_UF, cc );
+ }
+ std::map< TypeNode, int > var_count;
+ Node ret = mkGeneric( dt, i, var_count, pre );
+ // if it is a variable, apply the substitution
+ if( ret.getKind()==kind::BOUND_VARIABLE ){
+ Assert( ret.hasAttribute(SygusVarNumAttribute()) );
+ int i = ret.getAttribute(SygusVarNumAttribute());
+ Assert( Node::fromExpr( dt.getSygusVarList() )[i]==ret );
+ ret = args[i];
+ }
+ else
+ {
+ ret = Rewriter::rewrite(ret);
+ }
+ return ret;
+ }else{
+ Assert( en.isConst() );
+ }
+ 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().isComparableTo(
+ 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().isComparableTo(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::evaluateWithUnfolding(
+ Node n, std::unordered_map<Node, Node, NodeHashFunction>& visited)
+{
+ std::unordered_map<Node, Node, NodeHashFunction>::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 );
+ }
+ ret = getExtRewriter()->extendedRewrite(ret);
+ }
+ visited[n] = ret;
+ return ret;
+ }else{
+ return it->second;
+ }
+}
+
+Node TermDbSygus::evaluateWithUnfolding( Node n ) {
+ std::unordered_map<Node, Node, NodeHashFunction> visited;
+ return evaluateWithUnfolding( n, visited );
+}
+
+}/* CVC4::theory::quantifiers namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
diff --git a/src/theory/quantifiers/sygus/term_database_sygus.h b/src/theory/quantifiers/sygus/term_database_sygus.h
new file mode 100644
index 000000000..e796a3adc
--- /dev/null
+++ b/src/theory/quantifiers/sygus/term_database_sygus.h
@@ -0,0 +1,286 @@
+/********************* */
+/*! \file term_database_sygus.h
+ ** \verbatim
+ ** Top contributors (to current version):
+ ** Andrew Reynolds
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2017 by the authors listed in the file AUTHORS
+ ** in the top-level source directory) and their institutional affiliations.
+ ** All rights reserved. See the file COPYING in the top-level source
+ ** directory for licensing information.\endverbatim
+ **
+ ** \brief term database sygus class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_SYGUS_H
+#define __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_SYGUS_H
+
+#include <unordered_set>
+
+#include "theory/quantifiers/extended_rewrite.h"
+#include "theory/quantifiers/sygus/sygus_explain.h"
+#include "theory/quantifiers/term_database.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class CegConjecture;
+
+// TODO :issue #1235 split and document this class
+class TermDbSygus {
+ public:
+ TermDbSygus(context::Context* c, QuantifiersEngine* qe);
+ ~TermDbSygus() {}
+ /** Reset this utility */
+ bool reset(Theory::Effort e);
+ /** Identify this utility */
+ std::string identify() const { return "TermDbSygus"; }
+ /** register the sygus type */
+ void registerSygusType(TypeNode tn);
+ /** register a variable e that we will do enumerative search on
+ * conj is the conjecture that the enumeration of e is for.
+ * f is the synth-fun that the enumeration of e is for.
+ * mkActiveGuard is whether we want to make an active guard for e
+ * (see d_enum_to_active_guard).
+ *
+ * Notice that enumerator e may not be equivalent
+ * to f in synthesis-through-unification approaches
+ * (e.g. decision tree construction for PBE synthesis).
+ */
+ void registerEnumerator(Node e,
+ Node f,
+ CegConjecture* conj,
+ bool mkActiveGuard = false);
+ /** is e an enumerator? */
+ bool isEnumerator(Node e) const;
+ /** return the conjecture e is associated with */
+ CegConjecture* getConjectureForEnumerator(Node e);
+ /** return the function-to-synthesize e is associated with */
+ Node getSynthFunForEnumerator(Node e);
+ /** get active guard for e */
+ Node getActiveGuardForEnumerator(Node e);
+ /** get all registered enumerators */
+ void getEnumerators(std::vector<Node>& mts);
+ /** get the explanation utility */
+ SygusExplain* getExplain() { return d_syexp.get(); }
+ /** get the extended rewrite utility */
+ ExtendedRewriter* getExtRewriter() { return d_ext_rw.get(); }
+ //-----------------------------conversion from sygus to builtin
+ /** get free variable
+ *
+ * This class caches a list of free variables for each type, which are
+ * used, for instance, for constructing canonical forms of terms with free
+ * variables. This function returns the i^th free variable for type tn.
+ * If useSygusType is true, then this function returns a variable of the
+ * analog type for sygus type tn (see d_fv for details).
+ */
+ TNode getFreeVar(TypeNode tn, int i, bool useSygusType = false);
+ /** get free variable and increment
+ *
+ * This function returns the next free variable for type tn, and increments
+ * the counter in var_count for that type.
+ */
+ TNode getFreeVarInc(TypeNode tn,
+ std::map<TypeNode, int>& var_count,
+ bool useSygusType = false);
+ /** returns true if n is a cached free variable (in d_fv). */
+ bool isFreeVar(Node n) { return d_fv_stype.find(n) != d_fv_stype.end(); }
+ /** returns the index of n in the free variable cache (d_fv). */
+ int getVarNum(Node n) { return d_fv_num[n]; }
+ /** returns true if n has a cached free variable (in d_fv). */
+ bool hasFreeVar(Node n);
+ /** make generic
+ *
+ * This function returns a builtin term f( t1, ..., tn ) where f is the
+ * builtin op of the sygus datatype constructor specified by arguments
+ * dt and c. The mapping pre maps child indices to the term for that index
+ * in the term we are constructing. That is, for each i = 1,...,n:
+ * If i is in the domain of pre, then ti = pre[i].
+ * If i is not in the domain of pre, then ti = d_fv[1][ var_count[Ti ] ],
+ * and var_count[Ti] is incremented.
+ */
+ Node mkGeneric(const Datatype& dt,
+ unsigned c,
+ std::map<TypeNode, int>& var_count,
+ std::map<int, Node>& pre);
+ /** same as above, but with empty var_count */
+ Node mkGeneric(const Datatype& dt, int c, std::map<int, Node>& pre);
+ /** sygus to builtin
+ *
+ * Given a sygus datatype term n of type tn, this function returns its analog,
+ * that is, the term that n encodes.
+ */
+ Node sygusToBuiltin(Node n, TypeNode tn);
+ /** same as above, but without tn */
+ Node sygusToBuiltin(Node n) { return sygusToBuiltin(n, n.getType()); }
+ //-----------------------------end conversion from sygus to builtin
+
+ private:
+ /** reference to the quantifiers engine */
+ QuantifiersEngine* d_quantEngine;
+ /** sygus explanation */
+ std::unique_ptr<SygusExplain> d_syexp;
+ /** sygus explanation */
+ std::unique_ptr<ExtendedRewriter> d_ext_rw;
+ /** mapping from enumerator terms to the conjecture they are associated with
+ */
+ std::map<Node, CegConjecture*> d_enum_to_conjecture;
+ /** mapping from enumerator terms to the function-to-synthesize they are
+ * associated with
+ */
+ std::map<Node, Node> d_enum_to_synth_fun;
+ /** mapping from enumerator terms to the guard they are associated with
+ * The guard G for an enumerator e has the semantics
+ * if G is true, then there are more values of e to enumerate".
+ */
+ std::map<Node, Node> d_enum_to_active_guard;
+
+ //-----------------------------conversion from sygus to builtin
+ /** cache for sygusToBuiltin */
+ std::map<TypeNode, std::map<Node, Node> > d_sygus_to_builtin;
+ /** a cache of fresh variables for each type
+ *
+ * We store two versions of this list:
+ * index 0: mapping from builtin types to fresh variables of that type,
+ * index 1: mapping from sygus types to fresh varaibles of the type they
+ * encode.
+ */
+ std::map<TypeNode, std::vector<Node> > d_fv[2];
+ /** Maps free variables to the domain type they are associated with in d_fv */
+ std::map<Node, TypeNode> d_fv_stype;
+ /** Maps free variables to their index in d_fv. */
+ std::map<Node, int> d_fv_num;
+ /** recursive helper for hasFreeVar, visited stores nodes we have visited. */
+ bool hasFreeVar(Node n, std::map<Node, bool>& visited);
+ //-----------------------------end conversion from sygus to builtin
+
+ // TODO :issue #1235 : below here needs refactor
+
+ public:
+ Node d_true;
+ Node d_false;
+
+private:
+ void computeMinTypeDepthInternal( TypeNode root_tn, TypeNode tn, unsigned type_depth );
+ bool involvesDivByZero( Node n, std::map< Node, bool >& visited );
+
+ private:
+ // 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;
+ std::map<TypeNode, std::map<Node, int> > d_consts;
+ std::map<TypeNode, std::map<Node, int> > d_ops;
+ std::map<TypeNode, std::map<int, Node> > d_arg_ops;
+ std::map<TypeNode, std::map<Node, Node> > d_semantic_skolem;
+ // 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;
+ /** a cache for getSelectorWeight */
+ std::map<TypeNode, std::map<Node, unsigned> > d_sel_weight;
+
+ 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 );
+ /** get the weight of the selector, where tn is the domain of sel */
+ unsigned getSelectorWeight(TypeNode tn, Node sel);
+
+ public:
+ TypeNode sygusToBuiltinType( TypeNode tn );
+ 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 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 );
+ /** get arg type */
+ TypeNode getArgType(const DatatypeConstructor& c, unsigned i);
+ /** get first occurrence */
+ int getFirstArgOccurrence( const DatatypeConstructor& c, TypeNode tn );
+ /** is type match */
+ bool isTypeMatch( const DatatypeConstructor& c1, const DatatypeConstructor& c2 );
+
+ TypeNode getSygusTypeForVar( Node v );
+ Node sygusSubstituted( TypeNode tn, Node n, std::vector< Node >& args );
+ Node getSygusNormalized( Node n, std::map< TypeNode, int >& var_count, std::map< Node, Node >& subs );
+ Node getNormalized(TypeNode t, Node prog);
+ unsigned getSygusTermSize( Node n );
+ /** given a term, construct an equivalent smaller one that respects syntax */
+ Node minimizeBuiltinTerm( Node n );
+ /** given a term, expand it into more basic components */
+ Node expandBuiltinTerm( Node n );
+ /** get comparison kind */
+ Kind getComparisonKind( TypeNode tn );
+ Kind getPlusKind( TypeNode tn, bool is_neg = false );
+ // 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 );
+
+ /** 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
+ // TODO (as part of #1235) move some of these functions to sygus_explain.h
+ private:
+ /** the set of evaluation terms we have already processed */
+ std::unordered_set<Node, NodeHashFunction> d_eval_processed;
+ 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;
+
+public:
+ void registerEvalTerm( Node n );
+ void registerModelValue( Node n, Node v, std::vector< Node >& exps, std::vector< Node >& terms, std::vector< Node >& vals );
+ Node unfold( Node en, std::map< Node, Node >& vtm, std::vector< Node >& exp, bool track_exp = true );
+ Node unfold( Node en ){
+ std::map< Node, Node > vtm;
+ std::vector< Node > exp;
+ return unfold( en, vtm, exp, false );
+ }
+ Node getEagerUnfold( Node n, std::map< Node, Node >& visited );
+
+ // builtin evaluation, returns rewrite( bn [ args / vars(tn) ] )
+ Node evaluateBuiltin( TypeNode tn, Node bn, std::vector< Node >& args );
+ // evaluate with unfolding
+ Node evaluateWithUnfolding(
+ Node n, std::unordered_map<Node, Node, NodeHashFunction>& visited);
+ Node evaluateWithUnfolding( Node n );
+};
+
+}/* CVC4::theory::quantifiers namespace */
+}/* CVC4::theory namespace */
+}/* CVC4 namespace */
+
+#endif /* __CVC4__THEORY__QUANTIFIERS__TERM_DATABASE_H */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback